AOP
aop-springaop-aspectj
aop
基于切面的编程思想(aspect); 实现基于动态代理;
概念: Advice、Advisor、Pointcut、Aspect、Joinpoint
实现方式:
- Spring 1.2 基于接口的配置
- Spring 2.0 schema-based-xml
- Spring 2.0 @AspectJ
jdk
- 使用了注入(DI)的方式, 目标类必须实现某接口,getInterfaces()不能为null
- 代理类与目标类的关系是依赖(has)关系
- InvocationHandler,Proxy.newProxyInstance
cglib
- 基于
asm原理,基于对class字节码修改,生成代理子类 - 代理类与目标类的关系是
继承关系 - 方法必可继承,不能private, final
- MethodInterceptor,Enhancer.create()
一般默认使用jdk,基于接口编程,使用多态; 便于业务解耦
spring-aop
- Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这也是为什么我们不需要显式引入这两个依赖
- Spring AOP 只能作用于 Spring 容器中的 Bean,它是使用纯粹的 Java 代码实现的,只能作用于 bean 的方法。
aspectj
-
AspectJ 能干很多 Spring AOP 干不了的事情,它是
AOP 编程的完全解决方案 -
属于
静态织入,它是通过修改代码来实现的,它的织入时机(@Retention注解)可以是Compile-time weaving(Source源码植入):编译期织入,如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 BPost-compile weaving(class植入):也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。Load-time weaving(Runtime):指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法。1、自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了。2、在 JVM 启动的时候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar。
- 因为 AspectJ 在实际代码运行前完成了织入,所以大家会说它生成的类是没有额外运行时开销的
- Spring AOP 是基于代理实现的,在容器启动的时候需要生成代理实例,在方法调用上也会
增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 那么好.
aspectj
annotation
- @Target, 注解使用的位置
- ElemenetType.CONSTRUCTOR 构造器声明
- ElemenetType.FIELD 域声明(包括 enum 实例
- ElemenetType.LOCAL_VARIABLE 局部变量声明
- ElemenetType.METHOD 方法声明
- ElemenetType.PACKAGE 包声明
- ElemenetType.PARAMETER 参数声明
- ElemenetType.TYPE 类,接口(包括注解类型)或enum声名
- @Retention 注解的生命周期
- RetentionPolicy.SOURCE 源码周期,注解将被编译器丢弃
- RetentionPolicy.CLASS 字节码周期,注解在class文件中可用,但会被VM丢弃
- RetentionPolicy.RUNTIME 运行周期,VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
- @Documented 可以被javadoc文档化
- @Inherited 可以继承使用
aspectj
- @Aspect 指定一个类为切面类
- @Pointcut("execution(* cn.itcast.e_aop_anno..(..))") 指定切入点表达式
- @Before("pointCut_()") 前置通知: 目标方法之前执行
- @After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
- @AfterReturning("pointCut_()") 返回后通知,执行方法结束前执行(异常不执行)
- @AfterThrowing("pointCut_()") 异常通知,出现异常时候执行
- @Around("pointCut_()") 环绕通知,环绕目标方法执行
// true, 基于cglib实现类代理
// false, 基于JDK实现接口代理,默认
<aop:aspectj-autoproxy proxy-target-class="false"/>
execution
- execution([修饰符] <返回:><类路径:package.><方法名:><参数:(..)>[异常]) 例如: execution( com.sample.service..(..)) 等价于 execution(* com.sample.service..*(..))
- 修饰符默认 public,能够被代理
- 返回: * 表示任意返回
- 方法: com.sample.service.impl.. 包及子包下所有类的所有方法
- (..) 任意参数
- && 、|| 、! 符合表达式
- @within
- @target
- @args
- @annotation
ProceedingJoinPoint
//错误,拿到的是接口的方法,不是实现类的方法
Signature s = pjp.getSignature();
nature ms = (MethodSignature)s;
Method m = ms.getMethod();
//正确
Signature sig = pjp.getSignature();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
msig = (MethodSignature) sig;
Object target = pjp.getTarget();
Method currentMethod = target.getClass().getMethod(msig.getName(),msig.getParameterTypes());
JoinPoint
- 都用于获取切点的数据
- ProceedingJoinPoint 适合@Around
- 必须放置在第一个参数上:JoinPoint和ProceedingJoinPoint
- 如果是 @Around,我们通常会使用其子类 ProceedingJoinPoint,因为它有 procceed()/procceed(args[]) 方法
- JoinPoint主要用来获取参数信息