Spring 核心註解介紹

Spring是一款輕量級的IOC框架,Spring的核心就是Ioc和DI,並通過倆者解耦。Ioc(Inversion of control)控制反轉,可以把創建對象和查找依賴對象的權限交給Ioc容器控制,而不是傳統的由這些對象的使用方(消費者)進行創建初始化操作。IoC是一種讓服務消費者不直接依賴於服務提供者的組件設計方式,是一種減少類與類之間依賴的設計原則。DI(Dependency Injection)依賴注入,指容器複製創建和維護對象之間的依賴關係,而不是通過對象本身複製自己的創建和解決自己的依賴。控制反轉是通過依賴注入實現的。

1 概述

在Spring中可通過一些註解(Annotation)來使用Spring DI引擎功能,它們定義在org.springframework.beans.factory.annotation 和 org.springframework.context.annotation包中。我們通常將這些稱爲“Spring核心註解”,本文將對它們進行介紹。

2 DI相關注解

2.1 @Autowired

我們可以使用@Autowired來標記Spring將要解析和注入的依賴項。有三種注入方式:構造函數注入,setter注入或字段注入。

構造函數注入:

class Car {
    Engine engine;
 
    @Autowired
    Car(Engine engine) {
        this.engine = engine;
    }
}

setter注入

class Car {
    Engine engine;
 
    @Autowired
    void setEngine(Engine engine) {
        this.engine = engine;
    }
}

Field注入

class Car {
    @Autowired
    Engine engine;
}

@Autowired有一個名爲required的布爾參數,默認值爲true。當它沒有找到合適的bean來連接時,Spring 容器將拋出 BeanCreationException 異常。如果爲false,表示忽略當前要注入的bean,如果有直接注入,沒有跳過,不會報錯。

注意,如果我們使用構造函數注入,則所有構造函數參數都是必需的。

從版本4.3開始,除非我們聲明至少兩個構造函數,否則我們不需要顯式地使用@Autowired註解構造函數。

2.2 @Bean

@Bean標記了一個實例化Spring bean的工廠方法:

@Bean
Engine engine() {
    return new Engine();
}

當需要返回類的新實例時,Spring會調用這些方法。

默認情況下bean的名稱和方法名稱相同,你也可以使用name屬性來指定:

@Bean(name="myEngine")
Engine engine() {
    return new Engine();
}

請注意,所有使用@Bean註釋的方法都必須在@Configuration類中。

2.3 @Qualifier

使用@Qualifier和@Autowired來提供我們想要在不明確的情況下使用的bean id或bean名稱。

例如,以下兩個bean實現相同的接口:

class Bike implements Vehicle {}
 
class Car implements Vehicle {}

如果Spring需要注入一個Vehicle bean,它最終會有多個匹配的定義。在這種情況下,我們可以使用@Qualifier註釋顯式提供bean的名稱。

使用構造函數注入:

@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

使用setter注入:

@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
    this.vehicle = vehicle;
}

使用Field注入:

@Autowired
@Qualifier("bike")
Vehicle vehicle;

2.4 @Required

@Required註解適用於bean屬性setter方法,並表示受影響的bean屬性必須在XML配置文件在配置時進行填充。否則,容器會拋出一個BeanInitializationException異常。

@Required
void setColor(String color) {
    this.color = color;
}

<bean class="com.peterwanghao.annotations.Bike">
    <property name="color" value="green" />
</bean>

2.5 @Value

可以使用@Value將屬性值注入bean。它有三種方式:構造函數,setter和字段注入。

構造函數注入:

