4.4把自定义注解的基本信息注入到方法上
上面我们已经使用到了注解,但是目前为止注解上的信息和方法上的信息是没有任何关联的。
我们使用Servlet注解的时候,仅仅调用注解,那么注解的就生效了。这是Web容器把内部实现了。我们自己写的自定义注解是需要我们自己来处理的。
那现在问题来了,我们怎么把注解上的信息注入到方法上呢???我们利用的是反射技术
步骤可分为三部:
- 反射出该类的方法
- 通过方法得到注解上具体的信息
- 将注解上的信息注入到方法上
//反射出该类的方法 Class aClass = Demo2.class; Method method = aClass.getMethod("add", String.class, int.class); //通过该方法得到注解上的具体信息 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); String username = annotation.username(); int age = annotation.age(); //将注解上的信息注入到方法上 Object o = aClass.newInstance(); method.invoke(o, username, age);
当我们执行的时候,我们发现会出现异常…
这里写图片描述
此时,我们需要在自定义注解上加入这样一句代码(下面就会讲到,为什么要加入这句代码)
@Retention(RetentionPolicy.RUNTIME)
再次执行的时候,我们就会发现,可以通过注解来把信息注入到方法中了。
五、JDK的元Annotation
前面我们已经介绍了java.lang包下的几个基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有几个常用的元Annotation。
在annotation包下的好几个元Annotation都是用于修饰其他的Annotation定义。
5.1@Retention
上面在将注解信息注入到方法中的时候,我们最后加上了@Retention的注解….不然就会报错了..那它是干什么用的呢?
@Retention只能用于修饰其他的Annotation,用于指定被修饰的Annotation被保留多长时间。
@Retention 包含了一个RetentionPolicy类型的value变量,所以在使用它的时候,必须要为value成员变量赋值
value变量的值只有三个:
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
java文件有三个时期:编译,class,运行。@Retention默认是class
前面我们是使用反射来得到注解上的信息的,因为@Retention默认是class,而反射是在运行时期来获取信息的。因此就获取不到Annotation的信息了。于是,就得在自定义注解上修改它的RetentionPolicy值
5.2@Target
@Target也是只能用于修饰另外的Annotation,它用于指定被修饰的Annotation用于修饰哪些程序单元
@Target是只有一个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 }
如果@Target指定的是ElementType.ANNOTATION_TYPE,那么该被修饰的Annotation只能修饰Annotaion
5.3@Documented
@Documented用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档。
该元Annotation用得挺少的….
5.4@Inherited
@Inherited也是用来修饰其他的Annotation的,被修饰过的Annotation将具有继承性。。。
例子:
- @xxx是我自定义的注解,我现在使用@xxx注解在Base类上使用….
- 使用@Inherited修饰@xxx注解
- 当有类继承了Base类的时候,该实现类自动拥有@xxx注解
六、注入对象到方法或成员变量上
6.1把对象注入到方法上
前面我们已经可以使用注解将基本的信息注入到方法上了,现在我们要使用的是将对象注入到方法上…..
上边已经说过了,注解上只能定义String、枚举类、Double之类的成员变量,那怎么把对象注入到方法上呢?
6.1.2模拟场景:
- Person类,定义username和age属性,拥有uername和age的getter和setter方法
public class Person { private String username; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
- PersonDao类,PersonDao类定义了Person对象,拥有person的setter和getter方法
public class PersonDao { private Person person; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
- 现在我要做的就是:使用注解将Person对象注入到setPerson()方法中,从而设置了PersonDao类的person属性
public class PersonDao { private Person person; public Person getPerson() { return person; } //将username为zhongfucheng,age为20的Person对象注入到setPerson方法中 @InjectPerson(username = "zhongfucheng",age = 20) public void setPerson(Person person) { this.person = person; } }
步骤:
①: 自定义一个注解,属性是和JavaBean类一致的
//注入工具是通过反射来得到注解的信息的,于是保留域必须使用RunTime @Retention(RetentionPolicy.RUNTIME) public @interface InjectPerson { String username(); int age(); }
②:编写注入工具
//1.使用内省【后边需要得到属性的写方法】,得到想要注入的属性 PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class); //2.得到要想注入属性的具体对象 Person person = (Person) descriptor.getPropertyType().newInstance(); //3.得到该属性的写方法【setPerson()】 Method method = descriptor.getWriteMethod(); //4.得到写方法的注解 Annotation annotation = method.getAnnotation(InjectPerson.class); //5.得到注解上的信息【注解的成员变量就是用方法来定义的】 Method[] methods = annotation.getClass().getMethods(); //6.将注解上的信息填充到person对象上 for (Method m : methods) { //得到注解上属性的名字【age或name】 String name = m.getName(); //看看Person对象有没有与之对应的方法【setAge(),setName()】 try { //6.1这里假设:有与之对应的写方法,得到写方法 PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class); Method method1 = descriptor1.getWriteMethod();//setAge(), setName() //得到注解中的值 Object o = m.invoke(annotation, null); //调用Person对象的setter方法,将注解上的值设置进去 method1.invoke(person, o); } catch (Exception e) { //6.2 Person对象没有与之对应的方法,会跳到catch来。我们要让它继续遍历注解就好了 continue; } } //当程序遍历完之后,person对象已经填充完数据了 //7.将person对象赋给PersonDao【通过写方法】 PersonDao personDao = new PersonDao(); method.invoke(personDao, person); System.out.println(personDao.getPerson().getUsername()); System.out.println(personDao.getPerson().getAge());
③:总结一下步骤
其实我们是这样把对象注入到方法中的:
- 得到想要类中注入的属性
- 得到该属性的对象
- 得到属性对应的写方法
- 通过写方法得到注解
- 获取注解详细的信息
- 将注解的信息注入到对象上
- 调用属性写方法,将已填充数据的对象注入到方法中
6.2把对象注入到成员变量
上面已经说了如何将对象注入到方法上了,那么注入到成员变量上也是非常简单的。
步骤:
①:在成员变量上使用注解
public class PersonDao { @InjectPerson(username = "zhongfucheng",age = 20) private Person person; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
②:编写注入工具
//1.得到想要注入的属性 Field field = PersonDao.class.getDeclaredField("person"); //2.得到属性的具体对象 Person person = (Person) field.getType().newInstance(); //3.得到属性上的注解 Annotation annotation = field.getAnnotation(InjectPerson.class); //4.得到注解的属性【注解上的属性使用方法来表示的】 Method[] methods = annotation.getClass().getMethods(); //5.将注入的属性填充到person对象上 for (Method method : methods) { //5.1得到注解属性的名字 String name = method.getName(); //查看一下Person对象上有没有与之对应的写方法 try { //如果有 PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class); //得到Person对象上的写方法 Method method1 = descriptor.getWriteMethod(); //得到注解上的值 Object o = method.invoke(annotation, null); //填充person对象 method1.invoke(person, o); } catch (IntrospectionException e) { //如果没有想对应的属性,继续循环 continue; } } //循环完之后,person就已经填充好数据了 //6.把person对象设置到PersonDao中 PersonDao personDao = new PersonDao(); field.setAccessible(true); field.set(personDao, person); System.out.println(personDao.getPerson().getUsername());
七、总结
①:注入对象的步骤:得到想要注入的对象属性,通过属性得到注解的信息,通过属性的写方法将注解的信息注入到对象上,最后将对象赋给类。
②:注解其实就是两个作用:
- 让编译器检查代码
- 将数据注入到方法、成员变量、类上
③:在JDK中注解分为了
- 基本Annotation
- 在lang包下,用于常用于标记该方法,抑制编译器警告等
- 元Annotaion
- 在annotaion包下,常用于修饰其他的Annotation定义