Spring AOP 和 IoC 以及容器启动之间有何关联?

Spring AOP 和 IoC 以及容器启动之间有何关联?

前言

本系列我们已经学习了整个 Spring AOP 核心的设计和实现原理,那么 AOP 和 IoC 之间有什么关联?在 Spring 容器启动时,到底一个 Bean 如何被替换成 Proxy 对象?本章我们就来一探究竟。

相关文章

项目环境

1.示例

首先我们写一个 AOP 的简单示例,由此开始

spring-aop-demo.xml 配置文件如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userImpl" class="com.csdn.spring.aop.demo.impl.UserImpl"/>

    <!-- this is the actual advice itself -->
    <bean id="profiler" class="com.csdn.spring.aop.demo.profiler.DefaultProfiler"/>

    <aop:config>
        <aop:aspect ref="profiler">

            <aop:pointcut id="someUserServiceMethod"
                          expression="execution(* com.csdn.spring.aop.demo.service.UserService.getUser())"/>

            <aop:before pointcut-ref="someUserServiceMethod"
                        method="beforeAdvice"/>

        </aop:aspect>
    </aop:config>

</beans>

用户业务接口

public interface UserService {
    User getUser();
}

用户业务实现类

public class UserImpl implements UserService {

    @Override
    public User getUser() {
        System.out.println("方法执行...");
        return new User(1L, "xwf");
    }

}

通知类

public class DefaultProfiler {

    public void beforeAdvice(JoinPoint call) throws Throwable {
        System.out.println("前置通知");
    }
}

测试代码

public class AopApplicationDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/spring-aop-demo.xml");
        UserService userService = applicationContext.getBean(UserService.class);
        User user = userService.getUser();
        System.out.println(user.toString());
    }
}

调试截图

从截图可以看到,我们通过 getBean 依赖查找的 userService 对象是一个 JdkDynamicAopProxy 对象,说明在 IoC 容器初始化的过程中,userService 已经被替换为 JdkDynamicAopProxy 对象。
在这里插入图片描述
执行结果:

前置通知
方法执行...
User{id=1, name='xwf'}

2.猜想&验证

2.1 猜想

在 IoC 容器初始化的过程中,userService 已经被替换为 JdkDynamicAopProxy 对象。

2.2 验证

首先我们回顾 Spring 容器启动以及 Spring Bean 创建的过程

AbstractApplicationContext#refresh //Spring 应用上下文启动方法

  • DefaultListableBeanFactory#preInstantiateSingletons
    • AbstractBeanFactory#getBean(java.lang.String) // 逐个调用 getBean 方法
      • AbstractBeanFactory#doGetBean // 创建 Bean

在 doGetBean 方法中 298 行打上断点,添加条件 beanName.equals("userImpl"),因为我们只需要找到 UserService Bean 到底是在哪个阶段被替换成 Proxy 代理对象
在这里插入图片描述
继续往下执行,发现在 333 行之前,这个 bean 对象已经变成了 Proxy 类型,说明在 createBean 方法中就已经被替换了
在这里插入图片描述
一直往下探源码直到 AbstractAutowireCapableBeanFactory#doCreateBean 中,打上断点并加上条件,可以看到目前对象还是 UserImpl
在这里插入图片描述
继续往下执行,发现对象发生了变化,变成了 Proxy 对象,说明替换的过程一定在 AbstractAutowireCapableBeanFactory#initializeBean() 中,也就是初始化 Bean 方法中。
在这里插入图片描述
最后找到在 initializeBean 方法中最后会执行 applyBeanPostProcessorsAfterInitialization 方法,4 个 Processors 如下:
在这里插入图片描述
从名称可以看出第三个 AspectJAwareAdvisorAutoProxyCreator 和 Proxy 的创建相关,然后我们继续查找源码,这个方法在 AbstractAutoProxyCreator#postProcessAfterInitialization 中有相关的实现。最终调用 AbstractAutoProxyCreator#wrapIfNecessary,代码如下:
在这里插入图片描述
终于我们找到了创建代理对象的方法 createProxy

Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

具体 AopProxy 代理创建的过程,可以回顾之前的文章 《Spring AOP 创建 AopProxy 代理对象原理分析》

3.结论

知识点:ClassPathXmlApplicationContext 构造的过程中会调用 refresh() 方法启动容器,所以本例没有显示的调用 refresh()

从上面的验证过程可以看到,在 Spring 应用上下问题启动过程中,如果 Spring bean 和 AOP 的配置匹配,那么 Spring Bean 在初始化阶段完成之后会被替换成一个 AopProxy 对象,具体是 JDK 还是 CGLIB 根据实际情况。

具体替换的阶段是在 Spring Bean 生命周期的 初始化后阶段,postProcessAfterInitialization() 方法中,而 AspectJAwareAdvisorAutoProxyCreator 的父类实现了 InstantiationAwareBeanPostProcessor 这个回调接口,并在 postProcessAfterInitialization 方法中使用 createProxy() 来创建这个 Proxy 对象,最终完成替换。

Spring Bean 生命周期相关的文章

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP利用IOC(控制反转)来实现对目标类的增强。在Spring框架中,通过IOC容器来管理和创建对象,而不是由开发者手动创建对象。当使用Spring AOP时,我们可以将切面(Aspect)定义为一个普通的Java类,并使用IOC容器来创建和管理切面对象。 具体来说,Spring AOP通过以下步骤来利用IOC实现对目标类的增强: 1. 在Spring配置文件中定义切面(Aspect)类,并将其声明为一个Bean。例如: ```xml <bean id="myAspect" class="com.example.MyAspect"></bean> ``` 2. 在切面类中定义增强(Advice)方法,例如@Before、@After等。这些增强方法将在目标类的特定方法执行前后被调用。 3. 在Spring配置文件中定义目标类,并将其声明为一个Bean。例如: ```xml <bean id="myService" class="com.example.MyService"></bean> ``` 4. 在Spring配置文件中配置AOP代理,将切面和目标类关联起来。例如: ```xml <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut expression="execution(* com.example.MyService.*(..))" id="myPointcut"/> <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> ``` 在上述配置中,我们将切面类(myAspect)和目标类(myService)关联起来,并指定了切入点(pointcut)和增强方法(beforeAdvice)。 通过以上步骤,Spring AOP会在运行时动态地创建代理对象,并将增强逻辑织入到目标类的方法中。这样,当目标类的方法被调用时,增强方法会在特定的时机被执行,实现对目标类的增强。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值