什么是注解?

注解:Annotation….

注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理

为什么我们需要用到注解?

传统的方式,我们是通过配置文件(xml文件)来告诉类是如何运行的

有了注解技术以后,我们就可以通过注解告诉类如何运行

例如:我们以前编写Servlet的时候,需要在web.xml文件配置具体的信息

我们使用了注解以后,可以直接在Servlet源代码上,增加注解…Servlet就被配置到Tomcat上了。也就是说,注解可以给类、方法上注入信息。

明显地可以看出,这样是非常直观的,并且Servlet规范是推崇这种配置方式的

基本Annotation

在java.lang包下存在着5个基本的Annotation,其中有3个Annotation我们是非常常见的了。

@Overried

重写注解

如果我们使用IDE重写父类的方法,我们就可以看见它了。那它有什么用呢??

@Overried是告诉编译器要检查该方法是实现父类的…可以帮我们避免一些低级的错误…

比如,我们在实现equals()方法的时候,把euqals()打错了,那么编译器就会发现该方法并不是实现父类的,与注解@Overried冲突,于是就会给予错误


@Deprecated

过时注解

该注解也非常常见,Java在设计的时候,可能觉得某些方法设计得不好,为了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时

Date对象中的toLocalString()就被设置成过时了

  1. @Deprecated
  2. public String toLocaleString() {
  3. DateFormat formatter = DateFormat.getDateTimeInstance();
  4. return formatter.format(this);
  5. }

当我们在程序中调用它的时候,在IDE上会出现一条横杠,说明该方法是过时的。


@SuppressWarnings

抑制编译器警告注解

该注解在我们写程序的时候并不是很常见,我们可以用它来让编译器不给予我们警告

当我们在使用集合的时候,如果没有指定泛型,那么会提示安全检查的警告

如果我们在类上添加了@SuppressWarnings这个注解,那么编译器就不会给予我们警告了

@SafeVarargs

Java 7“堆污染”警告

什么是堆污染呢??当把一个不是泛型的集合赋值给一个带泛型的集合的时候,这种情况就很容易发生堆污染….

这个注解也是用来抑制编译器警告的注解…用的地方并不多,我也不详细说明了……有用到的时候再回来填坑吧。

@FunctionalInterface

@FunctionalInterface用来指定该接口是函数式接口

Java8的内容,等我回来填坑吧….


自定义注解

上面讲解的是java.lang包下的5个注解,我们是可以自己来写注解,给方法或类注入信息

标记Annotation

没有任何成员变量的注解称作为标记注解,@Overried就是一个标记注解


  1. //有点像定义一个接口一样,只不过它多了一个@
  2. public @interface MyAnnotation {
  3. }

元数据Annotation

我们自定义的注解是可以带成员变量的,定义带成员变量的注解叫做元数据Annotation

在注解中定义成员变量,语法类似于声明方法一样….


  1. public @interface MyAnnotation {
  2. //定义了两个成员变量
  3. String username();
  4. int age();
  5. }

注意:在注解上定义的成员变量只能是String、数组、Class、枚举类、注解

有的人可能会奇怪,为什么注解上还要定义注解成员变量??听起来就很复杂了….

上边已经说了,注解的作用就是给类、方法注入信息。那么我们经常使用XML文件,告诉程序怎么运行。XML经常会有嵌套的情况


  1. <书>
  2. <作者>zhongfucheng</作者>
  3. <价钱>22222</价钱>
  4. </书>

那么,当我们在使用注解的时候,也可能需要有嵌套的时候,所以就允许了注解上可以定义成员变量为注解。

使用自定义注解

上面我们已经定义了一个注解了,下面我们来使用它吧

常规使用

下面我有一个add的方法,需要username和age参数,我们通过注解来让该方法拥有这两个变量


  1. //注解拥有什么属性,在修饰的时候就要给出相对应的值
  2. @MyAnnotation(username = "zhongfucheng", age = 20)
  3. public void add(String username, int age) {
  4. }

默认值

当然啦,我们可以在注解声明属性的时候,给出默认值。那么在修饰的时候,就可以不用具体指定了。


  1. public @interface MyAnnotation {
  2. //定义了两个成员变量
  3. String username() default "zicheng";
  4. int age() default 23;
  5. }
  • 在修饰的时候就不需要给出具体的值了

  1. @MyAnnotation()
  2. public void add(String username, int age) {
  3. }

注解属性为value

还有一种特殊的情况,如果注解上只有一个属性,并且属性的名称为value,那么在使用的时候,我们可以不写value,直接赋值给它就行


  1. public @interface MyAnnotation2 {
  2. String value();
  3. }
  • 使用注解,可以不指定value,直接赋值

  1. @MyAnnotation2("zhongfucheng")
  2. public void find(String id) {
  3. }

