New 的對象也能被 Spring 注入?AspectJ LTW 打破限制!
環(huán)境:SpringBoot3.4.2
1. 簡介
在傳統(tǒng) Spring 應(yīng)用中,依賴注入(DI)僅限于由 Spring 容器管理的 Bean。然而,在實際開發(fā)中,常遇到需通過 new 關(guān)鍵字直接創(chuàng)建對象的場景,如領(lǐng)域?qū)嶓w、DTO 轉(zhuǎn)換、工廠模式或第三方庫回調(diào)。這些對象脫離了 Spring 容器的生命周期,無法使用 @Autowired 注入服務(wù),導(dǎo)致業(yè)務(wù)邏輯與數(shù)據(jù)訪問耦合,難以測試與維護。
為此,Spring 提供了基于 AspectJ 加載期織入(LTW)的解決方案。通過 @Configurable 注解與 META-INF/aop.xml 配置,結(jié)合 JVM -javaagent 機制,可在類加載時動態(tài)織入依賴注入邏輯,使 new 出的對象也能自動裝配 Spring Bean。該技術(shù)突破容器邊界,實現(xiàn)了真正意義上的全鏈路依賴注入,為復(fù)雜場景下的對象管理提供了強大支持。
本篇文章會詳細介紹LTW技術(shù)的完整實現(xiàn)過程。
2.實戰(zhàn)案例
2.1 引入Aspect依賴
要啟用基于 AspectJ 的配置,需要添加 AspectJ Weaver 依賴項。
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
</dependency>引入 spring-aspects 依賴以支持 @Configurable 等注解的織入,提供 AspectJ 與 Spring 集成的切面實現(xiàn),是實現(xiàn)加載期織入(LTW)和自動依賴注入的基礎(chǔ)。
2.2 開啟非管理Bean注入功能
@SpringBootApplication
@EnableSpringConfigured
@EnableLoadTimeWeaving
public class App {
}- @EnableSpringConfigured:注解向當(dāng)前應(yīng)用上下文發(fā)出信號,以將依賴注入應(yīng)用于那些在Spring Bean工廠之外實例化的非托管類(通常是使用@Configurable注解標注的類)。
 - @EnableLoadTimeWeaving:注解的作用是啟用 Spring 的加載期織入(Load-Time Weaving, LTW)機制,允許在類加載時由 AspectJ 對字節(jié)碼進行增強,從而實現(xiàn)對 new 創(chuàng)建的對象(如標記了 @Configurable 的類)進行依賴注入,突破 Spring 容器的管理邊界。
 
2.3 定義不受Spring管理的Bean
@Configurable
public class CommonService {
  @Resource
  private UserService userService ;
  public void getUser() {
    this.userService.query() ; 
  }
}@Configurable 注解指示 Spring 應(yīng)通過 AspectJ 加載期織入(LTW)機制,對該類的實例進行依賴注入。在該示例中,CommonService 類可以通過 new 關(guān)鍵字創(chuàng)建,并借助 @Configurable,Spring 仍能為其注入所需的 Bean。該類中通過 @Resource 注解注入了由 Spring 容器管理的 UserService Bean,使得即使脫離容器直接實例化,也能正常使用 userService.query() 等業(yè)務(wù)方法,實現(xiàn)與 Spring 管理對象的無縫集成。
定義UserService受管理的Bean
@Service
public class UserService {
  public void query() {
    System.err.println("查詢用戶信息...") ;
  }
}2.4 測試new創(chuàng)建CommonService
接下來,我們直接通過定義Runner進行測試,通過new創(chuàng)建CommonService后測試是否可以正確的注入UserService。
@Component
public class TestRunner implements CommandLineRunner {
  @Override
  public void run(String... args) throws Exception {
    CommonService cs = new CommonService() ;
    cs.getUser() ;
  }
}2.5 啟用AspectJ織入
要使 @EnableSpringConfigured 生效,必須啟用 AspectJ 織入。這可以在運行時或編譯時完成。
運行時織入,添加 spring-instrument 依賴,并將 AspectJ 織入器作為 Java Agent 啟用。
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-instrument</artifactId>
</dependency>2.6 啟動應(yīng)用測試
在完成以上步驟后,啟動應(yīng)用,你將看到如下的錯誤:
圖片
錯誤信息非常明確的告訴你還需要使用-javaagent:spring-instrument-xxx.jar參數(shù)。
-javaagent:D:\java\maven\org\springframework\spring-instrument\6.2.7\spring-instrument-6.2.7.jar再次啟動應(yīng)用,這次不會再有錯誤,但是將有一堆如下信息:
圖片
一堆error日志,并且我們程序也正確的運行了。
解決error日志
我們可以在META-INF下新建aop.xml文件,進行相關(guān)的配置,如下:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
  <weaver options="-Xlint:ignore">
    <include within="com.pack..*"/>
  </weaver>
</aspectj>- <weaver optinotallow="-Xlint:ignore">:啟用 AspectJ 織入器,并忽略所有織入過程中的警告信息(如無法找到類、注解等),避免日志污染,但需確保忽略不會掩蓋關(guān)鍵問題。
 - <include within="com.pack..*"/>:指定僅對 com.pack 包及其子包下的類進行織入處理,提高性能并避免對無關(guān)類(如第三方庫)進行不必要的織入。
 
2.7 添加增強功能
我們已經(jīng)使用了 META-INF/aop.xml 文件,但它不僅僅用于簡單配置(如忽略警告信息),還可以在其中定義切面織入規(guī)則,實現(xiàn)對目標類的 AOP 增強,從而擴展功能。
新建切面
@Aspect
public class ProfilingAspect {
  @Around("methodsToBeProfiled()")
  public Object profile(ProceedingJoinPoint pjp) throws Throwable {
    StopWatch sw = new StopWatch(getClass().getSimpleName());
    try {
      sw.start(pjp.getSignature().getName());
      return pjp.proceed();
    } finally {
      sw.stop();
      System.err.println(sw.prettyPrint());
    }
  }
  @Pointcut("execution(public * com.pack..*.*(..))")
  public void methodsToBeProfiled() {
  }
}修改aop.xml文件
<aspectj>
  <weaver options="-Xlint:ignore">
    <include within="com.pack..*"/>
  </weaver>
  <aspects>
    <aspect name="com.pack.mgr.aspect.ProfilingAspect"/>
  </aspects>
</aspectj>再次啟動應(yīng)用,控制臺輸出:

圖片















 
 
 






 
 
 
 