定义

注解是在JDK1.5之后引入的新特性位于java.lang.annotation,注解其实就是对代码进行一种特殊的标记,这些标记可以在编译,类加载和运行时被读取,并执行相应的处理。

第三方注解

在Java开发者,JDK自带了一些注解,在第三方框架Spring  带了大量的注解,这些注解称为第三方注解

1、Jdk通用注解

  1. @Override注解:编译检查,告诉编译器这个是个覆盖父类的方法。如果父类删除了该方法,则子类会报错。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  1. @Deprecated 注解:编译检查,表示被注解的元素已被弃用
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
                ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Deprecated {
}

上面两个注解除了都有一些元注解之外,没有任何的东西,但是我们加了这两个注解后,编译器就能够识别,可以将其理解为是一个标签,它并没有实际的逻辑处理,而实现逻辑的就是注解的用户。它本质就是一个 『标记式注解』,仅被编译器可知 ,这就是注解的工作原理。

注解的本质就是一个接口,该接口默认继承了java.lang.annotation.Annotation接口。

public interface Annotation {
        boolean equals(Object var1);
 
        int hashCode();
 
        String toString();
 
        Class<? extends Annotation> annotationType();
}

 

2、元注解

元注解用于注解其他注解的。Java 5.0定义了4个标准的元注解,如下:

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

 

2.1@Target

@Target注解用于声明注解的作用范围,例如作用范围为类、接口、方法等。源码:

package java.lang.annotation;
 
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
        ElementType[] value();
}

其中ElementType表明了该注解可以使用的位置:

public enum ElementType {
        TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE;
}

具体解释:

枚举

作用

ElementType.PACKAGE

注解用在包

ElementType.TYPE

注解作用于类型(类,接口,注解,枚举)

ElementType.ANNOTATION_TYPE

注解作用于注解

ElementType.CONSTRUCTOR

注解作用于构造方法

ElementType.METHOD

注解作用于方法

ElementType.PARAMETER

注解作用于方法参数

ElementType.FIELD

注解作用于属性

ElementType.LOCAL_VARIABLE

注解作用于局部变量

2.2@Retention

@Retention注解的作用就是指定注解的生命周期。比如在编译时可以处理运行时可以处理等。源码:

@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.ANNOTATION_TYPE})
public @interface Retention {
        public RetentionPolicy value();
}

 

它的枚举类为RetentionPolicy,只有三个可选值

 

枚举

作用

RetentionPolicy.SOURCE

源码中保留,编译期可以处理

RetentionPolicy.CLASS

Class文件中保留,Class加载时可以处理

RetentionPolicy.RUNTIME

运行时保留,运行中可以处理

2.3@Documented

是一个标记注解,有该注解的注解会在生成 java 文档中保留

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Documented {
}

 

2.4@Inherited

@Inherited 注解修饰的注解时具有可继承性的,就是说我们用 @Inherited 修饰了一个类,那么这个类的子类也会默认继承此注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Inherited {
}

 

自定义注解

在第三方注解没法满足我们业务要求的时候,需要我们自己写注解,这个时候就是自定义注解。

1.1、自定义注解语法

在注解中,需要使用四种元注解来声明注解的作用范围、生命周期、继承,是否生成文档等。另外在注解中也可以有自己的成员变量,如果一个注解没有成员变量则称为标记注解。

 

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Documented
public @interface CxmAction {
    String code() default "";// 权限码
String descrption() default "";// 描述
 
String prompt() default ""; // 权限提示
}

 

1.2、注解使用说明

1.2.1、AOP + 注解

使用AOP是一种常见的处理方式,首选,需要定义切面类

@Aspect
@Component
@Slf4j
@Order(-1)
public class CxmLogbackAspect {
 
/***
 * 定义controller切入点拦截规则,拦截SystemLog注解的业务方法
 */
@Pointcut("@annotation(com.cxm.common.annotation.SystemLog)")
public void logPointcut(){
 
}
 
/**
 * 环绕
 * @param proceedingJoinPoint
 * @return
 * @throws Throwable
 */
@Around("logPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    // 执行具体的业务逻辑
}
 
}

 

  • 1.2.2、HandlerInterceptor + 注解

使用Spring的拦截器HandlerInterceptorAdapter,也可以实现注解的业务,比如系统中使用这种方式配合SkipCertification,实现了某些指定接口跳过token验证的功能

@Slf4j
@Component
public class PermissionInterceptor extends HandlerInterceptorAdapter {
 
/**
 * 
  * @Title: preHandle 
  * @Description: 在Controller执行之前调用,如果返回false,controller不执行
  * @param  @param request
  * @param  @param response
  * @param  @param handler
  * @param  @return
  * @param  @throws Exception 
  * @throws 
  *
 */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (!(handler instanceof HandlerMethod)) {// 如果不是HandlerMethod而是静态资源的请求,就直接跳过去
        return true;
    }
    HandlerMethod method = (HandlerMethod) handler;
    SkipCertification  certification = method.getMethodAnnotation(SkipCertification.class);
    if (null != certification) {// 跳过token验证
        return true;
    } else {
         // 执行校验  
    }
}
 
}

 

1.2.3、java反射 + 注解

通过反射,可以获取添加注解的属性值

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
    TestAnnotation testAnnotation = new TestAnnotation();
    Field[] fields = obj.getClass().getDeclaredFields();
      for(Field field : fields) {
          field.setAccessible(true);
          if(field.isAnnotationPresent(testAnnotation )) {
              map.put(field.getName(),field.get(obj));
           }
        }
    }

 

1.2.4、 Filter + 注解

使用过滤器也可以实现直接的功能,比如利用fastJSON的AfterFilter,实现属性增强

public void doWrite(List<Field> fields, Object object) {
    for (int i = 0; i < fields.size(); i++) {
        Field field = fields.get(i);
        Stall stallAnn = AnnotationUtils.getAnnotation(field, Stall.class);
        if (stallAnn != null) {
                Object value = ReflectionUtils.getField(field, object);
                if (value != null) {
                        this.doWrite(field, value);
                }
        }
    }
}