JAVA基础5--注解的实现原理

一、注解简介

注解也叫元数据,是JDK1.5版本开始引入的一个特性,用于对代码进行标记说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解修饰

1.1、注解的类型

1、JDK注解和框架注解:JDK本身提供了很多注解比如@Resource、@PostConstruct等;另外常用的框架也提供了很多注解,比如Spring的@Autowired,@Service等等,这些注解使用时会自动被JDK或框架进行识别解析;

2、元注解:元注解用于修饰注解的,如@Retention(标明注解被保留的阶段)、@Target(标明注解使用的范围)、@Inherited(标明注解可继承)、@Documented(标明是否生成javadoc文档)

3、自定义注解:用户可以根据自行需求自定义注解

1.2、元注解

@Retention:定义注解的生命周期,默认是CLASS,取值范围如下:

SOURCE 在编译阶段被抛弃,通常用于编译时使用,比如@Override注解
CLASS 在编译阶段会被写入字节码,当类加载的时候会被丢弃
RUNTIME 不会被丢弃,运行期间也可以使用,所以通过反射机制就可以读取该注解的信息,通常自定义注解都会采用RUNTIME类型

@Target:定义注解可以修饰的目标,默认是可以修饰任意目标,取值范围如下

TYPE 用于描述类、接口或enum声明,如@Service、@Component等
FIELD 用于描述属性,比如@JSONField等
METHOD 用于描述方法,比如@Override
PARAMETER 用于描述方法参数,比如Mybatis框架中的@Param
CONSTRUCTOR 用于描述构造函数
LOCAL_VARIABLE 用于描述局部变量
ANNOTATION_TYPE 用于描述注解类型,比如@Target本身,@Retention,@Document注解等
PACKAGE 用于描述包名
TYPE_PARAMETER 用于描述参数类型
TYPE_USE 表示该注解能使用在使用类型的任意语句中

@Inherited:定义注解是否继承给子类

当@Inherited注解修饰了一个注解,那么如果这个注解修饰了一个类,那么这个类的子类也会继承该注解

@Documented:定义注解是否将注解信息加到Java文档中

1.3、注解的组成

注解通常有几个部分组成,包括修饰该注解的元注解,注解名称和注解方法,当然也可以将注解仅当作标记作用,没有任何方法也行,比如@Override注解就没有任何方法,仅当作标记使用

二、注解的使用

通常我们Web服务提供接口需要用户登录之后才可以访问,此时如果每个接口都判断下用户是否登录就会冗余很多的代码,所以需要在执行接口方法之前有一层验证用户登录的逻辑,可以通过过滤器,拦截器等方式实现,此时就可以配合注解来实现,在需要进行登录验证的方法上添加一个自定义的注解,然后每个添加了注解的方法就需要验证登录,没有注解的方法就不需要登录,实现方式如下:

自定义注解@Logined

@Documented
@Target(ElementType.METHOD) /** 修饰方法*/
@Retention(RetentionPolicy.RUNTIME)/** 生命周期为运行期间*/
public @interface Logined {

    /** 定义方法,如果没有登录的情况下是否直接抛异常*/
    boolean exception() default false;
}

定义了注解之后就可以直接使用,但是想要使注解的效果生效,就需要有一套获取注解并处理业务的逻辑,此时就离不开Java的反射机制,需要通过反射机制获取到修饰在方法、类、属性上的注解来进行判断是否加了注解。

另外在使用Spring框架时,可以配置AOP来配合使用自定义注解,比如以下案例就是用来处理@Logined注解的逻辑:

@Aspect
@Component
public class LoginAspect {

    @Around("@annotation(com.test.annotation.Logined)")
    public Object doBefore(ProceedingJoinPoint jp) throws Throwable {
        if (MessageConfig.LOCAL.get() == null) {
            System.out.println("请求用户为空,返回401:" + jp.getSignature().getName());
            return Result.returnUnauthorized();
        }
        return jp.proceed();
    }
}

三、注解的实现原理

注解本身没有任何逻辑,只能起到标记的作用,实现的逻辑完全取决于处理注解的逻辑,而处理注解就需要先找到注解,此时就离不开Java的反射机制,主要是通过Constructor、Class、Method、Field等反射相关类的getAnnotation(Class annotationClass)方法获取对应的注解,如果能获取到注解那么就表示被注解修饰了,案例如下:

 1 /** 1.查找类上的注解 */
 2         Annotation classAnnotation = cla.getAnnotation(Logined.class);
 3         if(classAnnotation != null){
 4             System.out.println("类被@Logined注解修饰");
 5         }
 6 
 7         /** 2.查找方法上的注解 */
 8         Method[] methods = cla.getMethods();
 9         for (Method method : methods){
10             if(method.getAnnotation(Logined.class) != null){
11                 System.out.println("方法:" + method.getName() + "被注解@Logined" + "修饰");
12             }
13         }
14 
15         /** 3.查找属性上的注解 */
16         Field[] fields = cla.getFields();
17         for (Field field : fields){
18             if(field.getAnnotation(Logined.class) != null){
19                 System.out.println("属性:" + field + "被注解@Logined" + "修饰");
20             }
21         }
22 
23         /** 4.查找构造函数上的注解 */
24         Constructor constructor = cla.getConstructor(String.class);
25         if(constructor.getAnnotation(Logined.class)!=null){
26             System.out.println("构造器被@Logined注解修饰");
27         }
原文地址:https://www.cnblogs.com/jackion5/p/14700195.html