一、注解(Annotation)简介

用武之地:
我们不拿定义做解释,因为相信看定义是非常枯燥的。注解用在哪?简单一句话就是,注解几乎是作为简化配置使用的技术。注解是jdk1.5以后出现的,它被广泛的用于现代的JavaWeb框架中作为配置使用,例如Spring、Struts、Hibernate、SpringMVC、MyBatis、SpringBoot等框架配置除了xml版本以外,注解(Annotation)也是配置的一种方法,在SpringBoot+SpringMVC+Spring+SpringData开发的项目中,几乎可以全部使用Annotation进行配置开发。它的优点是非常的便捷,某些情况下可以替代了xml配置的麻烦。

JavaSE自带的注解:
@Override:用于修饰此方法覆盖了父类的方法,如果子类方法与要覆盖的方法不同,则会报出编译错误;
  @Deprecated:用于修饰已经过时的方法;
  @SuppressWarnnings:用于抑制Java编译器的特定警告,比如unused警告、rawtypes警告、unchecked警告;

二、我们自己定义注解
自定义注解与注解的解析在常用情况下是离不开Java的反射技术的,注解技术与反射技术息息相关,如果对反射不熟悉的朋友请移步找到Java反射技术的文章一览然后再看,这样效果会更好。

元注解:
元注解就是Java内带最基础的注解(注解的注解),不可再分的注解,也就是自定义注解当中所需要声明的注解,用来修饰自定义注解的元注解

@Target
@Retention
@Documented
@Inherited

在jdk文档中的java.lang.annotation可以找到他们的身影,以及他们所支持的参数等等,下面对他们进行介绍。

1.@Target注解:
这个元注解最重要的作用是指定当前定义的注解是用在什么地方的,比如类、接口(包括注解类型)的头顶(修饰class),还是放在属性的头顶(修饰成员变量),还是放在方法的头顶(修饰方法),还是修饰局部变量、包、构造方法等。他有一个枚举类型的属性value,值为ElementType枚举类型的以下几个值:

ElementType.TYPE:用于修饰类、接口、enum、注解类型;

ElementType.CONSTRUCTOR:当前注解用于修饰构造方法;

ElementType.FIELD:用于修饰属性(成员变量);

ElementType.LOCAL_VARIABLE:用于修饰局部变量;

ElementType.METHOD:用于修饰方法;

ElementType.PACKGE:用于修饰包;

ElementType.PARAMETER:用于修饰方法参数;

2.@Retention元注解:
这个注解表示当前定义的注解的生命周期(在什么范围内有效),有只能保留在源码中的,它会被编译器丢弃;有能通过编译器并在class字节码中,但是会被JVM忽略的;还有一类就是在class被装载时将被读取的(这是最重要的,请继续往后看);取值也是enum类型RetentionPoicy的值:

RetentionPoicy.SOURCE:只保留在源码中的,在源码中有效,比如@Override;

RetentionPoicy.CLASS:在字节码中保留,在字节码中有效,用的较其它两项少;

RetentionPoicy.RUMTIME:在运行时保留,这是用得最多的,因为我们可以通过反射获得注解过的类(下面会讲到);

3.@Documented元注解:
与javadoc文档化有关的注解,它是一个标记注解,没有成员;

4.@Inherited
如果使用了这个注解,那么用户自定义的注解将是可以被继承的,如果父类使用了这个注解,它的子类也会有这个注解。请注意,类实现接口不会延伸这个继承性,普通继承情况下的方法重载不会延伸Annotation的继承性。
自定义注解:
我们直接贴代码吧:
例子1:
package org.fage.annotations;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author Fapha
 * @date 2017年4月13日下午4:29:08
 * @version v.0.1
 * <p>Description: 声明这是一个bean</p>
 *
 */
@Inherited//注解可以被继承
@Documented//文档化
@Retention(RetentionPolicy.RUNTIME)//可以被反射获取
@Target(ElementType.TYPE)//注解在类头顶的类型
public @interface Bean {
    String value() default"This is a JavaBean";
    String name();
}

Java中声明一个注解是使用@interface进行声明创建的,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型,返回值类型只能是基本类型Class、String、enum、int、double....方法可以设置为默认权限与public权限,方法声明后可以紧跟default关键字,意义是为注解设置相对应的默认属性;如果没有default关键字,那么就是不设置默认值,在使用注解的时候必须为没有默认值的属性赋值。
以上便是一个最简单的注解,这个注解有一个value方法,返回值类型是String类型,其实也就是对应在使用注解的时候的value属性(比如在用的时候可以@Bean(value="JavaBean")),default关键字后的一串值就是在使用@Bean注解时value属性的默认值,有默认值的属性在使用这个注解的时候如果不指定其值,则使用默认值;如果注解在定义的时候某个属性没有设置默认值,则必须要求使用者给其赋予属性值;比如上面声明的注解可以这样用:
@Bean(value="hello",name="cat")//为value设置值为"hello"
public class Test(){}
如果自定义注解中有value方法,那么在使用自定义注解的时候可以省略value="hello",直接写成"hello"。
也可以不设置value值:
@Bean(name="cat")//value默认为This is aJavaBean
public class Test(){}
此时@Bean的value属性默认就是"This is a JavaBean"
例子2:
package org.fage.annotations;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author Fapha
 * @date 2017年4月13日下午2:39:34
 * @version v.0.1
 * <p>Description: 注解学习02,小水果Annotation</p>
 */
