文章目录

  • 1. Spring AOP 与 AspectJ 的关系
  • 2. JDK和Cglib动态代理的区别
  • 3. Spring AOP应用案例
  • 4. Spring AOP有几种配置方式?
  • 5. Spring AOP源码解析
    • 5.1 自动配置类 AopAutoConfiguration 开启对Aop的支持
    • 5.2 解析切面类,封装成Advisor
    • 5.3 匹配并创建动态代理
    • 5.4 调用代理类增强逻辑
  • 6. 五种通知执行顺序

1. Spring AOP 与 AspectJ 的关系

Spring AOP 要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者增强处理。Aop依赖于IOC,Aop可以看做是调用IOC的后置处理器来实现的。 默认地,如果使用接口的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这也是为什么我们不需要显式的引入这两个依赖。

作为 Java 开发者,我们都很熟悉 AspectJ 这个词,甚至于我们提到 AOP 的时候,想到的往往就是 AspectJ,但真实情况是Spring虽然提供了AspectJ的支持,但只用到的AspectJ的切点解析和匹配。比如 @Aspect、@Pointcut、@Before、@After 、@Around 等注解都是来自于 AspectJ,利用AspectJ的解析execution、@Annotation等表达式的能力去解析,因为AspectJ也是一个优秀的框架,Spring为了不重复造轮子嘛,就利用到了这些。但是动态代理功能的实现是纯 Spring AOP 自己实现的。AspectJ 能干很多 Spring AOP 干不了的事情,它是 AOP 编程的完全解决方案。Spring AOP 致力于解决的是企业级开发中最普遍的 AOP 需求(方法织入),而不是力求成为一个像 AspectJ 一样的 AOP 编程完全解决方案。

在性能方面,由于Spring AOP 是基于代理实现的,在容器启动的时候需要生成代理实例,在方法调用上也会增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 那么好。
        
Spring AOP术语解释

  • 切面(Aspect):横切业务代码,带有@Aspect注解的类,被称为切面类,用于存放不同的切点、通知方式(@Around)和切点逻辑等。
  • 连接点(Join point):在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。
  • 通知(Advice): 前置通知@Before、后置@After、环绕@Around等等多种通知类型,不同的通知类型决定在不同的地方执行增强代码。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
  • 顾问(advisor):是Advice的一种包装,是Pointcut和Advice的一种结合!
  • 切点(Pointcut):类似于连接点,表示从哪个位置切入,一般与通知关联使用。
  • 织入(Weaving): 把通知逻辑切入连接点的过程
  • 引入(Introduction): 把其他接口和实现 动态的引入到目标类的过程

问题:为什么spring 不使用AspectJ全套的东西呢?而是只使用了部分呢?

猜测原因如下:

  • ①:AspectJ大部分内容是动态植入,因为AspectJ编译后的文件是.aj 结尾的,JVM编译后的是.class,如果spring使用AspectJ的动态植入,那么就要使用AspectJ的编译器,JVM是肯定编译不了的,无疑增加了开发成本!所以spring自己实现了一套代码实现植入增强!
  • ②:Spring在引入别的框架时,理念是取其精华、弃其糟粕。取Aspect对自己有用的理念和切点解析部分,舍弃掉了会增加开发成本的部分!

2. JDK和Cglib动态代理的区别

相同点

  • JDK动态代理和Cglib动态代理在 jdk1.7版本后都使用修改字节码的方式来进行代理。

不同点

  • ①:如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
  • ②:JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
  • ③:JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()
  • ④:如果目标对象没有实现接口,必须采用CGLIB,主要方式是对指定的类生成一个子类,覆盖其中的方法。Spring会自动在JDK动态代理和CGLIB之间转换。
  • ⑤:Cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类。
  • ⑥:每一次jdk版本升级,jdk代理效率都得到提升,1.8版本已经略高于CGLIB代理
  • ⑦:Cglib会重复调用动态代理类,而JDK不会!!
