Spring AOP小记
一、Spring AOP的使用
1.基于纯注解的 AOP
需要用到的几个注解如下:@EnableAspectJAutoProxy 开启全局AOP配置
@Aspect 告诉Spring 哪个类是切面类
通知方法:
@Before : 前置通知 具体用法 @before(value = "execution(public int com.itguigu.bean.person.doI(..))")
@After : 后置通知 @After(value = "execution(* com.itguigu.bean.person.*(..))")
@AfterReturning :返回通知
@AfterThrowing :异常通知
@Around : 环绕通知
它们的执行顺序为 环绕前置通知 -> 前置通知 -> 执行目标方法 -> 环绕后置返回/环绕后置异常 -> 后置环绕通知 -> 后置通知 -> 返回通知/异常通知
@PointCut : 用来抽取某个被增强的切入点,具体用法如下:
```java
@Pointcut(value = "anyMethod()")
private void anyMethod(){}//定义一个切入点
@AfterReturning(value = "anyMethod()" ,returning = "res")
public void doAfter(JoinPoint joinPoint,Object res)
@Before(value = "execution(* com.itguigu.bean.PersonImpl.*(..))")
public void doAccessCheck(JoinPoint joinPoint)
@AfterThrowing(value = "anyMethod()",throwing = "e")
JoinPoint 是一个切点类,可以用来获取目标方法的签名,方法名,方法参数,使用AOP也能获取目标方法执行的结果或异常信息。
具体的注解配置方法 (引入正确的依赖): 写一个被增强类:public class Calcultor {
public int div(int i,int j){
return i/j;
}
}
写一个切面类:记得加上@Aspect( )
@Aspect
public class MyInterceptor {
@Pointcut("execution(* com.itguigu.bean.PersonImpl.*(..))")
private void anyMethod(){}//定义一个切入点
@Before(value = "execution(* com.itguigu.bean.PersonImpl.*(..))")
public void doAccessCheck(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println("前置通知:"+joinPoint.getSignature().getName()+"方法即将开始执行,参数为:" + Arrays.asList(args));
}
@AfterReturning(value = "anyMethod()" ,returning = "res")
public void doAfter(JoinPoint joinPoint,Object res){
System.out.println("返回通知"+joinPoint.getSignature().getName()+"方法结束,"+"返回的结果为"+res);
}
@After("anyMethod()")
public void after(JoinPoint joinPoint){
System.out.println("后置通知:"+joinPoint.getSignature().getName());
}
@AfterThrowing(value = "anyMethod()",throwing = "e")
public void doAfterThrow(Exception e){
System.out.println("异常通知:"+e);
}
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出环绕方法");
return object;
}
全注解配置,所以需要一个配置类,加上 @Configuration + @EnableAspectJAutoProxy
@EnableAspectJAutoProxy代表启用AspectJ 框架的自动代理,这个时候Spring 才会自动生成代理对象,进而使用AOP
把相关的类注册到容器中 用到注解@Bean
@Configuration
@EnableAspectJAutoProxy
public class MyConfigure {
@Bean
public Calcultor calcultor(){
return new Calcultor();
}
@Bean
public MyAspect myAspect(){
return new MyAspect();
}
@Bean
public Man man(){
return new Man();
}
}
最后:getBean() 底层是实现动态代理,JDK代理或者cglib 代理的区别
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfigure.class);
Calcultor bean = context.getBean(Calcultor.class);
int i = bean.div(1,1);
System.out.println(i);
2.使用xml文件
将 Bean 注册都容器中,并且配置AOP ,配置AOP时要引入xml的AOP约束
<bean id="personImpl" class="com.itguigu.bean.PersonImpl"/>
<bean id="myInterceptor" class="com.itguigu.LogAspect.MyInterceptor" />
<aop:config>
<aop:aspect ref="myInterceptor">
<aop:before method="doAccessCheck" pointcut="execution(*com.itguigu.bean.PersonImpl.div(..))"/>
<aop:after method="after" pointcut="execution(* com.itguigu.bean.PersonImpl.div(..))"/>
</aop:aspect>
</aop:config>
3.使用xml文件和注解相结合
在Bean上 加入 @Component 注解 ,使得能够被扫描如容器中,其次在切面类加入注解@Aspect 来告诉Spring 哪个类是切面类
将组件扫描如容器中
<context:component-scan base-package="com.itguigu" />
开启AOP,作用形同于 @EnableAspectJAutoProxy
<aop:aspectj-autoproxy />
代码如下(示例):
二、源码分析
1.@EnableAspectJAutoProxy
@EnableAspectJAutoProxy >> @Import({AspectJAutoProxyRegistrar.class})
给容器中引入了AnnotationAwareAspectJAutoProxyCreator 类,AspectJAwareAdvisorAutoProxyCreator这个类 实现了两个接口:
BeanPostProcessor 和 BeanFactoryAware
BeanPostProcessor可以用来拦截容器Bean 初始化前后和实例化前后:
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
2.在初始化之前会调用函数isInfrasturep 判断Bean是否与切面有关,有关就放入AdviceBean集合;
在Bean实例化后,
1)找到被拦截的Bean的增强器(通知方法),以及切入点
2)将增强器保存到proxyFactory中
3)看被拦截的Bean是否实现了接口,决定使用jdk动态代理还是 cglib代理(关键在于被代理类是否实现了接口)
4)返回被拦截的Bean的代理对象,注册到容器
3.动态代理反射执行目标方法的过程:
1)保存所有的增强器,转化为一个拦截器链
// 执行器链是一个list集合,传入了被代理对象及目标方法
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
首先,缓存Map(methodCache)中找是否有已经创建了目标方法的执行器(增强器?拦截器),有就直接获取
如果没有,就调用方法 advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass); 创建执行器链
具体的创建流程:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass)
先判断传入容器的切面类的增强器 **advisor**(Before、After等) 是否实现了pointcut接口,如果实现了,就把这些增强器转而pointcut类型
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
然后在调用 getInterceptors(advisor) 方法,看这些增强器是否实现了MethodInterceptor接口,如果实现了,就直接把增强器添加到list中(执行器链),否则就把它们转为拦截器,再添加到list中(执行器链).
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
最后就能返回一个list集合,集合的长度为增强器数目加1,1为一个默认的增强器,不起作用,最后把list放到缓存中。
2)如果没有拦截器链,直接执行目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
3)有拦截器链,就将目标方法,拦截器链等信息传入并创建CglibMethodInvocation对象,调用 Proceed() 方法依次执行拦截器链,获取返回值。
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
在proceed() 方法内定义了一个常量 this.currentInterceptorIndex = -1
//如果执行链的长度为0,就直接执行 invokeJoinpoint(),反射调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
如果执行器链长度不为0,this.currentInterceptorIndex 自增
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
然后依次递归调用执行器链,形成责任链
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
==========================================
单步调试AOP ,依次得到的方法调用流程:
//默认的执行器
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
//异常通知的执行器
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
//返回通知的执行器
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
//后置通知的执行器
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
//前置通知的执行器
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); // 此处调用前置通知
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
return mi.proceed();//再次递归,此时执行器链长度来到0,this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1 条件成立,执行之前的return invokeJoinpoint()方法,反射调用目标方法
调用完目标方法后,依次递归退出回到 后置通知 --> 返回通知 --> 异常通知 --> 默认的执行器,至此,AOP 就完成了所有增强方法和目标方法的调用。
三、图示分析
调用图示如下:
以上便是我对AOP执行过程的一点理解,烦请各路大神批评指正 ,也参考了其他博主,特别是(segmengfault 简相杰)的Spring注解版的博文,表示衷心感谢,致礼。