AOP
Aspect
(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。Joint point
(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。Pointcut
(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。Advice
(增强):Advice 定义了在Pointcut
里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。Target
(目标对象):织入Advice
的目标对象.。Weaving
(织入):将Aspect
和其他对象连接起来, 并创建Advice
d object 的过程
Piontcut切点
execution表达式(满足某一匹配模式的的所有目标类方法连接点)
execution(<修饰符>? <返回类型> <类路径>?<方法名>(<参数列表>) <异常模式>? )
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern:方法的可见性,如public,protected;
- ret-type-pattern:方法的返回值类型,如int,void等;
- declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
- name-pattern:方法名类型,如buisinessService();
- param-pattern:方法的参数类型,如java.lang.String;
- throws-pattern:方法抛出的异常类型,如java.lang.Exception;
@annotation (标注了特定注解的目标方法连接点上)
例:
@Around("@annotation(com.cocosa.hrssc.tenant.api.Annotation.NoRepeated)")
Advice(增强)
处理时机(参数为Pointcut)
-
@Before:前置增强:目标方法执行之前先调用增强方法
-
@After:后置增强:目标方法执行之后调用增强方法
-
@AfterReturning:返回增强:目标方法执行return之后返回结果之前调用增强方法,如果出异常则不执行
-
@AfterThrowing:异常增强:目标方法执行产生异常调用增强方法
-
@Around:环绕增强,包含前面四种增强
处理内容
处理内容为具体方法,参数为
JoinPoint
或ProceedingJoinPoint
。Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。
Example(重复请求)
自定义注解
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface NoRepeated {
String key() default "";
//过期时间,使用本地缓存可以忽略,如果使用redis做缓存就需要
int expire() default 5;
}
切面
@Order(1)//切面执行顺序,数字越小优先级越高,默认为字典顺序
@Aspect
@Component
public class NoRepeatedMethodInterceptor {
/**
* 本地缓存 expire: 20s
*/
private static final Cache<String, Object> CACHE = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(20, TimeUnit.SECONDS).build();
//环绕增强 NoRepeated注解 && 以Controller结尾的类所有方法
@Around("@annotation(com.cocosa.hrssc.tenant.api.Annotation.NoRepeated) && execution(* *..*Controller.*(..)))")
public Object interceptor(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
NoRepeated noRepeated = method.getAnnotation(NoRepeated.class);
String key = getKey(noRepeated.key(), joinPoint.getArgs());
if (!StringUtils.isEmpty(key)) {
if (CACHE.getIfPresent(key) != null) {
//自定义全局异常处理
throw new BusinessException("请勿重复请求!");
}
CACHE.put(key, key);
}
try {
//执行目标对象方法
return joinPoint.proceed();
} catch (Throwable throwable) {
//自定义全局异常处理
throw new BusinessException("服务器异常");
} finally {
}
}
private String getKey(String keyExpress, Object[] args) {
for (int i = 0; i < args.length; i++) {
keyExpress = keyExpress.replace("arg[" + i + "]", MD5.create().digestHex(JSON.toJSONString(args[i])));
}
return keyExpress;
}
}
注解补充
@Target 注解
指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
ElementType的取值包含以下几种:
- TYPE:类,接口或者枚举
- FIELD:域,包含枚举常量
- METHOD:方法
- PARAMETER:参数
- CONSTRUCTOR:构造方法
- LOCAL_VARIABLE:局部变量
- ANNOTATION_TYPE:注解类型
- PACKAGE:包
@Retention 注解
指明修饰的注解的生存周期,即会保留到哪个阶段。
RetentionPolicy的取值包含以下三种:
- SOURCE:源码级别保留,编译后即丢弃。
- CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
- RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
@Documented 注解
指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
@Inherited注解
允许子类继承父类中的注解。
ME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
@Documented 注解
指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
@Inherited注解
允许子类继承父类中的注解。