问题描述
Spring的AOP是一个很强大的功能,就算不自行定义一些AOP,项目上也经常会使用到一些AOP注解,例如事务控制的 @Transactional
而AOP在service方法的内部调用的时,无法触发进入切面处理。
代码示例:
/**
* 测试Demo service impl
*
* @author azhuzhu 2020/7/11 14:20.
*/
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DemoServiceImpl implements IDemoService {
private final DemoRepository demoRepository;
@Override
@Transactional(rollbackFor = Exception.class)
public void insertDemo(Demo demo) {
demoRepository.insert(demo);
throw new RuntimeException();
}
@Override
public void doSomething() {
this.insertDemo(new Demo());
}
}
测试代码:
@RunWith(SpringRunner.class)
@SpringBootTest
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DemoApplicationTests {
private final IDemoService demoService;
@Test
public void internalCall() {
// 内部调用,抛出异常无法触发事务的回滚
demoService.doSomething();
}
@Test
public void serviceCall() {
// service调用,触发异常回滚插入的数据
demoService.insertDemo(new Demo());
}
}
此时如果为了触发AOP另起一个service,代码就略显臃肿。
解决方案
既然内部调用无法触发,我们需要获取到bean去调用,如果直接在 DemoServiceImpl内定义 IDemoService 会因为循环引用无法启动。
所以我们需要直接从容器获取bean,而Spring提供了获取自身bean代理的方法 AopContext.currentProxy()
:
/**
* 测试Demo service impl
*
* @author azhuzhu 2020/7/11 14:20.
*/
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DemoServiceImpl implements IDemoService {
private final DemoRepository demoRepository;
@Override
@Transactional(rollbackFor = Exception.class)
public void insertDemo(Demo demo) {
demoRepository.insert(demo);
throw new RuntimeException();
}
@Override
public void doSomething() {
((IDemoService)AopContext.currentProxy()).insertDemo(new Demo());
}
}
优雅的解决
如果项目上经常用到内部调用的AOP触发,我们可以更优雅一点,定义一个统一的接口:
/**
* 通用自身代理获取
*
* @author azhuzhu 2020/7/11 14:40.
*/
public interface AopProxy<T> {
@SuppressWarnings("unchecked")
default T self() {
return (T) AopContext.currentProxy();
}
}
需要用到内部调用AOP的,接口继承 AopProxy<T>
:
/**
* 测试服务
*
* @author azhuzhu 2020/7/11 14:19.
*/
public interface IDemoService extends AopProxy<IDemoService> {
/**
* AOP事务处理插入数据
*
* @param demo 测试数据
*/
void insertDemo(Demo demo);
/**
* 内部调用测试插入数据
*/
void doSomething();
}
实现方法调用 self()
获取代理:
/**
* 测试Demo
*
* @author azhuzhu 2020/7/11 14:20.
*/
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DemoServiceImpl implements IDemoService {
private final DemoRepository demoRepository;
@Override
@Transactional(rollbackFor = Exception.class)
public void insertDemo(Demo demo) {
demoRepository.insert(demo);
throw new RuntimeException();
}
@Override
public void doSomething() {
this.self().insertDemo(new Demo());
}
}