本文目录导读:

- 在同一个类中调用自己的方法(最基础)
- 使用Spring的
@Autowired注入自身(解决AOP失效) - 使用
ApplicationContext.getBean()获取代理对象 - 使用
AopContext.currentProxy()(Spring AOP) - 异步自调用(
@Async与自服务) - 纯Java中的“自服务”设计(不依赖框架)
- 循环自调用(递归)
- 总结:选择哪种方式?
在Java中实现“服务自服务”(即一个服务调用自身的方法或逻辑)通常有以下几种常见的场景和实现方式,根据具体的技术栈(如Spring、纯Java等)和业务需求,可以选择不同的方案。
以下是几种典型的实现方式:
在同一个类中调用自己的方法(最基础)
这是最直观的方式,直接调用类自己的方法,但需要注意事务、AOP代理等问题。
public class UserService {
public void processUser(Long userId) {
// 业务逻辑
System.out.println("处理用户: " + userId);
// 自服务:调用自己的另一个方法
this.sendNotification(userId); // 直接调用
}
public void sendNotification(Long userId) {
System.out.println("发送通知给用户: " + userId);
}
}
问题:如果使用了AOP(如@Transactional、@Cacheable等),直接通过this调用会导致代理失效,AOP切面不会执行。
使用Spring的@Autowired注入自身(解决AOP失效)
在Spring中,如果希望同一个类中的方法调用能触发AOP(如事务、缓存),需要注入自己(即代理对象)。
@Component
public class UserService {
@Autowired
private UserService self; // 注入代理对象
@Transactional
public void processUser(Long userId) {
System.out.println("处理用户: " + userId);
// 通过代理对象调用,事务/缓存等AOP生效
self.sendNotification(userId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW) // 独立事务
public void sendNotification(Long userId) {
System.out.println("发送通知给用户: " + userId);
// 如果有数据库操作,这里可以开启新事务
}
}
关键点:
- 注入
self(这个变量名可以随意,但推荐用self或thisProxy) - 所有需要走AOP的方法调用都必须通过
self.xxx(),而不是this.xxx() - 适用于Spring单例Bean
使用ApplicationContext.getBean()获取代理对象
如果不方便使用字段注入(比如在构造器中需要自调用),可以从ApplicationContext获取Bean。
@Component
public class UserService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Transactional
public void processUser(Long userId) {
UserService proxy = applicationContext.getBean(UserService.class);
proxy.sendNotification(userId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(Long userId) {
System.out.println("发送通知给用户: " + userId);
}
}
使用AopContext.currentProxy()(Spring AOP)
Spring AOP提供了AopContext.currentProxy()来获取当前代理对象,需要手动开启exposeProxy。
@Component
public class UserService {
@Transactional
public void processUser(Long userId) {
// 需要确保配置了 exposeProxy=true
UserService proxy = (UserService) AopContext.currentProxy();
proxy.sendNotification(userId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(Long userId) {
System.out.println("发送通知给用户: " + userId);
}
}
配置(如果是Spring Boot自动配置,需要手动开启):
# application.properties spring.aop.proxy-target-class=true spring.aop.expose-proxy=true
或者通过@EnableAspectJAutoProxy(exposeProxy = true)注解。
异步自调用(@Async与自服务)
当需要异步执行自己的方法时,也需要使用代理对象,否则异步不会生效。
@Component
public class NotificationService {
@Autowired
private NotificationService self;
public void sendBatchNotifications() {
// 异步调用自己的方法
self.sendSingleNotification("user@example.com");
System.out.println("异步任务已提交");
}
@Async
public void sendSingleNotification(String email) {
// 实际发送邮件...
System.out.println("异步发送邮件到: " + email);
}
}
纯Java中的“自服务”设计(不依赖框架)
如果不使用Spring,可以通过接口 + 实现类 + 手动代理来实现类似效果。
// 1. 定义接口
interface UserService {
void processUser(Long userId);
void sendNotification(Long userId);
}
// 2. 实现类
class UserServiceImpl implements UserService {
private UserService self; // 代理对象(由外部设置)
public void setSelf(UserService self) {
this.self = self;
}
@Override
public void processUser(Long userId) {
System.out.println("处理用户");
self.sendNotification(userId); // 通过代理调用
}
@Override
public void sendNotification(Long userId) {
System.out.println("发送通知");
}
}
// 3. 手动创建JDK动态代理
public class Main {
public static void main(String[] args) {
UserServiceImpl target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
Main.class.getClassLoader(),
new Class[]{UserService.class},
(obj, method, params) -> {
System.out.println("前置增强");
Object result = method.invoke(target, params);
System.out.println("后置增强");
return result;
}
);
target.setSelf(proxy); // 设置代理到自身
proxy.processUser(1L);
}
}
循环自调用(递归)
自服务”是指递归调用(如树形结构遍历),则直接使用递归即可,无需特殊处理。
public class MenuService {
public List<Menu> buildTree(List<Menu> allMenus, Long parentId) {
List<Menu> children = new ArrayList<>();
for (Menu menu : allMenus) {
if (menu.getParentId().equals(parentId)) {
menu.setChildren(buildTree(allMenus, menu.getId())); // 递归
children.add(menu);
}
}
return children;
}
}
选择哪种方式?
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 无事务/AOP需求,纯逻辑 | this.xxx() 直接调用 |
最简单 |
| Spring + 需要事务/缓存/AOP | @Autowired注入自身 |
最常用,清晰 |
异步自调用 (@Async) |
注入自身或AopContext |
确保异步生效 |
| 无法使用字段注入(如构造器) | ApplicationContext.getBean() |
灵活 |
| 不依赖Spring,纯Java | 手动代理 + 设置self | 完全控制 |
| 递归算法 | 递归调用 | 标准做法 |
最推荐的Spring实现:
@Service
public class OrderService {
@Autowired
private OrderService self; // 注入代理
@Transactional
public void placeOrder(Order order) {
// 订单逻辑
self.processPayment(order); // 走代理,事务AOP生效
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Order order) {
// 支付逻辑(独立事务)
}
}
这样既保持了代码的简洁性,又确保了框架特性(事务、AOP)的正确发挥。