Spring Aop流程源码分析

Spring AOP实现流程、源码分析 前言 AOP字面意思解释就是面向切面编程,面向切面编程是一种编程模型,我们知道JAVA是面向对象的也就是OOP,OOP这种面向对象的编程适用于定义纵向的关系,但是并不适用定义横向的关系。那么应对这种OOP的存在的这些不利,AOP面向切面的编程模型作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。 AO 阅读详情

终于来到了我们的Spring Aop的位置,为什么非要剖析一下这个源码呢?是因为之前在这块遇到过坑儿啊。图片 之前一直也没有搞明白是怎么回事,现在终于是弄懂了

书接上回,话说Spring启动之后,创建初始化Bean的时候 使用了 BeanPostProcessor 对Bean 对象进行了增强,

Spring Aop 也不例外,也是基于 BeanPostProcessor 做的增强扩展,将原来的Bean 实例对象,切换成了具有增强功能的代理对象

当然底层是基于之前我们讲过的Jdk的动态代理和CGlib的代理

话不多说,上图

一、Spring Aop 代理的流程:

在这里插入图片描述
从图上可知 在Spring Ioc 初始化bean对象的时候,使用了 BeanPostProcessor 增强
AbstractAutowireCapableBeanFactory.initializeBean() 方法


protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
        invokeAwareMethods(beanName, bean);
        return null;
      }, getAccessControlContext());
    }
    else {
    // 调用 Aware 方法 将对应容器信息设置到bean 对象里面去
      invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
    // 调用 beanPostProcessor 的前置增强方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
    // 调用初始化方法 afterProperties() -- 和指定的init-method 
      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()) {
    // 调用 beanPostProcessor 的后置增强
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
  }

最终会根据是否配置的有@EnableAspectJAutoProxy 是否开启Aop自动代理

[配置的切面自动代理增强器的对象在容器启动的时候会被自动注入到BeanFactory 容器中的 List beanPostProcessors 属性中

(AnnotationAwareAspectJAutoProxyCreator ----> 实现了BeanPostProcessor 接口)]

来调用 AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization() 方法

// AnnotationAwareAspectJAutoProxyCreator的此方法 
// 继承自 AbstractAutoProxyCreator

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }
  
  // 最终调用到了 AbstractAutoProxyCreator.wrapIfNecessary() 方法
  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
    }

    // Create proxy if we have advice.
    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;
  }

具体方法调用链图如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
AbstractAutoProxyCreator

至此 最终代理对象的创建还是调用底层的 两个

JdkDynamicAopProxy的getProxy() 方法

或者

CglibAopProxy的getProxy() 方法

【此处如图:若配置@EnableAspectJAutoProxy 指定了 proxyTargetClass=true
则会调用CglibAopProxy的getProxy() 方法来创建代理对象
如果没有配置proxyTargetClass,& 目标对象如果实现了接口,则使用jdk动态代理,如果没有就采用cglib的方式–>cglib 是基于继承的方式实现代理的】

在这里插入图片描述
jdk的动态代理,底层还是调用的Proxy.newInstance() 方法,此时就又回到了我们之前讲的代理模式下的应用和创建

在这里插入图片描述
在这里插入图片描述
CglibAopProxy动态代理 则是调用的

spring.cglib包下面的类库

org.springframework.cglib.proxy.Enhancer.create() 方法来创建代理对象的
这里的代理机制和原理跟上篇 java 代理模式[Proxy]–AOP 中的 Cglib 代理的原理机制是一样的
这里spring用的是自己写的一些包和类库,方法调用链和类库还是比较多的,感兴趣的话可以再往下跟跟
我这里就暂时先到这里

二、Spring Aop代理中之前遇到的坑儿

我们来自定义一个增强器CacheAspect,解析增强带有自定义注解@CacheEnable的方法,实现切面编程,实现无侵入式扩展增强

然而,如果在同一个类中,如果有两个方法同时被@CacheEnable 标注为增强,并且第一个方法又调用了第二个方法,调用了自己的被代理的方法二,就会造成第二个方法的增强功能失效