把自定义注解的基本信息注入到方法上

上面我们已经使用到了注解,但是目前为止注解上的信息和方法上的信息是没有任何关联的

我们使用Servlet注解的时候,仅仅调用注解,那么注解的就生效了。这是Web容器把内部实现了。我们自己写的自定义注解是需要我们自己来处理的

那现在问题来了,我们怎么把注解上的信息注入到方法上呢???我们利用的是反射技术

步骤可分为三部:

  • 反射出该类的方法
  • 通过方法得到注解上具体的信息
  • 将注解上的信息注入到方法上

  1. //反射出该类的方法
  2. Class aClass = Demo2.class;
  3. Method method = aClass.getMethod("add", String.class, int.class);
  4. //通过该方法得到注解上的具体信息
  5. MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
  6. String username = annotation.username();
  7. int age = annotation.age();
  8. //将注解上的信息注入到方法上
  9. Object o = aClass.newInstance();
  10. method.invoke(o, username, age);

当我们执行的时候,我们发现会出现异常…

此时,我们需要在自定义注解上加入这样一句代码(下面就会讲到,为什么要加入这句代码)


  1. @Retention(RetentionPolicy.RUNTIME)

再次执行的时候,我们就会发现,可以通过注解来把信息注入到方法中了。


JDK的元Annotation

前面我们已经介绍了java.lang包下的几个基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有几个常用的元Annotation。

在annotation包下的好几个元Annotation都是用于修饰其他的Annotation定义


@Retention

上面在将注解信息注入到方法中的时候,我们最后加上了@Retention的注解….不然就会报错了..那它是干什么用的呢?

@Retention只能用于修饰其他的Annotation,用于指定被修饰的Annotation被保留多长时间。

@Retention**包含了一个RetentionPolicy类型的value变量,所以在使用它的时候,必须要为value成员变量赋值**

value变量的值只有三个:


  1. public enum RetentionPolicy {
  2. /**
  3. * Annotations are to be discarded by the compiler.
  4. */
  5. SOURCE,
  6. /**
  7. * Annotations are to be recorded in the class file by the compiler
  8. * but need not be retained by the VM at run time. This is the default
  9. * behavior.
  10. */
  11. CLASS,
  12. /**
  13. * Annotations are to be recorded in the class file by the compiler and
  14. * retained by the VM at run time, so they may be read reflectively.
  15. *
  16. * @see java.lang.reflect.AnnotatedElement
  17. */
  18. RUNTIME
  19. }

java文件有三个时期:编译,class,运行。@Retention默认是class

前面我们是使用反射来得到注解上的信息的,因为@Retention默认是class,而反射是在运行时期来获取信息的。因此就获取不到Annotation的信息了。于是,就得在自定义注解上修改它的RetentionPolicy值


@Target

@Target也是只能用于修饰另外的Annotation它用于指定被修饰的Annotation用于修饰哪些程序单元

@Target是只有一个value成员变量的,该成员变量的值是以下的:


  1. public enum ElementType {
  2. /** Class, interface (including annotation type), or enum declaration */
  3. TYPE,
  4. /** Field declaration (includes enum constants) */
  5. FIELD,
  6. /** Method declaration */
  7. METHOD,
  8. /** Parameter declaration */
  9. PARAMETER,
  10. /** Constructor declaration */
  11. CONSTRUCTOR,
  12. /** Local variable declaration */
  13. LOCAL_VARIABLE,
  14. /** Annotation type declaration */
  15. ANNOTATION_TYPE,
  16. /** Package declaration */
  17. PACKAGE
  18. }

如果@Target指定的是ElementType.ANNOTATION_TYPE,那么该被修饰的Annotation只能修饰Annotaion


@Documented

@Documented用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档

该元Annotation用得挺少的….


@Inherited

@Inherited也是用来修饰其他的Annotation的,被修饰过的Annotation将具有继承性。。。

例子:

  1. @xxx是我自定义的注解,我现在使用@xxx注解在Base类上使用….
  2. 使用@Inherited修饰@xxx注解
  3. 当有类继承了Base类的时候,该实现类自动拥有@xxx注解

把对象注入到方法上

前面我们已经可以使用注解将基本的信息注入到方法上了,现在我们要使用的是将对象注入到方法上…..

上边已经说过了,注解上只能定义String、枚举类、Double之类的成员变量,那怎么把对象注入到方法上呢?

