先了解几个元注解
分别是@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;
}
}