今天要讲的是注解,对于本章节,最好是有Servlet基础的人查阅~由于单纯是Java基础的话,可能用不上注解这个东西。但若是开发过Servlet,就对@WebServlet
不会陌生。java
如今的开发都推崇使用注解来进行开发,这样就能够免去写XML配置了,十分方便的一项技术~web
学习注解能够更好地理解注解是怎么工做的,看见注解了就能够想到它的运行原理了~。数组
若是有错的地方请你们多多包涵并欢迎在评论区指正~安全
注解:Annotation....微信
注解其实就是代码中的特殊标记,这些标记能够在编译、类加载、运行时被读取,并执行相对应的处理。函数
传统的方式,咱们是经过配置文件(xml文件)来告诉类是如何运行的。工具
有了注解技术之后,咱们就能够经过注解告诉类如何运行学习
例如:咱们之前编写Servlet的时候,须要在web.xml文件配置具体的信息this
咱们使用了注解之后,能够直接在Servlet源代码上,增长注解...Servlet就被配置到Tomcat上了。也就是说,注解能够给类、方法上注入信息。spa
明显地能够看出,这样是很是直观的,而且Servlet规范是推崇这种配置方式的。
在java.lang包下存在着5个基本的Annotation,其中有3个Annotation咱们是很是常见的了。
重写注解
若是咱们使用IDE重写父类的方法,咱们就能够看见它了。那它有什么用呢??
@Overried是告诉编译器要检查该方法是实现父类的...能够帮咱们避免一些低级的错误...
好比,咱们在实现equals()方法的时候,把euqals()打错了,那么编译器就会发现该方法并非实现父类的,与注解@Overried冲突,因而就会给予错误。
过期注解
该注解也很是常见,Java在设计的时候,可能以为某些方法设计得很差,为了兼容之前的程序,是不能直接把它抛弃的,因而就设置它为过期。
Date对象中的toLocalString()就被设置成过期了
@Deprecated public String toLocaleString() { DateFormat formatter = DateFormat.getDateTimeInstance(); return formatter.format(this); }
当咱们在程序中调用它的时候,在IDE上会出现一条横杠,说明该方法是过期的。
抑制编译器警告注解
该注解在咱们写程序的时候并非很常见,咱们能够用它来让编译器不给予咱们警告
当咱们在使用集合的时候,若是没有指定泛型,那么会提示安全检查的警告
若是咱们在类上添加了@SuppressWarnings这个注解,那么编译器就不会给予咱们警告了
Java 7“堆污染”警告
什么是堆污染呢??当把一个不是泛型的集合赋值给一个带泛型的集合的时候,这种状况就很容易发生堆污染....
这个注解也是用来抑制编译器警告的注解...用的地方并很少,我也不详细说明了......有用到的时候再回来填坑吧。
@FunctionalInterface用来指定该接口是函数式接口
用该注解显示指定该接口是一个函数式接口。
上面讲解的是java.lang包下的5个注解,咱们是能够本身来写注解,给方法或类注入信息。
没有任何成员变量的注解称做为标记注解,@Overried就是一个标记注解
//有点像定义一个接口同样,只不过它多了一个@ public @interface MyAnnotation { }
咱们自定义的注解是能够带成员变量的,定义带成员变量的注解叫作元数据Annotation
在注解中定义成员变量,语法相似于声明方法同样....
public @interface MyAnnotation { //定义了两个成员变量 String username(); int age(); }
注意:在注解上定义的成员变量只能是String、数组、Class、枚举类、注解
有的人可能会奇怪,为何注解上还要定义注解成员变量??听起来就很复杂了....
上边已经说了,注解的做用就是给类、方法注入信息。那么咱们常用XML文件,告诉程序怎么运行。XML常常会有嵌套的状况
<书> <做者>zhongfucheng</做者> <价钱>22222</价钱> </书>
那么,当咱们在使用注解的时候,也可能须要有嵌套的时候,因此就容许了注解上能够定义成员变量为注解。
上面咱们已经定义了一个注解了,下面咱们来使用它吧
下面我有一个add的方法,须要username和age参数,咱们经过注解来让该方法拥有这两个变量!
//注解拥有什么属性,在修饰的时候就要给出相对应的值 @MyAnnotation(username = "zhongfucheng", age = 20) public void add(String username, int age) { }
固然啦,咱们能够在注解声明属性的时候,给出默认值。那么在修饰的时候,就能够不用具体指定了。
public @interface MyAnnotation { //定义了两个成员变量 String username() default "zicheng"; int age() default 23; }
@MyAnnotation() public void add(String username, int age) { }
还有一种特殊的状况,若是注解上只有一个属性,而且属性的名称为value,那么在使用的时候,咱们能够不写value,直接赋值给它就行
public @interface MyAnnotation2 { String value(); }
@MyAnnotation2("zhongfucheng") public void find(String id) { }
上面咱们已经使用到了注解,可是目前为止注解上的信息和方法上的信息是没有任何关联的。
咱们使用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)
再次执行的时候,咱们就会发现,能够经过注解来把信息注入到方法中了。
前面咱们已经介绍了java.lang包下的几个基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有几个经常使用的元Annotation。
在annotation包下的好几个元Annotation都是用于修饰其余的Annotation定义。
上面在将注解信息注入到方法中的时候,咱们最后加上了@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值
@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
@Documented用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档。
该元Annotation用得挺少的....
@Inherited也是用来修饰其余的Annotation的,被修饰过的Annotation将具备继承性。。。
例子:
前面咱们已经可使用注解将基本的信息注入到方法上了,如今咱们要使用的是将对象注入到方法上.....
上边已经说过了,注解上只能定义String、枚举类、Double之类的成员变量,那怎么把对象注入到方法上呢?
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; } }
public class PersonDao { private Person person; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = 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());
③:总结一下步骤
其实咱们是这样把对象注入到方法中的:
上面已经说了如何将对象注入到方法上了,那么注入到成员变量上也是很是简单的。
步骤:
①:在成员变量上使用注解
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
元Annotaion
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够 关注微信公众号:Java3y