Java注解原理解析
来源:互联网 发布:linux posix 编辑:程序博客网 时间:2024/05/01 02:37
一、注解(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内带最基础的注解(注解的注解),不可再分的注解,也就是自定义注解当中所需要声明的注解,用来修饰自定义注解的元注解
@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
自定义注解:
例子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关键字,那么就是不设置默认值,在使用注解的时候必须为没有默认值的属性赋值。
@Bean(value="hello",name="cat")//为value设置值为"hello"public class Test(){}
如果自定义注解中有value方法,那么在使用自定义注解的时候可以省略value="hello",直接写成"hello"。@Bean(name="cat")//value默认为This is aJavaBeanpublic 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")@Beanpublic class FruitBean {}
三、解析注解
解析注解会用到Java反射的技术,也就是当今大名鼎鼎的Spring、Struts、Hibernate等等框架使用的反射原理,他们最开始是拿xml、properties、yml文件等等做配置,往后又加入了Java注解做配置的技术,核心就在解析注解,反射解析注解然后做相应的逻辑处理。
代码贴上,我们就拿刚才的@Bean与@Fruit两个注解开刀,拿FruitBean做被注解的类做测试例子,测试注解在class上的用例,读取注解中的属性,添加以下的测试代码:
/** * <p>Description:测试放在类头上的Annotation<p/> */@Testpublic 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 JavaBeanif(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!!true10interface org.fage.annotations.FruitWaterMelonWATERMELON可见已经把属性都读取出来了
接下来我们要测试为类的属性注入相应的值,创建一个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;}}
编写测试:
@Testpublic 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,可见已经有雏形的注入功能了,大家可以尝试进一步的编写,看看Spring中@Autowire、@Resource是怎么根据类型、名称进行注入的。
教程结束啦!希望大家多多指教,如有错误,欢迎指正,如有雷同,纯属巧合,转载文章请带上链接原创哦。
- Java注解原理解析
- java自定义注解原理深度解析
- spring注解原理解析
- 注解工作原理源码解析
- java注解原理
- Java注解原理分析
- java注解实现原理
- Java 注解原理
- java 注解原理分析
- 全面解析Java注解
- java注解解析
- 全面解析Java注解
- java注解解析
- Java解析注解
- 全面解析Java注解
- Java注解机制解析
- Java注解解析(一)
- 全面解析 Java 注解
- [Codevs] 3044 矩形面积求并(离散化)
- 算法
- ubuntu校园网登陆客户端 mentohust下载安装
- 线段树练习题一
- Spring AOP 面向切面编程 常见通知实现(前置,后置,环绕,异常)
- Java注解原理解析
- 动画演示Raft
- 小马哥---高仿苹果7p 9900-875AQ0-A-6571新版刷机拆机图示与识别图
- opencv简单形状绘制
- 查字典
- 图像处理中的形态学(一)
- 关于parent()、parents()和closest()的区别
- 线段树联系2
- Android基础知识--TextView