代理类型 JDK Cglib
使用场景 目标类实现了接口,且未指定ProxyTargetClass = true 目标类未实现接口
代理类的字节码文件数量 根据接口生成1个$proxy.class文件 根据具体类生成多个cglib.class文件
调用 原始方法 的方式 反射 直接调用(正因为直接调用速度快,所以cglib在调用时比jdk快)
在被增强的方法中调用其他方法时 其他方法不会被增强,动态代理类只调用一次 其他方法会被增强,因为每一个方法都会调用动态代理类!
代码形式 InvocationHandler.invoke MethodInterceptor.intercept

3. Spring AOP应用案例

@Aspect //切面
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {protected Logger logger = LoggerFactory.getLogger(getClass());//切入点,寻找带有@DataSource注解的方法@Pointcut("@annotation(com.chinalife.policy_manage.common.datasource.annotation.DataSource) " +"|| @within(com.chinalife.policy_manage.common.datasource.annotation.DataSource)")public void dataSourcePointCut() {}//环绕通知   ProceedingJoinPoint 连接点/切入点@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();//获取目标类Class targetClass = point.getTarget().getClass();//获取目标方法Method method = signature.getMethod();//获取目标类上的@DataSource注解DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);//获取目标方法上的@DataSource注解DataSource methodDataSource = method.getAnnotation(DataSource.class);//如果@DataSource注解不为空,执行增强逻辑if(targetDataSource != null || methodDataSource != null){String value;if(methodDataSource != null){value = methodDataSource.value();}else {value = targetDataSource.value();}DynamicContextHolder.push(value);logger.debug("set datasource is {}", value);}try {return point.proceed();} finally {DynamicContextHolder.poll();logger.debug("clean datasource");}}
}

4. Spring AOP有几种配置方式?

  • ①: Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的(如:MethodInterceptor、MethodBeforeAdvice)。

    • 实现MethodBeforeAdvice:≈ 前置通知

      public class LogAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {String methodName = method.getName();System.out.println("执行目标方法【" + methodName + "】的<前置通知>,入参" + Arrays.asList(args));}
      }
      
    • 实现MethodInterceptor ≈ 环绕通知
      public class LogInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println(getClass()+"调用方法前");Object ret=invocation.proceed();System.out.println(getClass()+"调用方法后");return ret;}
      }
      

      然后把他们注册进容器中!即可实现增强逻辑,运行结果如下:

             此中方法有个致命的问题,如果我们只能指定单一的Bean的AOP, 如果多个Bean需要创建多个ProxyFactoryBean 。而且,我们看到,我们的拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截。
              后来有了升级版,通过配置 Advisor(内部封装了Advice通知),精确定位到需要被拦截的方法,然后使用内部的 Advice 执行逻辑处理。

       @Bean public NameMatchMethodPointcutAdvisor tulingLogAspect() { NameMatchMethodPointcutAdvisor advisor=new NameMatchMethodPointcutAdvisor(); // 通知(Advice) :是我们的通知类 // 通知者(Advisor):是经过包装后的细粒度控制方式。 advisor.setAdvice(tulingLogAdvice()); advisor.setMappedNames("div"); return advisor; }
      
  • ②: Spring 2.0 XML 配置:Spring 2.0 以后使用 XML 的方式来配置,使用命名空间 ,主要是针对xml形式来配置!
  • ③:Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。

5. Spring AOP源码解析

Aop源码大概分为以下几步:

  1. spring boot 自动配置AopAutoConfiguration类中带有@EnableAspectJAutoProxy,项目启动即开启对spring AOP的支持,该注解注册了AnnotationAwareAspectJAutoProxyCreator类,该类实现了bean的后置处理器,可以在类创建过程中做一些其他操作
  2. 在bean后置处理器的postProcessBeforeInstantiation方法中,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中!
  3. 在创建每一个bean时,在bean的后置处理器中的postProcessAfterInitialization方法中,拿到缓存中所有的Advisor,根据切入点PointCut与当前bean做匹配,匹配成功与否决定是否需要创建动态代理!如果匹配到了,则根据实际情况创建动态代理
  4. 调用目标方法时,会调用经过动态代理增强的方法逻辑 !

5.1 自动配置类 AopAutoConfiguration 开启对Aop的支持

在spring boot项目中,项目启动时会自动加载许多自动配置类,以完成项目结构!其中就有AopAutoConfiguration,该类的作用就是为项目提供Aop的支持,一种是jdk动态代理,一种是cglib动态代理

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
//自动配置类
public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class)static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)//开启自动代理:@EnableAspectJAutoProxy@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",matchIfMissing = false)//jdk动态代理static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)//Cglib动态代理static class CglibAutoProxyConfiguration {}}

