如何使用AOP对Runnable实现类做增强且不侵入原有业务代码?
实现方案(全程新增代码,无原有代码侵入)
这里提供两种可行方案,均不需要修改任何原有业务代码:
方案一:加载时织入(LTW)方案(推荐,适配内部调用场景)
该方案基于类加载期字节码织入实现,不需要把目标类交给Spring托管,也不受类内部方法调用的限制,完全匹配你的需求:
步骤1:开启LTW支持
在项目启动类新增@EnableLoadTimeWeaving注解:
import org.springframework.context.annotation.EnableLoadTimeWeaving; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableLoadTimeWeaving public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
步骤2:新增AOP切面
新增切面类实现前置增强逻辑:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Aspect @Component @Slf4j public class DaemonThreadAspect { // 切点匹配所有MessageRunnable实现类的doConsume方法,替换为你实际的包路径即可 @Pointcut("execution(* com.yourpackage.MessageRunnable.doConsume(..))") public void doConsumePointcut() {} @Before("doConsumePointcut()") public void beforeDoConsume() { log.info("now I'm in the aspect method"); } }
步骤3:配置启动参数
应用启动时添加spring-instrument的代理参数:
-javaagent:./lib/spring-instrument-对应版本号.jar
如果是Spring Boot项目,也可以直接引入spring-boot-starter-aop依赖,自动处理LTW相关配置。
方案二:Spring托管+实例替换方案
如果不想调整启动参数,可以通过将目标类纳入Spring托管,再替换原有TestComponent中的非托管实例实现:
步骤1:新增配置类注册目标Bean
新增配置类将MyDaemonThread注册到Spring容器:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; // 开启CGLIB代理并暴露代理对象,解决内部调用拦截问题 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) @Configuration public class DaemonThreadConfig { @Bean public MyDaemonThread myDaemonThread() { return new MyDaemonThread(); } }
步骤2:新增BeanPostProcessor替换原有实例
通过反射替换TestComponent中自行实例化的非托管对象为Spring代理对象:
import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; import java.lang.reflect.Field; @Component public class TestComponentPostProcessor implements BeanPostProcessor { @Autowired private MyDaemonThread myDaemonThread; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof TestComponent)) { return bean; } try { Field testThreadField = TestComponent.class.getDeclaredField("testThread"); testThreadField.setAccessible(true); // 中断原有非托管线程避免重复执行 MyDaemonThread oldInstance = (MyDaemonThread) testThreadField.get(bean); if (oldInstance != null) { // 可根据业务需要补充旧线程的优雅下线逻辑 } // 替换为Spring代理的实例并启动 testThreadField.set(bean, myDaemonThread); new Thread(myDaemonThread).start(); } catch (Exception e) { e.printStackTrace(); } return bean; } }
步骤3:新增AOP切面
切面代码和方案一完全一致。
内容的提问来源于stack exchange,提问作者william
相关产品推荐
相关产品推荐