Engine(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Setter注入:

@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}
或
@Value("8")
void setCylinderCount(int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Field注入:

@Value("8")
int cylinderCount;

當然,注入靜態值是沒有用的。因此,可以使用@Value中的佔位符字符串來連接外部資源中定義的值,例如,在.properties或.yaml文件中定義的值。

我們假設.properties文件中定義了:

engine.fuelType=petrol

我們可以使用以下內容注入engine.fuelType的值:

@Value("${engine.fuelType}")
String fuelType;

即使使用SpEL,我們也可以使用@Value。

2.6 @DependsOn

指定Spring容器初始化當前Bean之前先初始化所依賴的Bean。

當依賴項是隱式時,我們只需要這個註釋,例如,JDBC驅動程序加載或靜態變量初始化。我們可以在依賴類上使用@DependsOn來指定依賴關係bean的名稱。

@DependsOn("engine")
class Car implements Vehicle {}

2.7 @Lazy

一般情況下,Spring容器在啓動時會創建所有的Bean對象,使用@Lazy註解可以將Bean對象的創建延遲到第一次使用Bean的時候。

這個註解會根據放置的位置產生不同的效果:

  • 與@Bean放在一起,用於延遲方法調用
  • 與@Configuration放在一起,影響類中所有@Bean的方法
  • 與@Component放在一起,這個bean將會懶初始化
  • 與@Autowired放在一起,將懶加載依賴項

此註解具有名爲value的參數,其默認值爲true。當爲false時,代表不延遲立刻加載。

@Configuration
@Lazy
class VehicleFactoryConfig {
 
    @Bean
    @Lazy(false)
    Engine engine() {
        return new Engine();
    }
}

2.8 @Lookup

@Lookup註解是一個作用在方法上的註解,被其標註的方法會被重寫,然後根據其返回值的類型,容器調用BeanFactory的getBean()方法來返回一個bean。

2.9 @Primary

有時我們需要定義多個相同類型的bean。在這種情況下,注入將失敗,因爲Spring不知道我們需要哪種bean。

我們已經看到了一個處理這種情況的方法:用@Qualifier標記所有連接點並指定所需bean的名稱。

但是,大多數時候我們需要一個特定的bean而很少需要其他bean。我們可以使用@Primary來簡化這種情況:如果我們用@Primary標記最常用的bean,它將在不確定的情況下作爲首選:

@Component
@Primary
class Car implements Vehicle {}
 
@Component
class Bike implements Vehicle {}
 
@Component
class Driver {
    @Autowired
    Vehicle vehicle;
}
 
@Component
class Biker {
    @Autowired
    @Qualifier("bike")
    Vehicle vehicle;
}

在前面的例子中,Car是主要的載體。因此,在Driver類中,Spring注入了一個Car bean。當然,在Biker bean中,指定了值將是一個Bike對象。

2.10 @Scope

spring中scope是一個非常關鍵的概念,簡單說就是對象在spring容器(IOC容器)中的生命週期,也可以理解爲對象在spring容器中的創建方式。

目前,scope的取值有5種取值:

  • 在Spring 2.0之前,有singleton和prototype兩種;
  • 在Spring 2.0之後,爲支持web應用的ApplicationContext,增強另外三種:request,session和global session類型,它們只實用於web程序,通常是和XmlWebApplicationContext共同使用。
@Component
@Scope("prototype")
class Engine {}

3 上下文配置註釋

3.1 @Profile

們可以在不同的環境中激活不同的配置文件來引導我們需要的bean。使用@Profile註釋 - 我們將bean映射到該特定的配置文件。

假設,一個應用的工作環境有:dev、test、prod。我們有一個bean,它應該只在開發期間處於活動狀態,但不會在生產中部署。我們使用“ dev ”配置文件註解該bean ,並且它只會在開發期間出現在容器中 - 在生產中,dev將不會處於活動狀態:

@Component
@Profile("dev")
public class DevDatasourceConfig

3.2 @Import

導入資源。在應用中,有時沒有把某個類注入到IOC容器中,但在運用的時候需要獲取該類對應的bean,此時就需要用到@Import註解。

@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

3.3 @ImportResource

使用此註解導入XML配置。我們可以使用locations參數或其別名value參數指定XML文件位置:

@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}

3.4 @PropertySource

使用此註解,我們可以爲應用程序設置定義的屬性文件,將properties配置文件中的值存儲到Spring的Environment中:

@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}

@PropertySource引用了Java 8重複註解功能,這意味着我們可以多次使用它標記一個類:

@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}

3.5 @PropertySources

我們可以使用此註解指定多個@PropertySource配置:

@Configuration
@PropertySources({ 
    @PropertySource("classpath:/annotations.properties"),
    @PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

注意,自Java 8以來,我們可以通過如上所述的重複註解功能實現相同的功能。

4 結論

在本文中,我們看到了最常見的Spring核心註解的介紹。我們瞭解瞭如何配置bean和應用程序上下文,以及如何爲組件掃描標記類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章