Spring AOP 和 IoC 以及容器启动之间有何关联?
前言
本系列我们已经学习了整个 Spring AOP 核心的设计和实现原理,那么 AOP 和 IoC 之间有什么关联?在 Spring 容器启动时,到底一个 Bean 如何被替换成 Proxy 对象?本章我们就来一探究竟。
相关文章
项目环境
- Java 8
- Spring framework 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/spring-aop-demos
- 本章模块:demo
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
- AbstractBeanFactory#getBean(java.lang.String) // 逐个调用 getBean 方法
在 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 生命周期相关的文章
- 《夯实Spring系列|第十四章:Spring Bean 生命周期-下篇》 - 5.Spring Bean 初始化后阶段