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

 2023-09-15 阅读 20 评论 0

摘要:文章目录1. Spring AOP 与 AspectJ 的关系2. JDK和Cglib动态代理的区别3. Spring AOP应用案例4. Spring AOP有几种配置方式?5. Spring AOP源码解析5.1 自动配置类 AopAutoConfiguration 开启对Aop的支持5.2 解析切面类,封装成Advisor5.3 匹配并创建动态代理5.4

文章目录

    • 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 的源码包括进来了,这也是为什么我们不需要显式的引入这两个依赖。

开源php源码,         作为 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动态代理的区别

spring源码讲解。相同点

  • 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不会!!
代理类型JDKCglib
使用场景目标类实现了接口,且未指定ProxyTargetClass = true目标类未实现接口
代理类的字节码文件数量根据接口生成1个$proxy.class文件根据具体类生成多个cglib.class文件
调用 原始方法 的方式反射直接调用(正因为直接调用速度快,所以cglib在调用时比jdk快)
在被增强的方法中调用其他方法时其他方法不会被增强,动态代理类只调用一次其他方法会被增强,因为每一个方法都会调用动态代理类!
代码形式InvocationHandler.invokeMethodInterceptor.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源码解析

js框架原理?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);}

java ssm框架?看一下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);}

html5框架、        在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};

PHP源码?把解析到的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;}

Java框架、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方法中,开始真正创建动态代理对象

web前端整站源码、创建动态代理是在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);}}

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

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

 

6. 五种通知执行顺序

 
6.1 目标方法无异常时

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

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

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

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/59753.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息