注解提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联,注解可以让一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。
注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的 invoke 方法。该方法会从 memberValues 这个 Map 中索引出对应的值。而 memberValues 的来源是 Java 常量池。
java.lang.annotation 提供了四种元注解(专门注解其他的注解)
使用 @Retention 注解时必须指定一个 RetentionPolicy 枚举类中的 value 变量
其中 @Retention(RUNTIME) 因为可以反射,所以最常用,默认是CLASS
public enum RetentionPolicy {
SOURCE,//在编译结束之后丢弃,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
CLASS,//在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
RUNTIME//始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
}
使用 @Target 注解时,ElementType 枚举类提供以下 value 变量
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE
}
没有任何成员变量的注解称作为标记注解,@Override 就是一个标记注解
//类似定义接口
public @interface MyAnnotation{
}
自定义带有成员变量的注解叫做元数据注解
@Target({
ElementType.METHOD}) //下面的例子需要的目标类型为方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
//注解成员只能用public或默认(default)这两个访问权修饰
//注解成员只能用基本数据类型和String、Enum、Class、annotations等数据类型
String name();
int age();
}
根据前面创建的 @MyAnnotation 注解,让一个方法的变量对应注解中的变量,此时注解上的信息和方法上的信息还是没有关联的
public class AddPerson{
@MyAnnotation(name = "张三", age = 28)
public void add(String name, int age){
System.out.println(name);
System.out.println(age);
}
}
利用反射把注解上的信息注入到方法上
public class Demo{
@Test
public void test() throws Exception{
//反射出类的方法
Class clazz = AddPerson.class;
Method method = clazz.getMethod("add", String.class, int.class);
//通过反射得到的方法获取注解上的信息
MyAnnotation ma = method.getAnnotation(MyAnnotation.class);
String name = ma.name();
int age = ma.age();
//得到的信息注入到方法上
Object o = clazz.newInstance();
method.invoke(o, name, age);
}
}
Person 类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
PersonDao 类
public class PersonDao {
@InjectPerson(name = "李四", age = 30)
private Person person;
public Person getPerson() {
return person;
}
@InjectPerson(name = "张三", age = 28)
public void setPerson(Person person) {
this.person = person;
}
}
自定义注解 @InjectPerson
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface InjectPerson {
String name();
int age();
}
注入工具编写,test1() 为注入到方法上,test2() 为注入到属性上
public class InjectionUtils {
@Test
public void test1() throws Exception{
//得到要注入的属性
PropertyDescriptor pd = new PropertyDescriptor("Person", PersonDao.class);
//得到要注入的属性的类型并创建实例对象
Person person = (Person)pd.getPropertyType().newInstance();
//得到属性的写方法
Method writeMethod = pd.getWriteMethod();
//反射出方法上声明的注解
InjectPerson injectPerson = writeMethod.getAnnotation(InjectPerson.class);
//得到注解中声明的信息,遍历
Method[] methods = injectPerson.getClass().getMethods();
for(Method m : methods){
try {
String name = m.getName();
PropertyDescriptor pd1 = new PropertyDescriptor(name, Person.class);
Method writeMethod1 = pd1.getWriteMethod();
Object o = m.invoke(injectPerson, null);
writeMethod1.invoke(person, o);
}catch(Exception e){
continue;
}
}
//把填充了数据的person通过写方法invoke到personDao对象中
PersonDao personDao = new PersonDao();
writeMethod.invoke(personDao, person);
System.out.println(personDao.getPerson().getName());
System.out.println(personDao.getPerson().getAge());
}
@Test
public void test2() throws Exception{
Field field = PersonDao.class.getDeclaredField("person");
Person person = (Person)field.getType().newInstance();
InjectPerson injectPerson = field.getAnnotation(InjectPerson.class);
Method[] methods = injectPerson.getClass().getMethods();
for(Method m : methods){
String name = m.getName();
try {
PropertyDescriptor pd1 = new PropertyDescriptor(name, Person.class);
Method writeMethod1 = pd1.getWriteMethod();
Object o = m.invoke(injectPerson, null);
writeMethod1.invoke(person, o);
}catch(Exception e){
continue;
}
}
PersonDao personDao = new PersonDao();
field.setAccessible(true);
field.set(personDao, person);
System.out.println(personDao.getPerson().getName());
System.out.println(personDao.getPerson().getAge());
}
}