大致流程如下图:

在这里插入图片描述
举例代码如下:


/**
 * @author xiaolong.ge
 * @since 16 五月 2022
 */
@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserDao userDao;
  @Autowired
  private RedisService redisService;
  /**
   * 若缓存可用 则从缓存获取数据
   * @param id
   * @return
   */
  @CacheEnable
  @Override
  public User getById(Integer id) {
    System.out.println("调用 getById方法");
    return getUserFromRedis();
  }
// 在本类的其他方法中调用此方法 则该注解的增强效果会失效
  @CacheEnable
  public User getUserFromRedis() {
    return redisService.getUserData();
  }
}
-----------------------------------------
@Service
public class RedisService {
    @Autowired
    private UserService userService;
    
    public User getUserData() {
      return new User("redis",50);
    }
}

切面类代码:

@Aspect
@Component
public class CacheAspect {

  @Around("@annotation(com.gexl.annotation.CacheEnable)")
  public Object aroundCacheEnable(ProceedingJoinPoint joinPoint) throws Throwable {
    //获取目标对象
    Object tarObj = joinPoint.getTarget();
    // 获取代理对象
    Object proxyObj = joinPoint.getThis();
    // 获取到 CacheEnable 注解
    String methodName = joinPoint.getSignature().getName();
    System.out.println("代理对象proxy="+proxyObj+"--调用methodName="+methodName);
    return joinPoint.proceed();
  }
}

测试类:


// 测试类
public class Test {
  public static void main(String[] args) {
//    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    AbstractApplicationContext ac = new AnnotationConfigApplicationContext(App.class);
    UserService userService = (UserService)ac.getBean("userServiceImpl");
    User user = userService.getById(1);
    System.out.println(user);
   }
}
// 执行结果如下:
代理对象proxy=com.gexl.service.impl.UserServiceImpl@2d9d4f9d--调用methodName=getById
调用 getById方法
User{name='redis', age=50}

从测试类的执行结果看,Aop只拦截到了getById方法,而getUserFromRedis() 方法并未被拦截
是为getUserFromRedis()方法上加的@CacheEnable 注解增强失效了

解决方案:
最好就是梳理好自己的代码逻辑,尽量不要在本类的方法中调用本类的其他代理方法,若要使其生效,可以做如下修改:

1.修改 UserServiceImpl 类


/**
 * @author xiaolong.ge
 * @since 16 五月 2022
 */
@Service
public class UserServiceImpl implements UserService, BeanNameAware {
  @Autowired
  private UserDao userDao;
  @Autowired
  private RedisService redisService;
  
  private String beanName;
  
  // 添加容器对象 
  @Autowired
  private ApplicationContext applicationContext;

  @Override
  public void setBeanName(String name) {
    this.beanName = name;
  }
  /**
   * 若缓存可用 则从缓存获取数据
   * @param id
   * @return
   */
  @CacheEnable
  @Override
  public User getById(Integer id) {
    System.out.println("调用 getById方法");
    //return getUserFromRedis();
    // 从容器中获取代理对象 
    UserService userServiceProxy = (UserService) applicationContext.getBean(beanName);
    Method method = userServiceProxy.getClass().getMethod("getUserFromRedis",null);
    // 调用 代理对象的 代理方法 注解才会生效
    return (User)method.invoke(userServiceProxy,null);
  }
// 在本类的其他方法中调用此方法 则该注解的增强效果会失效
  @CacheEnable
  public User getUserFromRedis() {
    return redisService.getUserData();
  }
}

修改完成再次运行测试类:结果如下:
getUserFromRedis() 也被代理增强了,注解@CacheEnable 生效


代理对象proxy=com.gexl.service.impl.UserServiceImpl@22635ba0--调用methodName=getById
调用 getById方法
代理对象proxy=com.gexl.service.impl.UserServiceImpl@22635ba0--调用methodName=getUserFromRedis
User{name='redis', age=50}

2. 设置@EnableAspectJAutoProxy的 expostProxy 的属性为true
在这里插入图片描述

