Spring AOP 简介
1.什么是AOP
与OOP对比,AOP是处理一些横切性问题,这些横切性问题不会影响到主逻辑实现的,但是会散落到代 码的各个部分,难以维护。
AOP就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。
2. AOP的应用场景
- 日志记录
- 权限验证
- 效率检查
- 事务管理
问题: Aspectj 和spring aop的区别?
切面织入的方式:
- 编译期织入
- 类装载期织入
- 动态代理织入 spring采用 动态代理的方式,在运行时织入
区别:
织入的时期不同:Spring Aop采用的动态织入,而Aspectj是静态织入。静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。
从使用对象不同:spring的通知 是基于该对象是spring bean才可以;aspectj 可以在任何java对象上使用通知;
3. Spring AOP原理及其应用
3.1 AOP相关概念
Aspect(切面): 通常是一个类(交给Spring容器管理),里面可以定义切入点和通知
JointPoint(连接点): 程序执行过程中的一个点,如方法的执行或异常的处理
Advice(通知): AOP在特定的切入点上执行的增强处理
通知类型:
-
- Before advice
- After returning advice
- After throwing advice
- After (finally) advice
- Around advice
调用顺序: Around advice>Before advice>After (finally) advice>After returning advice/After throwing advice
Pointcut(切入点): 连接点的集合
Target object(目标对象):被通知对象 AOP proxy:AOP框架创建的对象,代理就是目标对象的增强。
-
- JDK dynamic proxy
- CGLIB proxy
思考: 初始化时织入还是获取对象时织入? spring是在初始化的时候织入的, ioc容器初始化bean的时候,在第8次调用 spring的后置处理器的时候执行的;
Weaving(织入):把代理逻辑加入到目标对象上的过程叫做织入.
3.2 AOP的应用
3.2.1 Spring AOP with AspectJ pointcuts
Schema-based AOP Support xml配置对AOP支持的 后置处理器
AspectJAwareAdvisorAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator // 配置 <aop:aspectj-autoproxy/>
spring-aop.xml
<!-- 自动为spring容器中那些配置@AspectJ切面的bean创建代理,织入切面 proxy-target-class默认为false,表示使用jdk动态代理织入增强, 当配为true时,表示使用Cglib动态代理技术织入增强-->
<aop:aspectj-autoproxy proxy-target-class="true"/> <aop:config> <!-- 配置切面--> <aop:aspect id="myAspect" ref="xmlAspect">
<!-- 配置切入点-->
<aop:pointcut id="businessService"
expression="execution(* bat.ke.qq.com.dao.*.*(..))"/>
<!-- 通知-->
<aop:before pointcut-ref="businessService" method="before"/>
<aop:after pointcut-ref="businessService" method="after"/>
</aop:aspect> </aop:config> <bean id="xmlAspect" class="bat.ke.qq.com.config.XmlAspect"/> <bean id="foxDao" class="bat.ke.qq.com.dao.FoxDao"/>
public class XmlAspect { public void before(JoinPoint point) {
System.out.println("before");
} public void after() {
System.out.println("after");
}
}
基于注解的方式:@AspectJ support
注解配置对AOP支持的 后置处理器: AnnotationAwareAspectJAutoProxyCreator
@Aspect
@Component
@Aspect
@Order(1) //如果有多个切面,可以通过该注解 指定加载顺序,值越小越先加载
public class AspectConfig { @Pointcut("execution(* bat.ke.qq.com.dao.*.*(..))") 常用的是execution
//@Pointcut("within(bat.ke.qq.com.dao.*)")
//@Pointcut("args(String)")
//@Pointcut("this(bat.ke.qq.com.dao.FoxDao)") // jdk动态代理 extend Proxy implements IFoxDao
//@Pointcut("target(bat.ke.qq.com.dao.FoxDao)")
//@Pointcut("args(String ...) || args()")
//@Pointcut("execution(* bat.ke.qq.com.dao.*.*(String))")
//@Pointcut("@annotation(bat.ke.qq.com.anno.Yuanma)")
//@Pointcut("@target(bat.ke.qq.com.anno.Dao)") // 目标是注解配置
//@Pointcut("@within(bat.ke.qq.com.anno.Dao)")
//@Pointcut("@args(bat.ke.qq.com.anno.Dao)") //传参类型配置@Dao的类型
@Pointcut("bean(foxDao)")
private void pointcut() {}
//前置通知 @Before("pointcut()")
public void before(JoinPoint point) {
point.getThis();
System.out.println("before");
}
//后置通知 @After("pointcut()")
public void after() {
System.out.println("after");
}
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("afterReturning");
}
@AfterThrowing("pointcut()")
public void afterThrwoing() {
System.out.println("afterThrwoing");
}
//环绕通知 @Around("pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("around");
Object[] args = point.getArgs();
for(int i=0;i<args.length;i++){
if(args[i].getClass().equals(String.class)){
args[i] += "xxxx";
}
}
return point.proceed(args);
}
3.2.2 pointcut的配置分析
execution : 属于方法级别的配置
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?namepattern(param-pattern) throws-pattern?) modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等,必须配置;
declaring-type-pattern:方法所在类的全路径名,如bat.ke.qq.com.dao.FoxDao;
name-pattern:方法名类型,如userService(),必须配置;
param-pattern:方法的参数类型,如java.lang.String,必须配置;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
@Pointcut("execution(* bat.ke.qq.com.dao.*.*(..))")
within 表达式的最小粒度 为 类;
@Pointcut("within(bat.ke.qq.com.dao.*)")
this 代理对象 :使用jdk动态代理时 表达式中配置的为 接口;使用cglib动态代理时,表达式中配置的是类
// jdk动态代理 基于接口 extend Proxy implements IFoxDao 只支持接口和Proxy
// cglib 基于继承,支持接口和目标类
@Pointcut("this(bat.ke.qq.com.dao.FoxDao)")
target 目标对象
@Pointcut("target(bat.ke.qq.com.dao.FoxDao)")
args :args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关
@Pointcut("args(String)") @Pointcut("args(String ...) || args()")
@target 目标对象有配置@Dao注解
@Pointcut("@target(bat.ke.qq.com.anno.Dao)")
@args 传参类型配置@Dao的类型
@Pointcut("@args(bat.ke.qq.com.anno.Dao)")
@within
@annotation 作用方法级别,配置@Yuanma 自定义注解;只用对使用该注解的方法生效; @Transactional注解就是基于该方式
@Pointcut("@annotation(bat.ke.qq.com.anno.Yuanma)")
bean 指定的bean
@Pointcut("bean(foxDao)") @Pointcut("bean(*Service)")
注意: 上述所有的表达式可以混合使用,|| && !