Spring 用的是什么动态代理?它是怎么配置的?

发布于:2025-06-26 ⋅ 阅读:(14) ⋅ 点赞:(0)

程序员面试资料大全|各种技术书籍等资料-1000G

一、Spring 的动态代理策略

Spring 采用智能代理策略,根据目标对象特性自动选择代理方式:

有接口
无接口
目标对象
是否有接口?
JDK 动态代理
CGLIB 代理
创建接口代理
创建子类代理

选择逻辑详解:

  1. 默认行为

    • 目标类实现了接口 → JDK 动态代理
    • 目标类未实现接口 → CGLIB 代理
  2. 强制策略

    • 可通过配置强制使用 CGLIB(即使有接口)

二、配置方式详解

1. XML 配置(传统方式)

<aop:config proxy-target-class="true"> <!-- 强制使用CGLIB -->
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.service.*.*(..))"/>
</aop:config>

2. 注解配置(主流方式)

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制CGLIB
public class AppConfig {
    // 其他配置
}

3. Spring Boot 自动配置

# application.properties
spring.aop.proxy-target-class=true # 全局启用CGLIB

三、核心实现原理

JDK 代理工作流程:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 1. 获取拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        
        // 2. 创建方法调用对象
        MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        
        // 3. 执行拦截器链
        return invocation.proceed();
    }
}

CGLIB 代理核心:

public class CglibAopProxy implements AopProxy {
    
    private static class DynamicAdvisedInterceptor implements MethodInterceptor {
        
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
            // 1. 获取拦截器链
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            
            // 2. 创建Cglib方法调用
            CglibMethodInvocation invocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);
            
            // 3. 执行拦截器链
            return invocation.proceed();
        }
    }
}

四、性能对比与调优

现代环境下的性能表现:

场景 JDK 代理 (ns/op) CGLIB (ns/op) 差异
简单方法调用 15.2 ± 0.5 18.7 ± 0.8 +23%
带5个Advice的方法调用 125.6 ± 2.1 132.3 ± 3.4 +5%
首次创建代理对象 0.5 ms 5.2 ms 10x

:基于 JDK 17 + Spring 6 的基准测试 (JMH)

调优建议:

  1. 高频调用场景

    // 启用优化模式(减少反射开销)
    @EnableAspectJAutoProxy(optimize = true)
    
  2. 减少 Advice 数量

    • 合并相同功能的切面
    • 使用 @Around 代替多个独立通知
  3. 代理对象缓存

    // 在多次使用的地方缓存代理引用
    private final SomeService cachedProxy;
    
    public MyComponent(@Lazy SomeService proxy) {
        this.cachedProxy = proxy;
    }
    

五、常见问题解决方案

问题1:内部方法调用不走代理

场景

public class OrderService {
    public void createOrder() {
        this.validate(); // 不走代理!
    }
    
    @Transactional
    public void validate() {...}
}

解决方案

// 方案1:自我注入(推荐)
public class OrderService {
    @Autowired
    private OrderService self; // 注入代理对象
    
    public void createOrder() {
        self.validate(); // 走代理
    }
}

// 方案2:使用AopContext(需启用exposeProxy)
@EnableAspectJAutoProxy(exposeProxy = true)
public class Config {}

public void createOrder() {
    ((OrderService) AopContext.currentProxy()).validate();
}

问题2:代理对象类型转换异常

错误

@Autowired
private UserRepository userRepository; // 实际是代理对象

// 直接转换失败
JpaRepository repo = (JpaRepository) userRepository; 

解决方案

// 1. 使用AopUtils获取目标对象
UserRepository target = (UserRepository) AopProxyUtils.getSingletonTarget(userRepository);

// 2. 通过Advised接口访问目标
if (userRepository instanceof Advised) {
    TargetSource targetSource = ((Advised) userRepository).getTargetSource();
    UserRepository realRepo = (UserRepository) targetSource.getTarget();
}

六、高级配置技巧

1. 混合代理策略

@Configuration
public class HybridProxyConfig {

    @Bean
    @Scope(proxyMode = ScopedProxyMode.INTERFACES) // 指定接口代理
    public PaymentService paymentService() {
        return new PaymentServiceImpl();
    }

    @Bean
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) // 强制CGLIB
    public ReportService reportService() {
        return new ReportService();
    }
}

2. 自定义代理处理器

public class CustomProxyProcessor extends AbstractAutoProxyCreator {

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        if (beanClass.getName().contains("Service")) {
            return new Object[] { new LoggingInterceptor() };
        }
        return DO_NOT_PROXY;
    }
}

// 注册处理器
@Bean
public static CustomProxyProcessor customProxyProcessor() {
    return new CustomProxyProcessor();
}

最佳实践总结

  1. 默认策略

    // 无特殊需求不修改proxyTargetClass
    @EnableAspectJAutoProxy
    
  2. 性能敏感场景

    # 启用代理优化
    spring.aop.auto=true
    spring.aop.proxy-target-class=false # 优先JDK代理
    
  3. 事务/安全等场景

    // 强制使用CGLIB确保代理生效
    @EnableTransactionManagement(proxyTargetClass = true)
    @EnableGlobalMethodSecurity(proxyTargetClass = true)
    
  4. 调试技巧

    // 判断代理类型
    AopUtils.isJdkDynamicProxy(object) 
    AopUtils.isCglibProxy(object)
    
    // 获取目标类
    AopUtils.getTargetClass(object)
    

程序员面试资料大全|各种技术书籍等资料-1000G

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到