1. 代理对象创建时机解析
在Spring框架中,代理对象的创建时机是一个关键的设计决策点。AbstractAutoProxyCreator作为自动代理创建的核心基类,通过实现BeanPostProcessor接口,巧妙地介入到Spring容器的bean生命周期中。
1.1 BeanPostProcessor的工作机制
BeanPostProcessor是Spring框架提供的一个扩展接口,它允许我们在Spring容器完成bean实例化、配置之后,初始化回调执行前后对bean进行自定义处理。这个接口定义了两个关键方法:
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName); Object postProcessAfterInitialization(Object bean, String beanName); }AbstractAutoProxyCreator主要重写了postProcessAfterInitialization方法,在这个阶段创建代理对象。选择这个时机有几个重要考虑:
- 此时bean已经完全实例化,所有属性都已注入
- 初始化回调(如InitializingBean的afterPropertiesSet)已经执行
- 原始bean已经完全准备好,可以安全地为其创建代理
1.2 代理创建的核心流程
当Spring容器调用postProcessAfterInitialization方法时,AbstractAutoProxyCreator会执行以下判断逻辑:
- 检查当前bean是否已经是代理对象(避免重复代理)
- 检查当前bean是否在排除列表中(通过配置可以排除某些bean不被代理)
- 检查当前bean是否匹配切点表达式(通过AOP配置的切入点匹配)
- 如果满足条件,则调用createProxy方法创建代理
这个流程确保了代理只在必要时创建,且每个bean只被代理一次。
提示:理解这个流程对于调试AOP相关问题时非常重要。当发现某个bean没有被预期代理时,可以按照这个判断流程逐步排查原因。
2. AbstractAutoProxyCreator的实现细节
2.1 代理工厂的配置过程
createProxy方法是代理创建的核心,它的主要工作包括:
- 创建ProxyFactory实例
- 配置代理接口(如果使用基于接口的代理)
- 添加advisor(切面逻辑)
- 设置代理目标(原始bean)
- 根据配置决定使用JDK动态代理还是CGLIB
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); // 复制当前proxy creator的配置 proxyFactory.copyFrom(this); // 决定使用接口代理还是类代理 if (!proxyFactory.isProxyTargetClass()) { // 评估接口代理是否可行 evaluateProxyInterfaces(beanClass, proxyFactory); } // 添加advisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); // 定制代理工厂 customizeProxyFactory(proxyFactory); return proxyFactory.getProxy(getProxyClassLoader()); }2.2 JDK动态代理与CGLIB的选择策略
AbstractAutoProxyCreator支持两种代理方式,选择策略如下:
- 如果目标类实现了至少一个接口,且proxyTargetClass=false(默认),则使用JDK动态代理
- 如果目标类没有实现接口,或proxyTargetClass=true,则使用CGLIB
这个选择可以通过以下方式显式配置:
<aop:config proxy-target-class="true"> <!-- 强制使用CGLIB代理 --> </aop:config>或者通过注解:
@EnableAspectJAutoProxy(proxyTargetClass = true)注意:CGLIB代理会创建目标类的子类,因此目标类和方法不能是final的。这是使用CGLIB时常见的陷阱。
3. 性能优化与缓存机制
3.1 代理创建的缓存策略
为了提高性能,AbstractAutoProxyCreator实现了代理对象的缓存机制。主要缓存包括:
- targetSourcedBeans:记录已经处理过的目标bean
- advisedBeans:记录哪些bean已经被代理
- proxyTypes:缓存bean的代理类型信息
这些缓存确保了:
- 每个bean只被代理一次
- 相同的配置不会重复计算
- 代理类型信息可以快速获取
3.2 自定义代理创建逻辑
虽然AbstractAutoProxyCreator提供了完整的代理创建流程,但它也预留了多个扩展点供子类定制:
- getAdvicesAndAdvisorsForBean:决定哪些advisor应用于当前bean
- shouldSkip:是否跳过当前bean的代理
- createProxy:完全控制代理创建过程
- wrapIfNecessary:包装bean的核心方法
这些方法使得我们可以实现更复杂的代理逻辑,比如:
- 基于特定条件动态决定是否创建代理
- 为不同类型的bean应用不同的advisor组合
- 实现自定义的代理创建策略
4. 常见问题与解决方案
4.1 代理不生效的排查步骤
当发现预期的AOP逻辑没有生效时,可以按照以下步骤排查:
- 确认bean是否被Spring容器管理(检查@Component等注解)
- 检查是否配置了@EnableAspectJAutoProxy(注解驱动场景)
- 确认切点表达式是否能匹配目标方法
- 检查是否有多个BeanPostProcessor影响了代理创建顺序
- 调试AbstractAutoProxyCreator的postProcessAfterInitialization方法
4.2 循环引用中的代理问题
Spring处理循环依赖时,代理对象的创建会带来一些特殊情况:
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void methodA() { // ... } } @Service public class ServiceB { @Autowired private ServiceA serviceA; }在这种情况下,Spring会:
- 先创建ServiceA的原始实例
- 在属性注入阶段发现需要ServiceB
- 创建ServiceB实例并注入原始ServiceA
- 最后再为ServiceA创建代理
这意味着ServiceB中持有的实际上是ServiceA的原始对象,而非代理对象。要解决这个问题,可以通过:
- 使用@Lazy延迟注入
- 通过ApplicationContext.getBean()方法显式获取代理
- 重构设计避免循环依赖
4.3 代理对象的方法调用内部调用问题
另一个常见问题是代理对象的方法内部调用不会触发AOP逻辑:
@Service public class MyService { @Transactional public void methodA() { methodB(); // 这里的调用不会触发事务 } @Transactional public void methodB() { // ... } }这是因为内部调用绕过了代理对象。解决方案包括:
- 将methodB提取到另一个bean中
- 通过AopContext获取当前代理(需要暴露代理)
- 使用方法注入获取代理实例
5. 高级应用与自定义扩展
5.1 自定义AutoProxyCreator实现
我们可以继承AbstractAutoProxyCreator实现自定义的代理创建逻辑。一个典型的场景是基于注解的自动代理:
public class CustomAnnotationAutoProxyCreator extends AbstractAutoProxyCreator { @Override protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) { // 检查bean是否有自定义注解 if (beanClass.isAnnotationPresent(CustomProxy.class)) { return new Object[] { new CustomInterceptor() }; } return DO_NOT_PROXY; } }然后通过BeanPostProcessor注册:
@Bean public CustomAnnotationAutoProxyCreator customAutoProxyCreator() { return new CustomAnnotationAutoProxyCreator(); }5.2 代理工厂的深度定制
ProxyFactory提供了丰富的配置选项,我们可以通过customizeProxyFactory方法进行深度定制:
@Override protected void customizeProxyFactory(ProxyFactory proxyFactory) { super.customizeProxyFactory(proxyFactory); // 添加自定义的代理接口 proxyFactory.addInterface(MyAdditionalInterface.class); // 设置代理的暴露方式 proxyFactory.setExposeProxy(true); // 添加自定义的拦截器 proxyFactory.addAdvice(new CustomMethodInterceptor()); }5.3 多数据源场景下的代理策略
在需要动态数据源切换的场景中,我们可以利用AbstractAutoProxyCreator实现更智能的代理:
public class DataSourceAutoProxyCreator extends AbstractAutoProxyCreator { @Override protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) { // 只代理标记了@Repository的类 if (beanClass.isAnnotationPresent(Repository.class)) { return new Object[] { new DataSourceAdvice() }; } return DO_NOT_PROXY; } }DataSourceAdvice可以根据方法签名或参数动态选择合适的数据源。
6. 性能调优与最佳实践
6.1 代理创建的性能考量
代理对象的创建是有成本的,特别是在以下方面:
- 反射调用开销(JDK动态代理)
- 字节码生成开销(CGLIB)
- 方法调用时的拦截器链执行
优化建议:
- 合理设置proxyTargetClass,避免不必要的CGLIB代理
- 使用共享的Interceptor实例
- 精简切点表达式,减少匹配计算
- 考虑使用AspectJ编译时织入(LTW)替代运行时代理
6.2 代理对象的序列化问题
代理对象在序列化/反序列化时可能遇到问题,特别是:
- CGLIB代理包含非序列化的字段
- 反序列化后代理逻辑可能丢失
解决方案:
- 实现Serializable接口
- 提供自定义的writeReplace方法
- 考虑使用基于接口的代理(JDK动态代理)
6.3 测试环境中的代理处理
在单元测试中处理代理对象时需要注意:
- Mockito等框架可能无法正确处理CGLIB代理
- 测试环境可能需要特殊的代理配置
建议做法:
- 在测试配置中显式设置proxyTargetClass
- 使用AopTestUtils获取底层目标对象
- 考虑使用@DirtiesContext重置代理状态
我在实际项目中发现,深入理解AbstractAutoProxyCreator的工作原理,能够帮助我们在复杂的AOP场景中快速定位问题,并实现更灵活的代理策略。特别是在需要自定义代理逻辑或优化AOP性能时,这些知识显得尤为重要。