模拟场景:

  • Person类,定义username和age属性,拥有uername和age的getter和setter方法

  1. public class Person {
  2. private String username;
  3. private int age;
  4. public String getUsername() {
  5. return username;
  6. }
  7. public void setUsername(String username) {
  8. this.username = username;
  9. }
  10. public int getAge() {
  11. return age;
  12. }
  13. public void setAge(int age) {
  14. this.age = age;
  15. }
  16. }
  • PersonDao类,PersonDao类定义了Person对象,拥有person的setter和getter方法

  1. public class PersonDao {
  2. private Person person;
  3. public Person getPerson() {
  4. return person;
  5. }
  6. public void setPerson(Person person) {
  7. this.person = person;
  8. }
  9. }
  • 现在我要做的就是:使用注解将Person对象注入到setPerson()方法中,从而设置了PersonDao类的person属性

  1. public class PersonDao {
  2. private Person person;
  3. public Person getPerson() {
  4. return person;
  5. }
  6. //将username为zhongfucheng,age为20的Person对象注入到setPerson方法中
  7. @InjectPerson(username = "zhongfucheng",age = 20)
  8. public void setPerson(Person person) {
  9. this.person = person;
  10. }
  11. }

步骤:

①: 自定义一个注解,属性是和JavaBean类一致的


  1. //注入工具是通过反射来得到注解的信息的,于是保留域必须使用RunTime
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface InjectPerson {
  4. String username();
  5. int age();
  6. }

②:编写注入工具


  1. //1.使用内省【后边需要得到属性的写方法】,得到想要注入的属性
  2. PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class);
  3. //2.得到要想注入属性的具体对象
  4. Person person = (Person) descriptor.getPropertyType().newInstance();
  5. //3.得到该属性的写方法【setPerson()】
  6. Method method = descriptor.getWriteMethod();
  7. //4.得到写方法的注解
  8. Annotation annotation = method.getAnnotation(InjectPerson.class);
  9. //5.得到注解上的信息【注解的成员变量就是用方法来定义的】
  10. Method[] methods = annotation.getClass().getMethods();
  11. //6.将注解上的信息填充到person对象上
  12. for (Method m : methods) {
  13. //得到注解上属性的名字【age或name】
  14. String name = m.getName();
  15. //看看Person对象有没有与之对应的方法【setAge(),setName()】
  16. try {
  17. //6.1这里假设:有与之对应的写方法,得到写方法
  18. PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class);
  19. Method method1 = descriptor1.getWriteMethod();//setAge(), setName()
  20. //得到注解中的值
  21. Object o = m.invoke(annotation, null);
  22. //调用Person对象的setter方法,将注解上的值设置进去
  23. method1.invoke(person, o);
  24. } catch (Exception e) {
  25. //6.2 Person对象没有与之对应的方法,会跳到catch来。我们要让它继续遍历注解就好了
  26. continue;
  27. }
  28. }
  29. //当程序遍历完之后,person对象已经填充完数据了
  30. //7.将person对象赋给PersonDao【通过写方法】
  31. PersonDao personDao = new PersonDao();
  32. method.invoke(personDao, person);
  33. System.out.println(personDao.getPerson().getUsername());
  34. System.out.println(personDao.getPerson().getAge());

③:总结一下步骤

其实我们是这样把对象注入到方法中的:

  • 得到想要类中注入的属性
  • 得到该属性的对象
  • 得到属性对应的写方法
  • 通过写方法得到注解
  • 获取注解详细的信息
  • 将注解的信息注入到对象上
  • 调用属性写方法,将已填充数据的对象注入到方法中

把对象注入到成员变量

上面已经说了如何将对象注入到方法上了,那么注入到成员变量上也是非常简单的。

步骤:

①:在成员变量上使用注解


  1. public class PersonDao {
  2. @InjectPerson(username = "zhongfucheng",age = 20) private Person person;
  3. public Person getPerson() {
  4. return person;
  5. }
  6. public void setPerson(Person person) {
  7. this.person = person;
  8. }
  9. }

②:编写注入工具


  1. //1.得到想要注入的属性
  2. Field field = PersonDao.class.getDeclaredField("person");
  3. //2.得到属性的具体对象
  4. Person person = (Person) field.getType().newInstance();
  5. //3.得到属性上的注解
  6. Annotation annotation = field.getAnnotation(InjectPerson.class);
  7. //4.得到注解的属性【注解上的属性使用方法来表示的】
  8. Method[] methods = annotation.getClass().getMethods();
  9. //5.将注入的属性填充到person对象上
  10. for (Method method : methods) {
  11. //5.1得到注解属性的名字
  12. String name = method.getName();
  13. //查看一下Person对象上有没有与之对应的写方法
  14. try {
  15. //如果有
  16. PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class);
  17. //得到Person对象上的写方法
  18. Method method1 = descriptor.getWriteMethod();
  19. //得到注解上的值
  20. Object o = method.invoke(annotation, null);
  21. //填充person对象
  22. method1.invoke(person, o);
  23. } catch (IntrospectionException e) {
  24. //如果没有想对应的属性,继续循环
  25. continue;
  26. }
  27. }
  28. //循环完之后,person就已经填充好数据了
  29. //6.把person对象设置到PersonDao中
  30. PersonDao personDao = new PersonDao();
  31. field.setAccessible(true);
  32. field.set(personDao, person);
  33. System.out.println(personDao.getPerson().getUsername());

