Spring Framework ‐ Annotation - CCH0124/spring-sandbox GitHub Wiki

  • @SpringBootApplication
  • @RestController
  • @Autowired
  • @Value
  • @Enable
  • @Configuration
  • @Bean

DI 相關註釋

  1. @Autowired 可以使用 @Autowired 來標記,Spring 將解析和注入的依賴項。

Constructor injection

@Controller
public class CarConstructorController {

    private CarService carService;
    
    @Autowired
    CarConstructorController(CarService carService) {
        this.carService = carService;
    }
}

Setter injection

@Controller
public class CarSetterController {

    private CarService carService;
    
    @Autowired
    void setCarService(CarService  carService) {
        this.carService = carService;
    }
}

Field injection

@Controller
public class CarController {
    @Autowired
    private CarService carService;

    public void get() {
        carService.out();
        System.out.println("Controller Layer");
    }
}

Arg Injection

@Controller
public class CarArgController {

    private CarService carService;
    
    public CarArgController(@Autowired CarService carService) {
        this.carService = carService;
    }
}
  1. @Bean

在 Spring 中,構成應用程式主要架構,並由 Spring IoC 容器管理的物件稱為 Bean。 Bean 是 Spring IoC 容器實例化、組裝和管理的物件。

此註釋可作用於方法。@Bean 是一個實例化 Spring bean 的工廠方法

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	/**
	 * Alias for {@link #name}.
	 * <p>Intended to be used when no other attributes are needed, for example:
	 * {@code @Bean("customBeanName")}.
	 * @since 4.3.3
	 * @see #name
	 */
	@AliasFor("name")
	String[] value() default {};
...
}
@Configuration
@ComponentScan(basePackageClasses = Vehicle.class)
public class Config {
    @Bean
    public Brand getBrand() {
        return new Brand("volvo", 1844);
    }
}

預設上產生的 Bean 與工廠方法具有相同的名稱。如果想以不同的方式命名它,可以使用 name 參數進行別名上的設定。

@Configuration
@ComponentScan(basePackageClasses = Vehicle.class)
public class Config {
    @Bean("brand")
    public Brand getBrand() {
        return new Brand("volvo", 1844);
    }
}

注意,所有用 @Bean 註解的方法都必須在 @Configuration 類別中

  1. @Qualifier

他可用於變數上、方法或是參數上的註解。基本上和 @Autowired 是搭配的。

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

	String value() default "";

}

透過 @Qualifier@Autowired 來提供想要在不明確的情況下使用的 Bean。

假設定義 Vehicle 介面,並由 BikeCar 來實作

@Component
public class Bike implements Vehicle{

	@Override
	public void printBrand() {
		System.out.println("Addmotor");
	}
    
}

@Component
public class Car implements Vehicle {

    @Override
    public void printBrand() {
        System.out.println("Volvo");
    }

}

如果 Spring 需要注入一個 Vehicle 的 Bean,它會得到多個符合的定義,會出現如下錯誤訊息。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'carController': Unsatisfied dependency expressed through field 'vehicle': No qualifying bean of type 'org.annotation.qualifier.Vehicle' available: expected single matching bean but found 2: bike,car

在這種情況下,我們可以使用 @Qualifier 註解明確提供 Bean 的名稱,如下這也表示每次對 Vehicle 進行注入都需要搭配 @Qualifier

@Controller
public class BikeController {
    @Autowired
    @Qualifier("bike")
    private Vehicle vehicle;

    public void get() {
        vehicle.printBrand();
        System.out.println("Bike Controller Layer");
    }    
}

@Controller
public class CarController {
    @Autowired
    @Qualifier("car")
    private Vehicle vehicle;

    public void get() {
        vehicle.printBrand();
        System.out.println("Car Controller Layer");
    }    
}
  1. @Value

可以使用 @Value 將屬性值注入到 Bean 中,它可用於建構函式、setter 和 Field 注入等。可以使用 @Value 中的定義的字串來與外部配置進行關聯(如 .properties 或 .yaml 檔案)中定義的值。

@Value("${car.type}")
String carType;

.properties 檔案可以如下定義

car.type=Volvo

@Value 可以設定預設值、陣列、系統變數或是 Map 來進行配置,彈性非常大。

  1. @DependsOn

這個註解讓 Spring 在被註解的 Bean 之前初始化其他 Bean。通常,這種行為是自動且要基於 Bean 之間的明確依賴關係。只有當依賴關係是隱式的時,例如 JDBC 驅動程式載入或靜態變數初始化,我們才需要此註解。

  1. @Configuration

它表明該類別具有 @Bean 定義方法。因此 Spring 容器可以處理該類別並產生要在應用程式中使用的 Bean。用此註解可取代 xml 宣告方式。

  1. @Component

當使用基於註解的配置和類別路徑掃描時,Spring 框架將自動偵測這些類別以進行依賴注入,並由 Spring 容器進行管理@Component 註解則是用來宣告一個普通的 Java 類別為Spring 中的物件(Bean)。

當 Spring 容器掃描到被 @Component 標記的類別時,會將其建立實例化並注入到 Spring 容器中,讓其他元件呼叫、依賴。以下的行為都是相同的,因為它們都是用 @Component 作為每個註釋的組合註釋

  • @Service
  • @Repository
  • @Controller

下面的 @Repository 包含了 @Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

	/**
	 * Alias for {@link Component#value}.
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

另一個 @ComponentScan,Spring 使用它將它們(@Component)收集到其 ApplicationContext 中。而 @SpringBootApplication 是一個包含 @ComponentScan 的組合註釋,只要 @SpringBootApplication 類別位於專案的根目錄下,它就會預設掃描我們定義的每個 @Component。前面提到是根目錄,如果不是可藉由配置 @ComponentScan 來尋找指定的任何套件。

與 @Bean 相比

  • @Component 是類別層級的註解,而 @Bean 是方法層級的,所以 @Component 只是當類別的原始程式碼可編輯時的一個選項。@Bean 是可以使用,但它更繁瑣。
  • @Component 與 Spring 的自動偵測相容,但 @Bean 需要手動類別實例化。
  • 使用 @Bean,將 bean 的實例化與其類別定義分開。這就是為什麼可以用它來將第三方類別製作成 Spring bean。這也表示可以引入邏輯來決定 bean 使用幾個可能的實例選項中的哪一個。
  1. @Lazy 用來想要延遲初始化 bean 時。在某些情況下,需要在請求時而不是在應用程式啟動時建立 bean。此 @Lazy 的行為有所不同,取決於確切放置它的位置。可以把它放在:
  • @Bean 的 Bean 工廠方法上,以延遲方法調用,延遲了 Bean 的創建
  • @Configuration 的類別上,所有包含的 @Bean 方法都會受影響
  • @Component 但不是 @Configuration 的類別上,此 Bean 將被延遲初始化
  • @Autowired 的建構函式、setter 或變數上,延遲加載依賴本身
  1. @Primary

當定義多個相同類型的 bean 時。在這些情況下,注入將不成功,因為 Spring 不知道我們需要哪一個 bean。但可以用 @Qualifier 來決定使用哪一個。如果需要一種特定的 bean,而很少需要其他的 bean,可以使用 @Primary 來簡易配置。