目录
目标
介绍springcore中的AOP,阐述通过源码了解的原理。
AOP
简介
AOP是对OOP的补充,从切面的角度考虑编程结构。OOP的单元是class,AOP的单元是aspect,aspect切面便于针对关注点进行模块化。
文档链接:https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#aop
相关概念
-
join point
程序执行中的一个关注点,如方法的执行或异常的控制。SpringAOP,指的是方法的执行。
-
aspect
对关注点的模块化,模块中包括关注点描述和对应的行为。例如事务管理。
SpringAOP,通过xml配置或@Aspect来实现。
-
advice
在确切的点,由aspect执行的具体行为。advice的类型包括around before after。许多AOP框架,包括SpringAOP,将advice视为interceptor拦截器,并且对join point维持一个拦截器链。
- type
- before advice
- 在join point之前执行的advice,无法控制执行流程进入joinpoint
-
@Before("pointcut()") public void doBefore(JoinPoint joinPoint) throws Exception { // do something }
- after returning advice
- 在join point正常返回之后执行的advice
-
@AfterReturning(pointcut = "pointcut()") public void doAfterReturning(JoinPoint joinPoint) { // do something }
- after throwing advice
- 在join point抛出异常后执行的advice
- after finally advice
- 忽略join point的返回方式(不管正常返回还是抛出异常),在join point退出后执行advice
- around advice
- 在join point之前和之后都可以执行的advice,威力最大的advice。它负责决定执行流程是否进入join point,是否通过返回自定义数据或抛出自定义异常的方式截断joinpoint的执行
-
@Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { // do something // 可以直接返回,中断调用 return joinPoint.proceed(); }
type选择建议:选择最适合需求的advice,这样让代码简洁,而且避免错误。
-
pointcut
一个描述,用于匹配感兴趣的join point。advice必须与pointcut描述关联,并且在由pointcut匹配到的joinpoint处执行,例如具体名称的方法执行。
-
introduction
允许在类型的代表之上,引入额外的方法或域。SpringAOP允许在advisedobject上引入任何接口。
-
target object
被一个或多个aspect advised的对象。因为SpringAOP使用了运行时代理,所以target指的是被代理的对象。
-
AOP proxy
为了实现aspect的约定,由AOP框架创建的对象。SpringAOP,指的是JDK动态代理或CGLIB代理。
SpringAOP只能将method作为joinpoint;如果需要异常和field作为joinpoint,需要选择AspectJ。
主要类
-
DefaultAopProxyFactory
默认aop代理工厂,创建Cglib代理或jdk动态代理
class DefaultAopProxyFactory implements AopProxyFactory
AopProxy createAopProxy(AdvisedSupport config){}
当AdvisedSupport中optimize=true或proxyTargetClass=true或没有设置proxy interfaces,创建CglibProxy,否则Jdk动态代理
-
CglibAopProxy
- 生成代理类实例的方法;
- 定义了一些拦截器类(如动态AOP拦截器)
class CglibAopProxy implements AopProxy
CglibAopProxy.DynamicAdvisedInterceptor 动态AOP拦截器
- 获取方法级别关注点的所有切面行为
- 生成CglibMethodInvocation并执行process
CglibAopProxy.CglibMethodInvocation extends ReflectiveMethodInvocation
-
DefaultAdvisorChainFactory
org.springframework.aop.framework.DefaultAdvisorChainFactory
getInterceptorsAndDynamicInterceptionAdvice,获取关注点的所有切面行为
-
ReflectiveMethodInvocation
class ReflectiveMethodInvocation implements ProxyMethodInvocation
Object proceed(),依次执行切面行为,最后执行关注点
-
JoinPoint
org.aspectj.lang.JoinPoint
用于自定义advice方法的参数,代表当前关注点信息
-
ProceedingJoinPoint
org.aspectj.lang.ProceedingJoinPoint
用于自定义around advice方法的参数,代表当前关注点信息
-
MethodInvocationProceedingJoinPoint
spring中joinpoint proceedingJoinPoint的实现类,真实持有当前关注点信息。
使用方式
使用AspectJ
-
开启Spring支持
需要org.aspectJ的jar
现在已经不使用xml文件了。
spring xml添加
<aop:aspectj-autoproxy/>
可能还需要
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
-
@aspect声明类作为面
-
@pointcut声明方法为pointcut
3种类型
- kind,如execution(规定接口与方法)
@Pointcut("execution(* com.xxxxx.mapper.DealMapper.save*(..)) || execution(* com.xxxx.mapper.DealMapper.update*(..))")
- scope,如within(规定包)
- context,如this,target(规定proxy traget 参数等类型)
最佳实践
- scope+kind
-
声明方法为advice
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
必须明确pointcut,可进行参数传递
使用autoconfig + StaticMethodMatcherPointcutAdvisor
使用起来也是很简单,一些依赖AOP实现的中间件会使用到该方式。
public class MyAdvisor extends StaticMethodMatcherPointcutAdvisor {
public MyAdvisor() {
// 添加切面行为
setAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("MyAdvisor deal");
return null;
}
});
}
/**
* 匹配类或方法是否满足join point要求
* @param method
* @param aClass
* @return
*/
@Override
public boolean matches(Method method, Class<?> aClass) {
boolean match = method.getName().equals("check");
return match;
}
}
@Configuration
public class MyAdvisorConfiguration {
@Bean
public MyAdvisor myAdvisor(){
return new MyAdvisor();
}
}
如何确定advice的执行顺序
依据aspect的order决定,order越大则优先执行。
多个aspect时,则按照order决定顺序。一个aspect中的多个advice,无法判断顺序,建议分拆到多个aspect中。
事务
详见后续章节