然后在修改UserServiceImpl.java 在方法中如下调用

@CacheEnable
  @Override
  public User getById(Integer id,String name) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

    System.out.println("调用 getById方法");
//    UserService userServiceProxy = (UserService) applicationContext.getBean(beanName);
//    Method method = userServiceProxy.getClass().getMethod("getUserFromRedis",null);
//    return (User)method.invoke(userServiceProxy,null);
    // 获取到当前类的代理对象  注:设置getUserFromRedis为接口方法
    UserService userServiceProxy = (UserService)AopContext.currentProxy();
    // 由代理类调用 增强之后的方法,则 注解增强生效
    return userServiceProxy.getUserFromRedis();
  }

再次运行测试类:


代理对象proxy=com.gexl.service.impl.UserServiceImpl@23282c25--调用methodName=getById
调用 getById方法
代理对象proxy=com.gexl.service.impl.UserServiceImpl@23282c25--调用methodName=getUserFromRedis
User{name='redis', age=50}

至此,问题解决

XGBoost原生接口和Sklearn接口参数详解 阅读详情

相关推荐

不用Root也能抓包?Android高版本HTTPS抓包的3种替代方案(含Charles技巧)

本文针对Android高版本HTTPS抓包难题,提供了三种无需Root权限的实战解决方案:利用VirtualXposed等虚拟沙盒环境、配置Postern等系统级代理工具,以及通过修改应用源码或网络安全配置。文章还详细解析了Charles抓包工具在非Root环境下的高级配置与排错技巧,帮助开发者在遵守安全规范的前提下,有效进行网络调试与监控。

http9protocoller的博客 648

Spring AOP源码分析

目录 1、AOP是什么 2、AOP 的作用及其优势 3、AOP 的底层实现 3.1 JDK动态代理: 3.2 CGLIB动态代理 4、AOP相关概念 5、AOP运行流程图 6、AOP源码解析 6.1 源码解析须知 6.2 注册自动代理创建器 6.3 触发后置处理器 6.4 创建Proxy过程分析 6.5 代理回调增强 6.6 增强器执行 6.7 增强器执行顺序 7、源码总结 1、AOP是什么 AOP 为 Aspect Oriented Programming ..

长沙老码农 2447

WOL唤醒配置(以太网、PHY、MAC)

MAC 就是以太网控制器,属于OSI的数字链路层。phy 属于OSI的物理层(Physical layer),所以叫phy。CPU Mac与phy通信通过MII接口,数据的传输通过MDIO数据线。通过配置phy的相关寄存器实现不同的功能。

qq_58264156的博客 8633

Spring原理:PostProcessor与AOP原理

Spring原理:PostProcessor与AOP原理

Leon_Jinhai_Sun的博客 970

RADIOML 2018.01A调制信号数据集介绍及处理

RadioML 2018.01A 由 DeepSig 发布,是专门针对自动调制识别(Automatic Modulation Recognition, AMR)和无线信号分类研究设计的基准数据集。它在原有 RadioML 2016.10a 的基础上,扩展并精细化了信道仿真模型,使得信号更接近真实无线环境下的表现。该数据集广泛用于机器学习模型的训练与评估,为各类深度学习算法提供了统一、可重复的实验条件。

weixin_56527269的博客 1898

GPU硬件架构与微架构深度解析28-稀疏计算:稀疏矩阵计算的硬件支持

本文介绍了GPU加速稀疏计算的关键技术。主要内容包括:1)稀疏数据的存储格式(COO、CSR、CSC)及其特点;2)现代GPU的稀疏计算硬件优化,如NVIDIA的稀疏张量核心和内存访问优化;3)稀疏计算在推荐系统、NLP和科学计算等领域的应用案例;4)性能优化技巧,包括存储格式选择、硬件特性利用和混合精度计算等。文章指出,专用稀疏计算技术可显著提升AI和大规模科学计算的效率,相比传统方法可获得5-10倍的性能提升。

2301_79143213的博客 114

【Spring】BeanPostProcessor 和 AOP 相关的 BeanPostProcessor