可以看到自动配置类AopAutoConfiguration除了帮我们配置了jdk动态代理和cglib动态代理,还有一个注解@EnableAspectJAutoProxy,这个注解内部通过@Import导入了一个bean定义的注册器AspectJAutoProxyRegistrar

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) //通过`@Import`导入了一个bean定义的注册器
public @interface EnableAspectJAutoProxy {

这个注册器帮我们注册了一个Aop中非常重要的类AnnotationAwareAspectJAutoProxyCreator!该类实现了bean的后置处理器BeanPostProcessor,可以在类创建前后做一些操作,具体如下:

  • postProcessBeforeInstantiation方法中,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中!
  • postProcessAfterInitialization方法中,匹配切入点,创建动态代理
 @Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {// 注册了一个Aop中非常重要的bean的后置处理器`AnnotationAwareAspectJAutoProxyCreator`!return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}

看一下AnnotationAwareAspectJAutoProxyCreator的继承关系:

5.2 解析切面类,封装成Advisor

①: 通过bean的后置处理器解析切面类,把通知封装成Advisor,并放入advisorsCache缓存中!
与spring事务一样,Aop也是通过bean的后置处理器解析带有@AspectJ的bean,这个bean的后置处理器在容器创建的时候就被注册,在解析时可以直接调用!

注意:下图中的AspectJAwareAdvisorAutoProxyCreator正是AnnotationAwareAspectJAutoProxyCreator的父类

Spring AOP发生在创建bean的时候,也就是finishBeanFactoryInitialization()内部的creatBean()方法中

try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {//创建bean的方法return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}

creatBean()方法内部,resolveBeforeInstantiation方法会扫描@Aspect注解,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中!

 try {/*** 第一次调用bean的后置处理器,事务在这里不会被调用,aop的才会被调用* 为啥aop在这里调用了?因为在此处需要解析出对应的切面保存到缓存中*/Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}

进入resolveBeforeInstantiation方法:

 @Nullableprotected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// 如果有bean后置处理器: InstantiationAwareBeanPostProcessorsif (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {//调用 postProcessBeforeInstantiation 方法bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {//调用 postProcessAfterInitialization 方法bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}

在实例化bean之前,会第一次调用bean的后置处理器,解析到所有的@AspectJ的类,保存到缓存中。那怎么解析的呢?进入上文resolveBeforeInstantiation方法中的applyBeanPostProcessorsBeforeInstantiation方法中!

 @Nullableprotected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {/*** 获取容器中的所有后置处理器*/for (BeanPostProcessor bp : getBeanPostProcessors()) {//判断后置处理器是不是InstantiationAwareBeanPostProcessorif (bp instanceof InstantiationAwareBeanPostProcessor) {//把我们的BeanPostProcessor强制转为InstantiationAwareBeanPostProcessorInstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;/*** 【很重要】* 我们AOP @EnableAspectJAutoProxy 为我们容器中导入了 AnnotationAwareAspectJAutoProxyCreator* 我们事务注解@EnableTransactionManagement 为我们的容器导入了 InfrastructureAdvisorAutoProxyCreator* 都是实现了我们的 BeanPostProcessor接口,InstantiationAwareBeanPostProcessor,* 进行后置处理解析切面*/Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;}
 @Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {//构建我们的缓存keyObject cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {//已经被解析过 直接返回if (this.advisedBeans.containsKey(cacheKey)) {return null;}/*** 判断是不是基础的bean* 判断是不是应该跳过 (aop解析直接解析出我们的切面信息(并且把我们的切面信息进行保存),而事务在这里是不会解析的)*/if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}

由于AOP使用的是AnnotationAwareAspectJAutoProxyCreator类,所以选择这个类中的shouldSkip方法,在所有的bean定义中选择是否跳过,如果带有@AspectJ注解,就不跳过,把这个类中的带有@Before、@After、@Around等注解的方法封装成一个个Advisor(顾问),它是Pointcut和Advice的一种结合! 并添加进集合中

 @Overrideprotected List<Advisor> findCandidateAdvisors() {//找出事务相关的advisorList<Advisor> advisors = super.findCandidateAdvisors();//找出Aspect相关的信息之后封装为一个advisorif (this.aspectJAdvisorsBuilder != null) {advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}//返回我们所有的通知return advisors;}

buildAspectJAdvisors方法内部会把带有下面的注解的挨个解析成Advisor

 //获取到切面类中的所有方法,但是该方法不会解析标注了@PointCut注解的方法for (Method method : getAdvisorMethods(aspectClass)) {//挨个去解析我们切面中的方法Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}//看是否含有这些注解!private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

把解析到的Advisor放入advisorsCache缓存中

 //加入到缓存中if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);}

原理图:

注意:以上解析切面的操作,是在bean的第一个后置处理器postProcessBeforeInstantiation中完成的!

5.3 匹配并创建动态代理

② 根据 Advisor 中的 PointCut 决定当前bean是否创建动态代理

我们都知道创建动态代理的时机是在初始化之后(如果存在循环依赖则在实例化之后!),所以在源码中创建动态代理在doCreateBean方法中的initializeBean方法中,这个方法内部调用了bean的后置处理器postProcessAfterInitialization,在后置处理器中完成了判断和动态代理的创建

 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {//1.回调各种 Aware 接口invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {//2.调用 BeanPostProcessorsBeforeInitialization 扩展wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {//3.调用实现InitializingBean的afterPropertiesSet方法// 调用xml方式的 bean标签里配置init-mothod属性invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {//4.调用 BeanPostProcessorsAfterInitialization 扩展,动态代理在这里!wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}***********************************************************************************************
==================== applyBeanPostProcessorsAfterInitialization 内部创建动代理 ==================
***********************************************************************************************public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {/** 【很重要】* 我们AOP @EnableAspectJAutoProxy 为我们容器中导入了 AnnotationAwareAspectJAutoProxyCreator* 我们事务注解@EnableTransactionManagement 为我们的容器导入了 InfrastructureAdvisorAutoProxyCreator* 都是实现了我们的 BeanPostProcessor接口,InstantiationAwareBeanPostProcessor,* 在这里实现的是BeanPostProcessor接口的postProcessAfterInitialization来生成我们的代理对象 */Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;}

AbstractAutoProxyCreator抽象类扩展了这个后置处理器

 /*** 在该后置方法中 我们的事务和aop的代理对象都是在这生成的* @param bean bean实例* @param beanName bean的名称* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {if (bean != null) {//获取缓存keyObject cacheKey = getCacheKey(bean.getClass(), beanName);//如果循环依赖时已经创建了代理,在这里把他移除掉!!if (this.earlyProxyReferences.remove(cacheKey) != bean) {//找到合适的就会被代理return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

wrapIfNecessary方法如下:主要内容就是拿到所有通知与当前类匹配,如果匹配成功则创建动态代理

 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {//已经被处理过if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}//排除掉不需要增强的,比如Aop一些基础类if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}//是不是基础的bean 是不是需要跳过的if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}//***如果Advisor中的切点表达式命中了这个类,就返回适合本类的通知器Advisor!Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);//我们的合适的通知器不为空if (specificInterceptors != DO_NOT_PROXY) {//表示当前的对象已经代理模式处理过了this.advisedBeans.put(cacheKey, Boolean.TRUE);//创建我们的真正的代理对象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));//加入到缓存this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

其中getAdvicesAndAdvisorsForBean方法中拿到了所有的Advisor与当前bean进行了匹配,返回合适本类的通知器advisor,如果合适的advisor为空,则返回DO_NOT_PROXY,不需要代理,表示不需要代理。

 protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {/*** 找到合适的增强器对象advisor*/List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);//若合适的通知器为空,则返回DO_NOT_PROXY,不需要代理if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();}
 protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//找到ioc容器中候选的通知  (找到Aop扫描到所有通知的Advisor,注意是所有的!!!)List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断我们的通知Advisor能不能作用到当前的类上,返回合适本类的通知器advisorList<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);//加了一个内置的advisorextendAdvisors(eligibleAdvisors);//对我们的advisor进行排序,如果有多个切面类,则根据order排序//排序方式:异常--返回通知--后置通知--前置通知//这样排序的原因是,后边调用目标方法会讲!!if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

findAdvisorsThatCanApply的调用链很深,这里就不再跟进了。

以上代码其实都是匹配阶段的代码,如果匹配成功,则进入上文wrapIfNecessary方法中的createProxy方法中,开始真正创建动态代理对象

创建动态代理是在createProxy方法中由ProxyFactory代理工厂来创建的

 //创建一个代理对象工厂ProxyFactory proxyFactory = new ProxyFactory();。。。//真正的创建代理对象return proxyFactory.getProxy(getProxyClassLoader());

createAopProxy() : 该方法用来创建我们的代理对象

代理形式由ProxyTargetClass和是否实现接口来决定!!

  • ①:我们代理的类没有实现接口,那么会直接走cglib代理
  • ②:我们代理的类实现了接口,且ProxyTargetClass 指定为false才会走jdk动态代理,如果ProxyTargetClass指定的有值,则还是使用cglib代理
 @Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {//判断我们是否前置指定使用cglib代理ProxyTargetClass =true  fasle//判断是否实现了接口//判断是否是Optimize() 可手动设置,一般为false//三个判断只要有一个是true,就会使用cglib动态代理!if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}//所targetClass是接口 使用的就是jdk代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}//cglib代理return new ObjenesisCglibAopProxy(config);}else {//动态代理return new JdkDynamicAopProxy(config);}}

至此,目标类的动态代理创建完成!

5.4 调用代理类增强逻辑

③ 调用目标方法

如果使用的时jdk动态代理,在调用目标方法时会进入JdkDynamicAopProxy中的 invoke 方法,把通知加入责任链,把Advisor转换成Inteceptor,通过责任链的方式递归调用proceed()方法完成对方法的增强调用处理

 @Override@Nullablepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;//获取到我们的目标对象TargetSource targetSource = this.advised.targetSource;Object target = null;try {//若执行代理对象的equals方法不需要代理if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}//若执行的是hashCode方法 不需要代理else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}//若执行的class对象是DecoratingProxy 也不要拦截器执行else if (method.getDeclaringClass() == DecoratingProxy.class) {// There is only getDecoratedClass() declared -> dispatch to proxy config.return AopProxyUtils.ultimateTargetClass(this.advised);}else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;/*** 这个配置很重要很实用【暴露我们的代理对象到线程变量中】需要搭配@EnableAspectJAutoProxy(exposeProxy = true)* 一起使用.* 比如我们的aop中 multi和 mode方法都是被切入的方法,但是在切入的方法中通过* this来调用另外一个方法的时候,那么该方法就不会被代理执行,而是通过方法内部执行*还有的就是事务方法调用事务方法的时候 也需要这样*/if (this.advised.exposeProxy) {//把我们的代理对象暴露到线程变量中oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}//获取我们的目标对象target = targetSource.getTarget();//获取我们目标对象的classClass<?> targetClass = (target != null ? target.getClass() : null);//把我们的aop的advisor 转化为拦截器,相当于责任链的Handler顶级接口List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//加入我们的拦截器链为空if (chain.isEmpty()) {//通过反射直接调用执行Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {//创建一个方法调用对象MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);//调用执行,注意proceed方法是递归调用,//会把需要的通知 通过责任链模式全部调用retVal = invocation.proceed();}Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal = proxy;}else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {AopContext.setCurrentProxy(oldProxy);}}}

注意:如果是jdk动态代理,在invoke方法中,会把代理对象暴露到本地线程变量ThreadLocal中,这样做的目的是:在A方法中调用另一个B方法时,保证两个方法都享受到动态代理的增强! 如果没有暴露出来,那么在调用B方法时,B方法是不会有增强逻辑的!而cglib就不存在这样的问题,因为他每次调用都会重新获取代理对象!

advisor转化为Inteceptor拦截器,用于责任链调用,这个拦截器就相当于责任链中的顶级接口Handler。然后递归调用proceed方法

 @Override@Nullablepublic Object proceed() throws Throwable {//执行到了最后一个拦截器的时候(从-1开始,结束条件执行目标方法是下标=拦截器的长度-1)if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {//如果责任链运行到了最后一个,表示增强代码结束,开始运行我们自己的代码逻辑return invokeJoinpoint();}/*** 获取第一个方法拦截器,按照之前排好序的advisor获取 * 顺序为:(新增的内置拦截器)-- 异常--返回通知--后置通知--前置通知*/Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {return proceed();}}else {//在这个地方需要注意,抵用第一个拦截器的invoke方法,传入的是this 当前的方法拦截器对象return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

问题:那么为什么之前要按照 异常–返回通知–后置通知–前置通知 的方式来排序呢?

答:是因为在递归调用时,递的过程最底层的通知(前置通知)首先被执行,然后才会有归的过程。 所以为了使通知顺序保持 前置通知 – 目标方法 – 异常通知(如果有异常) – 返回通知 – 后置通知 的顺序,就必须按照这样排序!!

6. 五种通知执行顺序

6.1 目标方法无异常时

  • ①:前置通知
  • ②:环绕通知的调用目标方法之前的代码
  • ③:目标方法
  • ④:环绕通知的调用目标方法之后的代码
  • ⑤:返回通知
  • ⑥:后置通知

6.2 在目标方法抛出异常的情况下

  • ①:前置通知
  • ②:环绕通知的调用目标方法之前的代码
  • ③:目标方法 抛出异常 异常通知
  • ④:后置通知

框架源码专题:Spring的Aop实现原理,Spring AOP 与 AspectJ 的关系相关推荐

  1. 框架源码专题:springIOC的加载过程,bean的生命周期,结合spring源码分析

    文章目录 1.BeanFactory和ApplicationContext的区别? 2. IOC与 Bean的加载过程 ①:初始化容器DefaultListableBeanFactory ②:创建读取 ...

  2. 框架源码专题:Spring声明式事务Transactional的原理

    文章目录 1. @Transactional的使用 2. spring事务的原理 2.1 开启事务,注册bean的后置处理器和相关bean对象,封装Advisor 2.2 匹配并创建动态代理 2.3 ...

  3. 框架源码专题:Spring是如何解决循环依赖的?

    文章目录 1.什么是循环依赖? 2.解决循环依赖思路 3. 使用了三级缓存还有什么问题?怎么解决的? 4. 手写伪代码解决缓存依赖 5. 二级缓存能否解决循环依赖,三级缓存存在的意义 6. Sprin ...

  4. 框架源码专题:Spring是如何集成Mybatis的?Spring怎么管理Mapper接口的动态代理

    文章目录 1. Spring集成Mybatis代码示例 2. Spring 如何解析Mybatis配置文件 3. Spring是怎么管理Mapper接口的动态代理的 4. Spring整合Mybati ...

  5. 框架源码专题:Spring的事件监听、发布机制 ApplicationListener

    文章目录 1.Spring内置事件 2.自定义事件 3.事件监听器 4.事件发布 publishEvent 4.Spring事件原理 5. 面试题:怎么样可以在所有Bean创建完后做扩展代码? 6. ...

  6. 框架源码专题:Mybatis启动和执行流程、源码级解析

    文章目录 1. Mybatis 启动流程 步骤一: 把xml配置文件解析成Configuration类 步骤二: 创建SqlSession会话 mybatis的三种执行器 步骤三: 在sqlSessi ...

  7. 框架源码专题:Mybatis的一级缓存、二级缓存是什么?有什么作用?

    文章目录 1. Mybatis中缓存的作用 2. 一级缓存 3. 二级缓存 4. 一级缓存和二级缓存的区别 5. 通过代码观察Mybatis缓存工作的全过程 1. Mybatis中缓存的作用 首先缓存 ...

  8. Spring框架源码阅读读后感

    Spring框架源码阅读读后感 spring的bean生命周期,从上到下依次完成,本人在阅读源码时总结得出此步骤,当然,spring是一个强大的框架,其对bean的生命周期管理只是其中的一部分,本人也 ...

  9. 有感而发:程序员到底要不要阅读框架源码?

    写在前面 最近正在写[高并发专题]的文章,其中,在[高并发专题]中,有不少是分析源码的文章,很多读者留言说阅读源码比较枯燥!问我程序员会使用框架了,会进行CRUD了,是否真的有必要阅读框架源码?! 对 ...

最新文章

  1. 搞技术的OpenAI现在要做投资了,成立1亿美元创业基金,网友:融资花不完了?...
  2. springboot使用Thymeleaf引入html文件
  3. JDK7 AIO介绍
  4. 转:linux svn常用命令
  5. [AHOI2005]COMMON 约数研究
  6. Oracle性能监控脚本(sql)
  7. 阿里巴巴Druid数据源及使用
  8. 数据挖掘:模型选择——SVM
  9. Django ModelForms
  10. 浅谈Eclipse dropins插件安装的坑(附m2e的各个版本插件下载)
  11. ExtJS Grid中文字链接点击事件的更合理的处理办法
  12. 诺基亚手机: 诺基亚N9将在下月19日开卖 售价4400元
  13. 操作系统中进程的同步、互斥、通信的区别,进程与线程同步知识
  14. vex编程语言是基于c语言,vex机器人编程软件 vex机器人大赛
  15. FineReport帆软报表的安装
  16. CMSInitiatingOccupancyFraction计算释疑
  17. 蓝桥杯 核桃的数量(python)
  18. 租车新玩法 神州租车打造行业标杆
  19. Android 自定义注解处理器详解
  20. C++中的new和delete运算符(内存管理)

热门文章

  1. 敏捷开发中的测试金字塔(转)
  2. 【java】关于Map的排序性的一次使用,有序的Map
  3. PostgreSQL 设置单条SQL的执行超时 - 防雪崩
  4. 融合大数据能力,解决在存量时代下的力分之困
  5. 六大举措深耕光通信市场
  6. [ImportNew]Java线程面试题
  7. FontAwesome-网站ui设计中一套非常棒的icon
  8. linux访问windows共享目录开机自动挂载
  9. jquery使用原型
  10. 区块链制作的比特币的缺陷