@Target(ElementType.TYPE)//放在class头顶的注解
@Retention(RetentionPolicy.RUNTIME)//此注解在运行时可以通过反射机制获取
@Documented//生成文档
@Inherited//注解的继承性
public @interface Fruit {
    //水果名
    public String name() default "";
    //水果水分值
    public String value() default "";
    //水果价格
    public int price();
    //是不是瓜类
    public boolean isMelon() default false; 
    
    //水果所属的类型(测试Enum)
    public enum FruitType{WATERMELON,APPLE,ORANGE,OTHER};  
    public FruitType fruitType()default FruitType.OTHER;
}

为这个注解的fruitType设置了默认属性为FruitType.OTHER,以下是对这个两个自定义注解的使用:
使用注解:

@Fruit(isMelon=true,name="WaterMelon",price=10,fruitType=FruitType.WATERMELON,value="12.33")
@Bean
public class FruitBean {}

三、解析注解
解析注解会用到Java反射的技术,也就是当今大名鼎鼎的Spring、Struts、Hibernate等等框架使用的反射原理,他们最开始是拿xml、properties、yml文件等等做配置,往后又加入了Java注解做配置的技术,核心就在解析注解,反射解析注解然后做相应的逻辑处理。

代码贴上,我们就拿刚才的@Bean与@Fruit两个注解开刀,拿FruitBean做被注解的类做测试例子,测试注解在class上的用例,读取注解中的属性,添加以下的测试代码:

/**
     * <p>Description:测试放在类头上的Annotation<p/>
     */
    @Test
    public void testFruit(){
        FruitBean fruit = new FruitBean();
        //通过类反射获得注解
        Fruit fruitAnnotation = fruit.getClass().getAnnotation(Fruit.class);
        Bean beanAnnotation = fruit.getClass().getAnnotation(Bean.class); 
        //可以把反射得到的注解,将其方法进行调用
        if(fruitAnnotation!=null){
            //如果@Bean起作用了,那么这行会输出This is a JavaBean
            if(beanAnnotation!=null)
            System.out.println(beanAnnotation.value());
            
            System.out.println(fruitAnnotation.isMelon());
            System.out.println(fruitAnnotation.price());
            System.out.println(fruitAnnotation.annotationType());
            System.out.println(fruitAnnotation.name());
            System.out.println(fruitAnnotation.fruitType());
        }
    }
输出结果:
This is a Fruit JavaBean!!
true
10
interface org.fage.annotations.Fruit
WaterMelon
WATERMELON
可见已经把属性都读取出来了
接下来我们要测试为类的属性注入相应的值,创建一个Annotation:

/**
 * 
 * @author Fapha
 * @date 2017年4月13日下午3:27:42
 * @version v.0.1
 * <p>Description: 测试属性设置Annotation</p>
 *
 */
@Documented
//这个注解可以放在方法、属性上
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SetString {
    String value() default"";
}
上述Annotation的@Target标签有两个值,一个表示可以注解在属性上,一个表示可以注解在方法上:接下来创建一个User类,我们来为他注入属性:
package org.fage.bean;
import org.fage.annotations.SetString;
public class User {
    //在属性上面注解注入username为"caizhifa"
    @SetString("caizhifa")
     private String username;
     private String password;
    
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    //在方法上注解注入password为123456
    @SetString("123456")
    public void setPassword(String password) {
        this.password = password;
    }
}

编写测试:
@Test
    public void testUser() throws Exception{
        //反射获取当前名称表示的类
        Class<?> clazz = Class.forName("org.fage.bean.User");
        User user  = (User) clazz.newInstance();    
        //得到该类的所有字段
        Field[] fields = clazz.getDeclaredFields();
        //遍历所有字段,找出字段头上是否有相应注解的字段
        for(Field f:fields){
            //测试是否能得到字段的名称
            //System.out.println(f.getName());
            SetString annotation = f.getAnnotation(SetString.class);
            //如果找到了字段头上有相应的注解
            if(annotation!=null){
                //暴力一把跳过private权限,把注解上value的值设置到相应的字段上
                f.setAccessible(true);
                f.set(user,annotation.value());
            }
        }
        
        //查找是否有方法头顶上是否有相应的注解
        Method[] methods = clazz.getDeclaredMethods();
        for(Method m:methods){
            //System.out.println(m.getName());
            SetString annotation = m.getAnnotation(SetString.class);
            //如果有SetString这个注解,直接传入value的值给方法参数
            if(annotation!=null){
                m.invoke(user,annotation.value());
            }
        }
        System.out.println(user.getUsername()+"|"+user.getPassword());
    }

