知識庫 / Spring RSS 訂閱

將 Spring Bean 注入非管理對象

Spring
HongKong
4
12:54 PM · Dec 06 ,2025

1. 驅動因素

在 Spring 應用中,將一個 Bean 注入到另一個 Bean 中非常常見。然而,有時希望將 Bean 注入到普通對象中也是一種需求。例如,我們可能希望從實體對象中獲取服務引用。

幸運的是,實現這一點並不像看起來那麼困難。以下部分將介紹如何使用 `@Configurable</a title> 註解和 AspectJ 編織器來實現。

2. @Configurable 註解

此註解允許被裝飾的類實例持有指向 Spring beans 的引用。

2.1. 定義和註冊 Spring Bean

在介紹 @Configurable<//em/> 註解之前,我們先設置一個 Spring Bean 定義:

@Service
public class IdService {
    private static int count;

    int generateId() {
        return ++count;
    }
}

此類被裝飾了 @Service 註解;因此,可以通過組件掃描將其註冊到 Spring 上下文中。

這是一個簡單的配置類,啓用該機制:

@ComponentScan
public class AspectJConfig {
}

2.2. 使用 @Configurable

在最簡單的形式下,我們可以直接使用 @Configurable 而無需任何元素:

@Configurable
public class PersonObject {
    private int id;
    private String name;

    public PersonObject(String name) {
        this.name = name;
    }

    // getters and other code shown in the next subsection
}

@Configurable 註解在此處,標記了 PersonObject 類為可供 Spring 驅動進行配置的類。

2.3. 將 Spring Bean 注入到非管理對象中

我們可以將 IdService 注入到 PersonObject 中,就像在任何 Spring Bean 中一樣:

@Configurable
public class PersonObject {
    @Autowired
    private IdService idService;

    // fields, constructor and getters - shown in the previous subsection

    void generateId() {
        this.id = idService.generateId();
    }
}

然而,一個標註只有在被識別和處理時才有用。這正是 AspectJ 編織器發揮作用的地方。具體來説,AnnotationBeanConfigurerAspect 將作用於 @Configurable 的存在,並進行必要的處理。

3. 啓用 AspectJ 織入

This section describes how to enable AspectJ weaving in your project. AspectJ weaving is the process of inserting AspectJ code into your application code at compile time. This allows you to modify the behavior of your code without changing the source code itself.

Prerequisites:

  • You must have AspectJ installed and configured on your system.
  • You must have a build tool (e.g., Maven, Gradle) configured to execute AspectJ weaving.

Steps:

  1. Add the AspectJ Dependency: Add the AspectJ dependency to your project's build file (e.g., pom.xml for Maven, build.gradle for Gradle). This makes the AspectJ libraries available to your project.

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.2</version>  <!-- Use the latest version -->
    </dependency>
    
  2. Configure the Weaving Plugin: Configure your build tool to execute AspectJ weaving. The specific steps will vary depending on your build tool. For example, in Maven, you would typically add a plugin to your pom.xml file.

  3. Create AspectJ Source Files: Create AspectJ source files (.aj files) that contain your AspectJ code. These files will be woven into your application code.

    // Example AspectJ code
    @Aspect
    public class MyAspect {
        @Pointcut("execution(* com.example.myapp.MyClass.*)")
        public void myPointcut() {}
    
        @Advice(pointcut = "myPointcut()", adviceType = Execution.RUNTIME)
        public void doSomething() {
            System.out.println("Executing doSomething()");
        }
    }
    
  4. Run the Build: Execute your build process. The build tool will compile your application code and weave in the AspectJ code.

Important Considerations:

  • Classpath: Ensure that the AspectJ libraries are included in your application's classpath.
  • AspectJ Version: Use the latest version of AspectJ to benefit from bug fixes and new features.
  • Testing: Thoroughly test your application after enabling AspectJ weaving to ensure that the weaving process did not introduce any unexpected behavior.

3.1. 插件聲明

為了啓用 AspectJ 織入,首先需要使用 AspectJ Maven 插件

<plugin>
    <groupId>dev.aspectj</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.13.1</version>
    <!-- configuration and executions -->
</plugin>

並且需要進行一些額外的配置:

<configuration>
    <complianceLevel>17</complianceLevel>
    <aspectLibraries>
        <aspectLibrary>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </aspectLibrary>
    </aspectLibraries>
</configuration>

第一個必需的元素是 complianceLevel。值為 17 將源 JDK 和目標 JDK 版本都設置為 17

要將 Bean 注入到未管理對象中,我們必須依賴 AnnotationBeanConfigurerAspect 類,該類位於 spring-aspects.jar 中。由於這是一個預編譯的方面,因此我們需要 將包含的 Artifact 添加到插件配置中。

請注意,此類引用的 Artifact 必須作為項目的依賴存在:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.1.5</version>
</dependency>

我們可以在 Maven Central 找到 spring-aspects 的最新版本。

3.2. 插件執行

為了指示插件編織所有相關類,我們需要以下 executions 配置:

<executions>
    <execution>
        <goals>
            <goal>compile</goal>
        </goals>
    </execution>
</executions>

請注意,該插件的 編譯 目標默認綁定到編譯生命週期階段。

3.3. Bean 配置

要啓用 AspectJ 織入,最後的步驟是@EnableSpringConfigured 添加到配置類中:

@ComponentScan
@EnableSpringConfigured
public class AspectJConfig {
}

額外的標註配置項會配置 AnnotationBeanConfigurerAspect,進而將 PersonObject 實例註冊到 Spring IoC 容器中。

4. 測試

現在,讓我們驗證 IdService 組件是否已成功注入到 PersonObject 對象中:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AspectJConfig.class)
public class PersonUnitTest {
    @Test
    public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() {
        PersonObject personObject = new PersonObject("Baeldung");
        personObject.generateId();
        assertEquals(1, personObject.getId());
        assertEquals("Baeldung", personObject.getName());
    }
}

5. 將 Bean 注入到 JPA 實體中

從 Spring 容器的角度來看,實體僅僅是一個普通的對象。因此,將 Spring Bean 注入到 JPA 實體中沒有任何特殊之處。

然而,由於將 Bean 注入到 JPA 實體中是一種常見的用例,因此我們將更詳細地進行説明。

5.1. 實體類

讓我們從實體類的基本框架開始:

@Entity
@Configurable(preConstruction = true)
public class PersonEntity {
    @Id
    private int id;
    private String name;

    public PersonEntity() {
    }

    // other code - shown in the next subsection
}

請注意 <em preConstruction</em> 元素在 <em @Configurable</em> 註解中:它允許我們在對象完全構造之前注入依賴項。

5.2. 服務注入

現在我們可以將 IdService 注入到 PersonEntity 中,類似於我們注入 PersonObject 的方式:

// annotations
public class PersonEntity {
    @Autowired
    @Transient
    private IdService idService;

    // fields and no-arg constructor

    public PersonEntity(String name) {
        id = idService.generateId();
        this.name = name;
    }

    // getters
}

@Transient 註解用於告知 JPA idService 是一個不應被持久化的字段。

5.3. 測試方法更新

最後,我們可以更新測試方法,以指示服務可以注入到實體中:

@Test
public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() {
    // existing statements

    PersonEntity personEntity = new PersonEntity("Baeldung");
    assertEquals(2, personEntity.getId());
    assertEquals("Baeldung", personEntity.getName());
}

6. 備註

雖然從非管理對象訪問 Spring 組件可能方便,但通常不建議這樣做。

問題在於,非管理對象,包括實體,通常是域模型的一部分。這些對象應該只攜帶用於在不同服務之間重用的數據。

將 Bean 注入到這些對象中可能會將組件和對象綁定在一起,從而使應用程序更難維護和增強。

7. 結論

本教程詳細介紹了將 Spring Bean 注入到非管理對象中的過程。同時,還討論了依賴注入到對象中的相關設計問題。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.