总结

①:注入对象的步骤:得到想要注入的对象属性,通过属性得到注解的信息,将注解的信息注入到对象上,最后将对象赋给类

②:注解其实就是两个作用:

  • 让编译器检查代码
  • 将数据注入到方法、成员变量、类上

注解【介绍、基本Annotation、元Anntation、自定义注解、注入基本信息、对象】的更多相关文章

  1. 深入理解Java的注解(Annotation):自定义注解入门(2)

    要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...

  2. Annotation之三:自定义注解示例,利用反射进行解析

    @Retention定义了该Annotation被保留的时间长短有3中RetentionPolicy.SOURCE源文件有效,RetentionPolicy.CLASS:在class文件中有效,Ret ...

  3. java 编程基础:注解的功能和作用,自定义注解

    1,什么是注解: 从JDK5开始,Java增加了对元数据 (MetaData)的支持,也就是Annotation注解,这种注解与注释不一样,注解其实是代码里的特殊标记,这些标记可以在编译.类加载 运行 ...

  4. Java基础笔记 – Annotation注解的介绍和使用 自定义注解

    Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 |  被围观 25,969 views+ 1.Anno ...

  5. 深入理解Java:注解(Annotation)自定义注解入门

    转载:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准 ...

  6. 【转】深入理解Java:注解(Annotation)自定义注解入门

    http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的me ...

  7. Java:注解(Annotation)自定义注解入门

    转载地址:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的 ...

  8. 注解(Annotation)自定义注解入门

    摘自:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准 ...

  9. va注解应用实例 - Annotation, 自定义注解, 注解类规则【转】

    本文介绍了java的自定义注解及注解类编写的规则, 并通过实例来说明下如何使用java的注解. 实例演示了注解在类,构造方法,方法和字段的使用. 可以从这里下载到完成的工程代码: http://dl. ...

  10. [2]注解(Annotation)-- 深入理解Java:注解(Annotation)自定义注解入门

    转载 http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 深入理解Java:注解(Annotation)自定义注解入门 要深入学习 ...

随机推荐

  1. VMware Workstation 12 Pro 之安装Windows10 EP系统

    VMware Workstation 12 Pro 之安装Windows10 EP系统... --------------- 先准备好要用的Win10的镜像文件ISO ---------------- ...

  2. 7.20.01 java格式化输出 printf 例子

    java格式化输出 printf 例子 importjava.util.Date; publicclassPrintf { publicstaticvoidmain(String[] args) { ...

  3. asp.net mvc 接入美圣短信 验证码发送

    第1步:登录美圣短信控制台 http://www.rcscloud.cn/hy/HY_ZH/login 账号:******* 密码:******* http://www.rcscloud.cn/com ...

  4. SkipList跳表基本原理

    为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗 ...

  5. asp .net 模板引擎 使用 Razor 生成html静态页面

    刚开始不是理解 写完之后 觉得还蛮简单的 分为这几个步骤 1.获取页面模板Html 2.获取数据 3.解析模板和数据,生成静态页Html代码 4.生成静态文件 模板形式是mvc的模式,会mvc 看一下 ...

  6. vue.js基础知识篇(5):过渡、Method和Vue实例方法

    第8章:过渡 1.CSS过渡 2.JavaScript过渡 3.渐进过渡 第9章:method Vue.js的事件一般通过v-on指令配置在HTML中,虽然也可以在js的代码中使用原生的addEven ...

  7. Java基础---其他对象

    第一讲     System类 一.概述 1.System是描述系统一些信息的类,类中的属性和方法都是静态的.不能被实例化,没有提供构造函数. 2.字段摘要 out:标准输出流.默认是控制台. in: ...

  8. JSON 理解

    转自: http://blog.csdn.net/qyf_5445/article/details/8635578 (json很全面的理解) http://www.cnblogs.com/haitao ...

  9. java 运行时常量、编译时常量、静态块执行顺序

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt223 常量是程序运行时恒定不变的量,许多程序设计语言都有某种方法,向编译器告 ...

  10. django源码解析一(请求处理流程)

    1.我们都知道WSGI是一个规范,规范了server和application之间通信的一些约束,server端在监听到请求之后,会把请求转给application去处理,他们之间关联起来的桥梁是一个e ...