Bean后置处理器 BeanPostProcessor BeanPostProcessor 是一个接口类: public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName)

L_MaFia的博客 2544

手把手教你从入门到精通C# MES通信

本文是C#与MES通信通信的目录,使用C#与各种MES系统通信的方法我都会在专栏中讲到,比如WebService、Http、Ftp、共享文件夹、串口通信等,绝对是上位机软件工程师实现与MES系统通信的必备教程!

qq_34059233的博客 8751

Spring源码之AOP源码解析

文章目录前言基本概念横切关注点切面(Aspect)连接点(JoinPoint)切入点(PointCut)通知(Advice)目标对象(Target)织入(Weave)引入(Introduction)类结构体系示例代码产生过程创建过程执行过程 前言 上一篇看了Spring IOC源码,猿猿们都知道,有IOC就有AOP,那这篇就结合例子来看一下AOP的源码。 基本概念 AOP(Aspect Orien...

从入门到脱发 4451

文末含资料链接和视频讲解!YOLOv8集成CoordAtt注意力机制详细教程

本文详细介绍了如何在YOLOv8目标检测框架中集成CoordAtt注意力机制。首先解析了CoordAtt的工作原理,它通过分解式池化将位置信息嵌入到通道注意力中,相比传统注意力机制具有更好的空间信息保持能力。然后提供了PyTorch实现代码解析,包括自适应平均池化、Hard-Swish激活函数等关键实现细节。最后概述了YOLOv8架构,为后续集成CoordAtt提供基础。该教程适合具备深度学习基础的开发者,将理论与实践结合,帮助读者掌握这一目标检测优化技术。

FJN110的博客 266

九天揽月带你玩转Ardupilot 的EKF2纸老虎(1)

目录 目录 摘要 1.kalman基础知识储备 摘要 本文主要记录自己学习ardupilot的ekf2代码的过程,相信很多人想移植或者学习ekf2,看到眼花缭乱的代码无从下手。九天揽月将带你玩转Ardupilot 的EKF2纸老虎 1.kalman基础知识储备 (1)先来看下最基础也是最重要的线性kalman 总结:不要害怕kalman,只要你可以懂得线性kal...

陌城烟雨 1万+

Spring IOC---AOP代理对象生成的时机

文章目录1.概述2.前置知识3.Spring AOP代理对象生成的时机3.1非提前生成代理对象3.2 提前生成代理对象4. 为什么需要两种动态代理的时机 1.概述 Spring AOP可以采用注解或者xml配置的方式实现,那么在spring的生命周期当中,是在什么时候生成的代理对象呢?本文就AOP代理对象生成的时机进行介绍。不清楚spring生命周期的读者可以先阅读另一篇博客《Spring IOC—Bean的生命周期》。 2.前置知识 BeanPostProcessor接口的作用 简单的讲就是在一个对象

gongsenlin341的博客 1万+

【Nginx学习】5步揭秘Nginx邮件代理模块:邮件代理服务器的功能与实现

Nginx的邮件代理模块(Mail Proxy Module)允许Nginx作为邮件代理服务器,支持SMTP、POP3和IMAP协议。这使得Nginx可以作为一个高性能的邮件网关,用于过滤垃圾邮件、病毒扫描、负载均衡等任务。简单来说,Nginx在这里就像是一个邮递员,负责将邮件从发件人手中安全、快速地送到收件人手中。通过上述的解析,我们可以看到Nginx在邮件代理模块中的设计和实现。从配置解析、连接处理,到协议处理、SSL/TLS加密和日志记录,每一步都至关重要。

java专栏 1314

Spring AOP 执行流程及源码分析

CGLIB 和 JDK 动态代理实现,区别是 CGLIB 是采用继承类的方式实现,如果方法中为 final 或 static 将不会被代理增强,生成的代理类会比较多;而 JDK 是采用实现接口的方式;如果它包含的接口中有存在 equals 或 hashCode 方法时将不会被代理增强,生成的代理类只有一个;它们的底层实现都是通过 ASM 字节码框架操作的. 两者在处理整个流程时,都是先将所有的 advisor 进行匹配,然后再将需要进行增强的 advisor 拦截器找到并组装起来

vnjohn 博主 > 主线分享后端领域业务设计知识、源码思想、架构设计 2893

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Spring的AOP调用流程源码分析~

这里我们只对基于注解的AOP进行讲解。 首先定义一个类用于扫描包: @Service @ComponentScan(basePackages = {"com.test"}) public class EnableComponentScan { } 然后创建一个类开启AOP的注解扫描: 这也是Springboot开启AOP的方式。 @Service @EnableAspectJAutoProxy...

一颗小陨石的博客 851

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Spring-Aop流程源码分析

spring aop的整体流程思路是这样的:首先先注册一个专门处理aop的BeanPostProcess,在bean的实例化过程中,从BeanPostProcess的list列表中拿出这个处理aop的BeanPostProcess,判断该bean是否满足advise要求,如果符合,会针对该bean创建一个代理对象,最终在ioc容器中,该beanName对应的实例就是这个代理对象。如果不符合,返回的就还是原生对象。 @Configuration @ComponentScan("com.su.spring.ao

xianmingsu的博客 183

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Spring源码分析(四) Aop全流程

spring Aop 全流程分析以及总结

springsdl的博客 435

Spring源码分析之AOP拦截器调用流程

Spring AOP 拦截器调用流程 1、代理对象调用invoke方法 @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; /** * 【1】获取代理工厂 */ TargetSource targetSour

白鸽 827

Spring AOP执行流程图&源码分析

老规矩,先来个图: 源码,前边的执行和普通bean的加载流程一致,我们直接来到doCreateBean

雪落南城的博客 2183

较真儿学源码系列-Spring AOP核心流程源码分析

Spring AOP核心流程源码分析

天瑕的博客 768

Spring框架进阶(九)Spring V3.0 AOP源码分析流程

1、总体分析 在Spring的设计之中,AOP发生在实例化的过程中,在符合代理要求的情况下,返回的类从原生类改为代理类。 整个代理切面方法会被组成一个代理链,通过递归的方式,将它们按顺序执行。 2、源码分析 回到上一节DI之中,可以看到,之前提到过在doCreateBean方法之中,有两个方法,一个是实例化方法createBeanInstance,一个是依赖注入方法pupolateBean。 ...

jiayibingdong的博客 725

Spring基于注解方式的AOP的原理流程和源码分析【一】

  废话不多说直接上源码: Bean public class MathDiv { public int div(int i,int j){ return i/j; } } LogAspects import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotatio...

one_dayR的博客 403

spring-aop源码分析(3)完结_执行流程分析

本文详细介绍Spring AOP的执行阶段流程。Cglib代理的代理拦截逻辑在DynamicAdvisedInterceptor中,JDK代理的拦截逻辑在JdkDynamicAopProxy中,本文将从这两个类入手分析Spring AOP的执行阶段流程。

xuguofeng2016的博客 966

Spring基于注解方式的AOP的原理流程和源码分析【二】

流程:创建AnnotationAwareAspectJAutoProXyCreator的过程 AnnotationConfigApplicationContext(ConfigOfAop.class);  创建配置类,创建ioc容器 注册配置类,刷新容器 注册bean的后置处理器,方便拦截bean的创建 获取ioc已经定义的需要创建对象的beanPostP...

one_dayR的博客 276

Spring的深入浅出--9.注解AOP实例及bean创建以及bean创建后执行流程的源码分析

AOP(动态代理):指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方法; 导入AOP模块jar包 创建一个业务逻辑类MathCalculator,实现一个简单的除法 要实现的业务:在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常等等) 创建LogAspects类 通知方法: 前置通知(@Before):logStrat:在目标方法(...

moxiaolin的博客 571

Java架构学习(二十六)SpringBean生命周期与Aop、SpringMVC执行流程源码分析(没看)

没看

Leeue李月 550
上一篇: Spring源码编译Could not find method compile()的问题
下一篇: Redis工作原理与机制
xiaolong_java
博客等级 码龄12年 1粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值