自定义注解——基于AOP实现

先了解几个元注解
分别是@Target, @Retention,@Documented,@Inherited

@Target:决定自定义的注解用在什么地方

属性值 作用
ElementType.CONSTRUCTOR 用在构造函数的声明上
ElementType.FIELD 定义在成员变量上
LOCAL_VARIABLE 局部变量的声明
METHOD 定义在方法上面
PACKAGE 包的声明
PARAMETER 用在参数的声明上
TYPE 类、接口、枚举的声明

比如我们常用的@Override的定义

@Target(ElementType.METHOD)  //声明该注解用于方法上
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

值得一提,和@RequestMapping类似,@Target(ElementType.METHOD)等同于@Target(value=ElementType.METHOD)

@Retention
这个注解的保留级别

属性值 作用
RetentionPolicy.SOURCE 注解将被编译器丢弃
RetentionPolicy.CLASS 注解在class文件中可用, 但会被VM丢弃
RetentionPolicy.RUNTIME VM将运行期也保留注解信息,因此可用通过反射机制来读取注解的信息

我们之前使用注解做过一个AOP的参数合法性校验,使用的自定义注解就是RUNTIME,因为要通过反射获取参数值
而Class的取值可用,则有点类似于lombok的@Data注解,主要用来在class文件中生成代码

@Documented @Inherited
这两个注解比较简单,@Documented 的主要作用是用来生成注释,表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
@Inhrited 是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注解实现类

一般有两种方法,一种是通过AOP,拦截所有拥有某注解的类/方法/枚举…,另一种就是apt,反射使用太多比较消耗性能,但APT好像才安卓中用比较多,这里只是简单描述。

AOP实现自定义注解处理器

aop切面中定义五种切面方法

前置通知:@Before 在目标业务方法执行之前执行
后置通知:@After 在目标业务方法执行之后执行
返回通知:@AfterReturning 在目标业务方法返回结果之后执行
异常通知:@AfterThrowing 在目标业务方法抛出异常之后
环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行
写一个获取方法参数并将参数进行加一操作的小栗子。
定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ArgsFilter {
    String[] rules() default  {};
}

定义切面处理类,确定切点位置(下面例子中切点和具体执行方法分开处理)

@Aspect
@Component
public class ArgsFiletImpl  {
   //等价于 @Pointcut("value=@annotation(argsFilter)")
    //定义哪些方法会被切面拦截,这里定义的是ArgsFilter注解使用的地方
    @Pointcut("@annotation(argsFilter)")
 public void checkAnnoation(ArgsFilter argsFilter){
    }

切点Pointcut中的参数
由下列方式来定义或者通过 &&、 ||、 !、 的方式进行组合:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法

具体处理方法

@Around(value = "auditLogPointcut(auditLog)")
//ProceedingJoinPoint仅用于around方法中,新增两个方法 执行目标方法和新增参数执行目标方法
public Object around(ProceedingJoinPoint joinPoint, AuditLog auditLog) throws Throwable {
    //获取请求对象
  HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
    Map<String, String[]> parameterMap = request.getParameterMap();
    StringBuffer requestURL = request.getRequestURL();
    MethodSignature signature = (MethodSignature)joinPoint.getSignature(); // 代理对象
    Method targetMethod = signature.getMethod(); //获取参数所在方法
    final Class declaringType = signature.getDeclaringType();//获取声明所在类
    String declaringTypeName = signature.getDeclaringTypeName();//类名
    Object object=joinPoint.proceed();//执行目标方法
    return object;
}

ProceedingJoinPoint对象
roceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
在这里插入图片描述

apt介绍

apt(annotation processing tool) 注解处理工具,就是操作Java源文件,当处理完源文件后编译它们,在系统创建的过程中会自动创建一些新的源文件,这些新文件会在新的一轮中的注解处理器中接受检查,直到不再有新的源文件产生为止。这个过程中是发生在编译期间(compile time),而非运行期间,
我们可以使用ProcessingEnvironment获取一些实用类以及获取选项参数等:

public interface ProcessingEnvironment {
    Map<String, String> getOptions(); //返回指定的参数选项
    Messager getMessager(); //返回实现Messager接口的对象,用于报告错误信息、警告提醒
    Filer getFiler(); //返回实现Filer接口的对象,用于创建文件、类和辅助文件
    Elements getElementUtils(); //返回实现Elements接口的对象,用于操作元素的工具类
    Types getTypeUtils(); //返回实现Types接口的对象,用于操作类型的工具类
    SourceVersion getSourceVersion();
    Locale getLocale();
}

apt一般是需要继承AbstractProcessor 类


public class ArgsFiletImpl extends AbstractProcessor {
    protected ArgsFiletImpl() {
        super();
    }
  //指定该处理器是工作于哪个注解的
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(ArgsFilter.class.getCanonicalName());
    }
    @Override
    public SourceVersion getSupportedSourceVersion() {//指定当前jdk版本
        return  SourceVersion.latestSupported();
    }
   //初始化方法
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

   //核心方法
    //这些注解是否由此 Processor 处理,该方法返回ture表示该注解已经被处理, 后续不会再有其他处理器处理; 返回false表示仍可被其他处理器处理
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }

}

猜你喜欢

转载自blog.csdn.net/qq_41700030/article/details/103058858