定义
注解是在JDK1.5之后引入的新特性位于java.lang.annotation,注解其实就是对代码进行一种特殊的标记,这些标记可以在编译,类加载和运行时被读取,并执行相应的处理。
第三方注解
在Java开发者,JDK自带了一些注解,在第三方框架Spring 带了大量的注解,这些注解称为第三方注解
1、Jdk通用注解
- @Override注解:编译检查,告诉编译器这个是个覆盖父类的方法。如果父类删除了该方法,则子类会报错。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- @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个标准的元注解,如下:
- @Target
- @Retention
- @Documented
- @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);
}
}
}
}