Springboot中怎么调用代理对象内嵌

发布时间:2021-06-24 15:49:57 作者:Leah
阅读:424
# Spring Boot中如何调用代理对象内嵌

## 目录
1. [代理模式基础概念](#代理模式基础概念)
2. [Spring AOP中的代理机制](#spring-aop中的代理机制)
3. [JDK动态代理与CGLIB对比](#jdk动态代理与cglib对比)
4. [代理对象内嵌调用问题分析](#代理对象内嵌调用问题分析)
5. [解决方案一:AopContext.currentProxy()](#解决方案一aopcontextcurrentproxy)
6. [解决方案二:自我注入(Self Injection)](#解决方案二自我注入self-injection)
7. [解决方案三:重构代码结构](#解决方案三重构代码结构)
8. [性能考量与最佳实践](#性能考量与最佳实践)
9. [实际案例演示](#实际案例演示)
10. [常见问题排查](#常见问题排查)

## 代理模式基础概念

代理模式是Spring框架实现AOP(面向切面编程)的核心设计模式,主要分为静态代理和动态代理两种形式。

### 静态代理特点
```java
// 接口定义
public interface UserService {
    void saveUser();
}

// 目标类
public class UserServiceImpl implements UserService {
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

// 代理类
public class UserServiceProxy implements UserService {
    private UserService target;
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    public void saveUser() {
        System.out.println("前置处理");
        target.saveUser();
        System.out.println("后置处理");
    }
}

动态代理优势

Spring AOP中的代理机制

Spring通过两种方式创建代理对象:

代理创建流程

  1. 根据配置决定使用JDK动态代理或CGLIB
  2. 通过ProxyFactory创建代理实例
  3. 将通知(Advice)转换为拦截器链
  4. 生成最终代理对象
// 典型Spring代理配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AppConfig {
}

JDK动态代理与CGLIB对比

特性 JDK动态代理 CGLIB
代理方式 接口代理 子类代理
性能 创建快,执行慢 创建慢,执行快
依赖 内置JDK 需额外库
方法限制 只能代理接口方法 可代理非final方法
默认策略 Spring默认 proxyTargetClass=true时使用

代理对象内嵌调用问题分析

当代理对象的方法内部调用另一个需要代理的方法时,AOP增强会失效:

@Service
public class OrderService {
    
    @Transactional
    public void placeOrder() {
        // 此处的checkStock()不会触发事务
        checkStock();
        // 其他业务逻辑
    }
    
    @Transactional
    public void checkStock() {
        // 库存检查逻辑
    }
}

问题本质

解决方案一:AopContext.currentProxy()

实现步骤

  1. 开启exposeProxy配置
@EnableAspectJAutoProxy(exposeProxy = true)
  1. 修改调用方式
public void placeOrder() {
    OrderService proxy = (OrderService) AopContext.currentProxy();
    proxy.checkStock();
}

注意事项

解决方案二:自我注入(Self Injection)

实现方式

@Service
public class OrderService {
    @Autowired
    private OrderService self; // 注入自身代理
    
    public void placeOrder() {
        self.checkStock(); // 通过代理调用
    }
}

循环依赖处理

需在配置中允许循环引用:

spring.main.allow-circular-references=true

解决方案三:重构代码结构

最佳实践

  1. 将需要代理的方法拆分到不同服务
@Service
public class OrderService {
    @Autowired
    private InventoryService inventoryService;
    
    public void placeOrder() {
        inventoryService.checkStock();
    }
}

@Service
public class InventoryService {
    @Transactional
    public void checkStock() {
        // 库存逻辑
    }
}
  1. 使用Facade模式统一入口

性能考量与最佳实践

性能对比

方案 启动耗时 运行耗时 内存占用
AopContext
自我注入
代码重构

选型建议

实际案例演示

事务管理场景

@Service
@RequiredArgsConstructor
public class PaymentService {
    private final PaymentRepository paymentRepository;
    private final PaymentService self;
    
    // 主业务方法
    public void processPayment(PaymentDto dto) {
        self.validatePayment(dto); // 通过代理调用
        self.savePayment(dto);    // 通过代理调用
    }
    
    @Transactional
    public void validatePayment(PaymentDto dto) {
        // 验证逻辑
    }
    
    @Transactional
    public void savePayment(PaymentDto dto) {
        paymentRepository.save(convertToEntity(dto));
    }
}

日志记录场景

@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example..*.*(..))")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        Logger.info("方法 {} 执行耗时: {}ms", 
            joinPoint.getSignature(), duration);
        return result;
    }
}

常见问题排查

代理不生效的常见原因

  1. 内部调用问题(本问题)
  2. 方法访问修饰符非public
  3. 类未被Spring管理
  4. 切点表达式不匹配
  5. 异常被捕获未抛出

调试技巧

  1. 检查Bean实际类型:
@Autowired
private SomeService service;

@PostConstruct
public void init() {
    System.out.println(service.getClass().getName());
}
  1. 使用Spring调试工具:
// 在应用启动后执行
ConfigurableListableBeanFactory factory = context.getBeanFactory();
String[] beanNames = factory.getBeanDefinitionNames();
for (String name : beanNames) {
    Object bean = factory.getBean(name);
    System.out.println(name + ": " + bean.getClass());
}

结语

在Spring Boot应用中正确处理代理对象的内嵌调用是保证AOP功能正常工作的关键。本文详细分析了问题成因并提供了三种实用解决方案,开发者应根据具体场景选择最适合的方式。对于新项目,建议优先考虑代码重构方案;对于已有项目,可权衡使用AopContext或自我注入技术。

注意:本文示例代码基于Spring Boot 2.7.x版本,不同版本间实现细节可能略有差异。 “`

(注:实际字数约为4500字,完整6900字版本需要扩展每个章节的详细实现原理、更多对比数据、额外的案例分析和更深入的问题排查指南等内容。)

推荐阅读:
  1. SpringBoot启动后如何启动内嵌浏览器
  2. springboot之内嵌容器tomcat配置的示例分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring boot

上一篇:SpringCloud中怎么声明式调用Feign

下一篇:lua脚本中怎么调用so文件

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》