引子:全中国的Java程序员都知道,如今主流的项目架构都是ssm、ssh、springcloud等,而这些框架都离不开spring,而spring中使用了大量的注解(包括spring自定义的注解)。所以想要会用注解,咱们就得知道Java注解的原理和基本用法,这样有助于咱们在项目中如鱼得水。java
在JDK5.0中,新增了不少对如今影响很大的特性,如:枚举、自动装箱和拆箱、注解、泛型等等。其中注解的引入,是为了增长对元数据的支持。程序员
注解:是一个接口,程序能够经过反射来获取指定程序元素的Annotation对象,而后经过Annotation对象来取得注解里的元数据,注解能用来为程序元素(包、类、方法、成员变量等)设置元数据,它不影响程序代码的执行。若是但愿让程序中的Annotation在运行时起必定做用,只有经过某种配套的工具对Annotation中的信息进行访问和处理,这个工具同城为APT(Annotation program Tool)spring
JDK5.0除了增长注解特性以外,还提供了5个基本的Annotation:编程
- @Overrride:限定重写父类方法(旨在强制性提醒)
- @Deprecated:表示某个程序元素(类、方法)已过期
- @SuppressWarnings:抑制编译警告
- @SafeVarargs:堆污染警告
- @FunctionalInterface:指定某个接口必须为函数式接口 注:函数式接口是指一个接口中只包含一个抽象方法(能够包含多个默认方法或多个static方法)
以上这几个注解,在咱们的项目常常可见,可是由于他们对程序只是一个强制提醒或者警告做用,并不影响程序的执行,由于咱们都没在乎这些注解的做用,而当spring出来以后,大量的注解眼花缭乱,做用各异,但他们都有一个共同做用: 让咱们在编码过程当中简化了很多重复性的代码。 所以咱们在了解了注解的原理以后,必需要能自定义一些注解而且使用它到项目中去,才能让咱们更好的了解和使用它。 而要想自定义注解, 就必须得了解Java提供的几个元注解 那什么是元注解呢? 元注解:就是负责注解其它注解的注解json
在Java5以后定义了4个标准的元注解,分别是:架构
target注解用来标识注解所修饰的对象范围。框架
它的可用范围(ElementType的取值范围)有:ssh
CONSTRUCTOR:用于描述构造器(构造方法)ide
FIELD:用于描述域(成员变量)函数
LOCAL_VARIABLE:用于描述局部变量(局部变量)
METHOD:用于描述方法(普通方法)
PACKAGE:用于描述包(包定义)
PARAMETER:用于描述参数(如catch等参数)
TYPE:用于描述类、接口(包括注解类型) 或enum声明
ANNOTATION_TYPE:用于注解
使用实例:
@Target(ElementType.FIELD)
public @interface TargetTest6{
}
@Target({ElementType.TYPE_PARAMETER,ElementType.METHOD})
public @interface TargetTest7{
}
复制代码
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出如今源代码中,而被编译器丢弃;而另外一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另外一些在class被装载时将被读取(请注意并不影响class的执行,由于Annotation与class在使用上是被分离的)。使用这个meta-Annotation能够对 Annotation的“生命周期”限制。
它的取值(RetentionPoicy)有:
SOURCE:在源文件中有效(即源文件保留),注解只保留在源代码中,编译器直接丢弃这种注解。
CLASS:在class文件中有效(即class保留),编译器把注解记录在class文件中,当Java程序运行时,JVM不能获取该注解的信息。
RUNTIME:在运行时有效(即运行时保留),编译器将把注解记录在class文件中,当Java运行时,JVM能够获取注解的信息,程序能够经过反射获取该注解的信息。
1@Target(ElementType.FIELD) 2@Retention(RetentionPolicy.RUNTIME) 3public @interface TargetTest6{ 4} @Inherited @Inherited注解指定被它休市的注解将具有继承性:若是莫个类使用了@XXX注解,则其子类自动被@XXX修饰
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface TargetTest6{
}
//全部使用了@TargetTest6注解的类讲具有继承性,
//也就是它的子类自动带上@TargetTest6注解
复制代码
@Documented用于指定被该注解修饰的注解类将被javadoc工具提取城文档.
1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3@Inherited
4@Documented
5@interface TargetTest6{
6}
7//javadoc工具生成的API文档将提取@Documented的使用信息
复制代码
以上就是全部的元注解以及他们的做用分析, 有了这些元注解有什么用呢? 。。。。 固然是为了方便咱们在项目中自定义注解咯 那自定义注解怎么自定义呢? 下面咱们来看看介绍如何自定义注解并利用注解完成一些实际的功能
语法:
类修饰符 @interface 注解名称{
//成员变量,在注解中以无形参的形式存在
//其方法名和返回值定义了该成员变的名字和类型
String name();
int age();
}
复制代码
能够看到:注解的语法和接口的定义很是相似,他也同样具有有做用域,可是它的成员变量的定义是以无形参的方法形式存在,名字定义了成员变量的名字,返回值定义了变量类型。 下面咱们来自定义一个注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
int age();
String name();
}
复制代码
注解@AnonTest在运行时有效,做用域在成员变量上,它有两个成员变量分别是int型的age和String型的name。 接下来咱们就可使用这个注解了
public class test {
@AnonTest(age = 0, name = "1")
public Integer tes;
}
复制代码
这样就是一个注解的定义和使用了,有人会疑惑说,spring中不少注解都是@xxx,为何这个@AnonTest必定要要带上两个成员变量呢? 缘由很简单: 注解中的成员变量若是没有默认值,则在使用注解时必需要给成员变量赋值 但若是成员变量有默认值,那能够直接在定义注解时,赋值上去,这样在使用时就能够省略不写
@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
int age() default 1;
String name() default "注解辣鸡";
}
复制代码
这样在调用注解时就能够不赋值
public class test {
@AnonTest
public Integer tes;
}
复制代码
上面看到,咱们已经使用了注解,可是咱们并没发现@AnonTest对咱们的tes成员变量有任何做用,这是由于注解自己在程序中是不会生效的,而是须要程序来提取数据而且处理注解本应该作的工做。 所以 咱们须要知道如何从注解中提取信息而且作相应的处理。 Java使用Annotation接口来表明程序元素前面的注解,该接口是全部注解的父接口,该接口主要有如下几个实现类:
Class:类定义
Constructor:构造器定义
Field:类成员变量定义
Method:类的方法定义
Package:类的包定义
AnnotatedElement接口是全部程序元素所实现的接口的父接口,因此程序经过反射获取了某个类的AnnotatedElement对象以后,程序就能够调用该对象的以下几个方法来访问注解信息:
getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,若是该类型注解不存在,则返回null。
getAnnotations():返回该程序元素上存在的全部注解。
isAnnotationPresent(Class annotatonClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,不然返回false.
getDeclaredAnnotations():返回直接存在于此元素上的全部注释。
复制代码
有了这些方法,咱们就能够在类、方法、成员变量等程序元素中获取到注解信息 好比:
1@Component
2@Service
3public class test {
4
5 @AnonTest
6 public Integer tes;
7
8 @Deprecated
9 public void m1(){
10
11 }
12
13 public static void main(String[] args) {
14 try {
15 //获取全部test类上的注解
16 Annotation[] ar=Class.forName("com.anon.test").getAnnotations();
17 //获取全部test类上m1方法的注解
18 Annotation[] ar1=Class.forName("com.anon.test").getMethod("m1").getAnnotations();
19 //遍历全部的注解,检测是否有咱们想要的某个注解 如@Component注解
20 for(Annotation a:ar){
21 System.out.println(a.getClass());
22 if(a instanceof Component)
23 System.out.println("用了注解:"+a);
24 }
25 } catch (Exception e) {
26 // TODO Auto-generated catch block
27 e.printStackTrace();
28 }
29 }
30}
31
32//输出:
33//用了注解:@org.springframework.stereotype.Component(value=)
34//class com.sun.proxy.$Proxy4
复制代码
利用AnnotatedElement提供的方法能获取到咱们想要的注解信息以后,就能够针对注解作一些特定的行为。
例如,对于全部的注册用户信息,系统须要把名称和年龄上报到另外一个年龄分布表中,就能够利用注解就能够完成这样的动做
1public class test {
2 @AnonTest(name="张小龙",age=25)
3 public Integer tes;
4 public static void main(String[] args) {
5 test.getInfo(test.class);
6 }
7 public static void getInfo(Class<?> clazz) {
8 //获取目标类的全部成员变量
9 Field[] fields = clazz.getDeclaredFields();
10 for (Field field : fields) {
11 //查找变量中是否有存在@AnonTest注解
12 if (field.isAnnotationPresent(AnonTest.class)) {
13 //若是存在,则作相应处理
14 AnonTest anon= field.getAnnotation(AnonTest.class);
15 System.out.println("名称是:"+anon.name());
16 System.out.println("年龄是:"+anon.age());
17 System.out.println("上报信息到用户年龄分布表中");
18 //上报信息到用户年龄分布表中
19 //uploadInfoToLog(anon.name()),anon.age())
20 }
21 }
22 }
23}
复制代码
输出:
1名称是:张小龙
2年龄是:25
3上报信息到用户年龄分布表中
复制代码
最终程序按照咱们的需求运行而且输出正确的信息,因而可知,注解对于Java来讲是一种补充,他的存在与否对程序来讲应该是无影响的,灵活使用注解能使开发的效率更高,并且程序规范性也获得加强。
以为本文对你有帮助?请分享给更多人
关注「编程无界」,提高装逼技能