上面代码的运行结果是: caizhifa|123456,可见已经有雏形的注入功能了

————————————————
转至:CSDN博主「梅仁」的原创文章,遵循 CC 4.0 BY-SA 版权协议,
原文链接:https://blog.csdn.net/Phapha1996/article/details/70175675

Java注解原理解析 转载至:梅仁相关推荐

  1. Java类加载原理解析(转)

    1       基本信息 摘要: 每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加 ...

  2. Java类加载原理解析

    Java类加载原理解析 基本信息摘要: 每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java ...

  3. java 序列化 原理解析

    序列化相关文章: * Java 序列化 之 Serializable * Java 序列化之 Externalizable * Java 序列化 之 单例模式. 阅读本文章之前,务必要阅读上面的三篇文 ...

  4. Servlet 工作原理解析--转载

    原文:http://www.ibm.com/developerworks/cn/java/j-lo-servlet/index.html?ca=drs- Web 技术成为当今主流的互联网 Web 应用 ...

  5. 基本功 | Java即时编译器原理解析及实践

    跟其他常见的编程语言不同,Java将编译过程分成了两个部分,这就对性能带来了一定的影响.而即时(Just In Time, JIT)编译器能够提高Java程序的运行速度. 本文会先解析一下即时编译器的 ...

  6. Java即时编译器原理解析及实践

    一.导读 常见的编译型语言如C++,通常会把代码直接编译成CPU所能理解的机器码来运行.而Java为了实现"一次编译,处处运行"的特性,把编译的过程分成两部分,首先它会先由java ...

  7. Java并发原理解析!java零基础教学系列视频教程

    前言 Linux 网络协议栈是根据 TCP/IP 模型来实现的,TCP/IP 模型由应用层.传输层.网络层和网络接口层,共四层组成,每一层都有各自的职责. 应用程序要发送数据包时,通常是通过 sock ...

  8. 深入理解Java类加载器:Java类加载原理解析

    http://blog.csdn.net/zhoudaxia/article/details/35824249 1 基本信息 每个开发人员对java.lang.ClassNotFoundExcetpi ...

  9. Java注解全面解析

    1.基本语法 注解定义看起来很像接口的定义.事实上,与其他任何接口一样,注解也将会编译成class文件. @Target(ElementType.Method)@Retention(Retention ...

最新文章

  1. 深度学习被高频引用的41篇论文下载(附下载)
  2. 报名 | 赢取20万美金!Call For Code编程马拉松北京站来袭!
  3. jmeter 多机负载压测与服务器性能监测
  4. 芝麻信用_别忘查看支付宝芝麻信用分,这些特权别浪费
  5. python-week5
  6. 每天进步一点点《ML - 基于层次的聚类》
  7. 使用Tslib在触摸屏上显示汉字
  8. tornado cookie和session
  9. jupyter-notebook设置⽀持远程访问
  10. 独立的定义有多重等价表述方式
  11. 开启win7笔记本自带无线功能
  12. 阿里、京东、拼多多电商三巨头财报大比拼:拼多多用户数上拼了 京东营收超过阿里...
  13. looking for domain authoritative name server and domain name location
  14. 网站域名服务器怎么备案,网站域名服务器怎么备案
  15. 练字格子纸模板pdf_十字田字格模板空格40格-练字用书十字格a4打印版下载最新excel版-西西软件下载...
  16. Java查找算法-17-二分查找
  17. Web前端技术第一节
  18. 微信点餐小程序开发_分享微信点餐小程序可以实现哪些功能
  19. 【Python实战】数据预处理(数据清理、集成、变换、归约)
  20. vue使用高德地图的搜索地址和拖拽选址

热门文章

  1. 还有必要吗?iPhone 11系列终于要全家族支持双卡双待了
  2. 李楠宣布离职!“魅族三剑客”时代已逝去,前老板的回应却有点扎心
  3. 高通芯片曾被发现一重大漏洞 影响骁龙845等30多款芯片
  4. 华为畅享9S曝光:2400万超广角AI三摄+珍珠屏
  5. 你大爷还是你大爷!三星震撼首发折叠屏智能手机Galaxy Fold
  6. 我对正向代理和反向代理的理解
  7. c4503文件服务器,理光C3503/C4503/C5503检查状态下各项目说明解释
  8. git 如何撤销提交
  9. 我的docker随笔10:docker客户端使用其它主机的docker服务器
  10. 【clickhouse】clickhouse 单机安装 集群安装