Spring调用指定业务方法后创建Bean并触发AOP前置通知的实现咨询
问题根因
- 类内部调用方法不触发AOP:Spring AOP基于动态代理实现,只有通过代理对象调用被切方法时才会触发切面逻辑,同一个类内部直接调用方法走的是原对象
this,绕过了代理,因此切面不生效。 @Bean注解不符合时序要求:@Bean用于向Spring容器注册Bean实例,标注在方法上时Spring容器初始化阶段就会执行该方法获取返回值注册为Bean,和业务逻辑执行完成后再调用方法的需求冲突。
解决方案
不需要动态注册Bean,调整现有代码即可满足需求:
1. 调整业务服务类代码
移除generateSimulation方法上的@Bean注解,通过代理对象调用该方法触发AOP:
@RequiredArgsConstructor @Service public class Simulation_service { private final SimulationRepository simulationRepository; private final SimulationsValuesRepository simulationsValuesRepository; // 懒注入自身代理,避免循环依赖 @Lazy private final Simulation_service selfProxy; public void Simulation(String N, Long P, Long I, Double R, Double M, Long Ti, Long Tm, Long Ts) { var simulation = Simulation.builder() .simulation_Name(N) .population_Size(P) .initial_infected_number(I) .how_many_one_infects(R) .mortality_Rate(M) .number_of_days_to_recovery(Ti) .number_of_days_to_Death(Tm) .simulation_time(Ts) .build(); simulationRepository.save(simulation); // 调用代理对象方法触发AOP selfProxy.generateSimulation(); } @MyAnnotation public void generateSimulation() { for (int i = 2; i <= simulationRepository.getById(1L).getSimulation_time(); i++) { var simulation_values = SimulationsValues.builder() .healthy_prone_to_infection(simulationsValuesRepository.getById((long) i - 1) .getHealthy_prone_to_infection() - simulationsValuesRepository.getById((long) i - 1) .getNumber_of_infected() - simulationsValuesRepository.getById((long) i - 1) .getRegained_health_and_immunity() - simulationsValuesRepository.getById((long) i - 1).getDead()) .regained_health_and_immunity(0L) .number_of_infected((long) (simulationsValuesRepository.getById((long) i - 1) .getNumber_of_infected() * simulationRepository.getById((long) i - 1).getHow_many_one_infects())) .dead(0L) .build(); simulationsValuesRepository.save(simulation_values); } } }
可选方案:通过AopContext获取代理
如果不想注入自身代理,可以修改启动类的@EnableAspectJAutoProxy注解开启代理暴露:
@EnableAspectJAutoProxy(exposeProxy = true) @EntityScan("Terative.Entity") @EnableJpaRepositories("Terative.Repositories") @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
然后把Simulation方法里的调用逻辑替换为:
((Simulation_service) AopContext.currentProxy()).generateSimulation();
验证逻辑
调整后业务执行时序完全符合要求:控制器调用Simulation方法 → 保存模拟数据到数据库 → 调用代理对象的generateSimulation方法 → 触发@Before切面写入第一天的模拟参数 → 执行generateSimulation循环写入后续天数的模拟数据。
内容的提问来源于stack exchange,提问作者Krzysiek
相关产品推荐
相关产品推荐

