先上程式碼:
pom.xml:
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.springframework.example</groupId>
- <artifactId>spring-annotation-autowiring</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-beans</artifactId>
- <version>5.0.8.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>5.0.8.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>5.0.8.RELEASE</version>
- </dependency>
- </dependencies>
- </project>
Animal.java:
- package org.springframework.example;
- public interface Animal {
- public String makeSound();
- }
Cat.java:
- package org.springframework.example;
- public class Cat implements Animal{
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Meow Meow Meow~";
- }
- }
Dog.java:
- package org.springframework.example;
- public class Dog implements Animal{
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Woof Woof Woof~";
- }
- }
Pet.java:
- package org.springframework.example;
- public class Pet {
- private Cat cat;
- private Dog dog;
- public Pet(Cat cat, Dog dog) {
- this.dog = dog;
- this.cat = cat;
- }
- public Cat getCat() {
- return cat;
- }
- public void setCat(Cat cat) {
- this.cat = cat;
- }
- public Dog getDog() {
- return dog;
- }
- public void setDog(Dog dog) {
- this.dog = dog;
- }
- public void playWithCat() {
- System.out.println(this.cat.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
App.java:
- package org.springframework.example;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class App {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/example/beans/beans.xml");
- Pet pet = (Pet) context.getBean("pet");
- pet.playWithCat();
- pet.playWithDog();
- ((ClassPathXmlApplicationContext)context).close();
- }
- }
beans.xml:
基本程式大致如上,到這邊程式是沒辦法正常執行的,因為還沒設定Spring bean 注入方式。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="cat" class="org.springframework.example.Cat"></bean>
- <bean id="dog" class="org.springframework.example.Dog"></bean>
- <bean id="pet" class="org.springframework.example.Pet"></bean>
- </beans>
1. 透過 @Autowired 來自動綁定
@Autowired 會有一套自己的注入流程,一開始會先嘗試 byType,如果遇到 ambiguous的情況再改 byName的方式,最後都找不到匹配對象才會拋出Exception,不過後面會提到可以用更多的方式來幫助Spring找到適合的bean 做注入,減少這類 ambiguous的情況發生。要透過Annotation來做到自動綁定,在 beans.xml的 namespace需要啟用 context:
![]() |
beans.xml |
![]() |
beans.xml |
![]() |
加入後會自動在xml加入設定 |
實際上加入context:annotation-config是在Spring中註冊了AutowiredAnnotationBeanPostProcessor﹑ CommonAnnotationBeanPostProcessor﹑ PersistenceAnnotationBeanPostProcessor與RequiredAnnotationBeanPostProcessor,
目的是要讓程式可以是別不同的Annotation,如加入了AutowiredAnnotationBeanPostProcessor 後程式才可以識別 @Autowire annotation。
透過@Autowired可以有三種設定方式:
1. 透過 setter注入:
第一種方式可以在 setter上加上 @Autowired:
![]() |
Pet.java |
2. 透過 constructor 注入:
第二種方式是在 constructor 上加上 @Autowired:
![]() |
Pet.java |
3. 透過property注入:
第三種方式是直接在要注入的property上加上 @Autowired,而且不需要對應的 setter即可注入,這邊調整一下程式碼:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- public class Pet {
- @Autowired
- private Cat cat;
- @Autowired
- private Dog dog;
- public Cat getCat() {
- return cat;
- }
- public Dog getDog() {
- return dog;
- }
- public void playWithCat() {
- System.out.println(this.cat.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
程式的部分把 constructor與 setter移除掉,只在要注入的 property上加上annotation即可。
4. 混搭:
第四種就是結合前面三種混和使用,如透過 constructor與 property一起使用也是可行的:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- public class Pet {
- private Cat cat;
- @Autowired
- private Dog dog;
- @Autowired
- public Pet(Cat cat) {
- this.cat = cat;
- }
- public Cat getCat() {
- return cat;
- }
- public Dog getDog() {
- return dog;
- }
- public void playWithCat() {
- System.out.println(this.cat.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
使用 Autowired 時Spring 會檢查要注入的目標是否有正常的被注入,如果沒有,則會拋出Exception,假使要注入的目標並非一定要注入東西,可以在 @Autowired上加入 required = false, Spring 看到這個設定後,如果目標在注入時因為一些原因無法注入, Spring 就會忽略這個錯誤。
如:
- @Autowired(required = false)
- private Dog dog;
2. Qualifiers
使用Qualifiers可以有效幫助Spring 識別要注入的 bean,主要是在bean 定義上加上Qualifier資訊,這邊稍微調整程式,刻意產生一個 Spring 認為 ambiguous的情況:Pet.java:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- public class Pet {
- @Autowired
- private Cat cat;
- @Autowired
- private Dog dog;
- public Cat getCat() {
- return cat;
- }
- public Dog getDog() {
- return dog;
- }
- public void playWithCat() {
- System.out.println(this.cat.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
beans.xml:
這邊調整beans.xml,讓beans.xml中存在兩個Cat 類別,id 分別為 bigCat 與smallCat,在 runtime 時Spring 會拋出 Exception,因為無法判斷要注入哪一個Cat,當然我們可以將變數 cat 改名為 smallCat或是 bigCat就可以排除問題(autowired byName),但這邊嘗試用 Qualifier解決。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
- <bean id="smallCat" class="org.springframework.example.Cat"></bean>
- <bean id="bigCat" class="org.springframework.example.Cat"></bean>
- <bean id="dog" class="org.springframework.example.Dog"></bean>
- <bean id="pet" class="org.springframework.example.Pet"></bean>
- <context:annotation-config></context:annotation-config>
- </beans>
我們可以在 beans.xml 的bean上加上 qualifier 資訊:
![]() |
beans.xml |
![]() |
beans.xml |
接著在 Pet.java上也加入qualifier annotation,目的在告訴Spring要找qualifier 為 smallCat的 Cat類別:
![]() |
Pet.java |
如此一來,Spring就可以知道諸多的Cat類別中,要找smallCat類別作為注入來源。
除了在 beans.xml 中定義 qualifier,也可以在類別上直接定義一個 Qualifier,調整一下程式碼:
beans.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
- <bean id="smallCat" class="org.springframework.example.Cat"></bean>
- <bean id="dog" class="org.springframework.example.Dog"></bean>
- <bean id="pet" class="org.springframework.example.Pet"></bean>
- <context:annotation-config></context:annotation-config>
- </beans>
Pet.java
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- public class Pet {
- @Autowired
- @Qualifier("Cat")
- private Animal animal;
- @Autowired
- private Dog dog;
- public void playWithCat() {
- System.out.println(this.animal.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
原本為Cat 類別之變數,現改為Animal類別,而 Cat與 Dog皆繼承自 Animal 類別,我們在animal 變數上加上 Qualifier,指明 Cat,接著調整Cat類別:
Cat.java:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Qualifier;
- @Qualifier("Cat")
- public class Cat implements Animal{
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Meow Meow Meow~";
- }
- }
在Cat 類別上方加上 Qualifier,設定好後,在runtime Spring 就會知道要將Cat 注入到 animal變數中。
3. 使用 @Resource Annotation
Resource的定義在 JSR-250中,可以參考Wiki 上說明:JSR-250 ,會提到正是因為與 dependency有關。Resource Annotation 可以如同 Autowired Annotation一樣,做到 Dependency Injection,參考下列程式碼:
beans.xml:
將 beans.xml調回最初的設定。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
- <bean id="cat" class="org.springframework.example.Cat"></bean>
- <bean id="dog" class="org.springframework.example.Dog"></bean>
- <bean id="pet" class="org.springframework.example.Pet"></bean>
- <context:annotation-config></context:annotation-config>
- </beans>
Pet.java:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- import javax.annotation.Resource;
- public class Pet {
- @Resource(name = "cat")
- private Animal animal;
- @Autowired
- private Dog dog;
- public void playWithCat() {
- System.out.println(this.animal.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
這邊將 animal 注入的 @Autowired 改為 @Resource,使用 Resource 需要指明要注入的bean 名稱,如此即可在 animal注入 Cat 類別。
@Autowired 與 @Resource 做的事情是相同的,差異在於 Spring 在選擇注入來源時的順序,在這一篇文章中有不錯的解釋,簡單來說,
@Autowired 會先 byType -> Qualifier -> byName
@Resource 則是 byName -> byType -> Qualifier
4. 使用 @Inject
與 @Resource與 @Autowired 相似,都可以用來做 Spring 的 Dependency Injection, @Inject 定義於 JSR330,要使用 @Inject,需要在 pom.xml 上額外加入 javax.inject dependency:![]() |
pom.xml |
beans.xml 與前一個部分相同,這邊僅修改 Pet.java,直接看使用方式:
- package org.springframework.example;
- import javax.inject.Inject;
- import javax.inject.Named;
- import org.springframework.beans.factory.annotation.Autowired;
- public class Pet {
- @Inject
- @Named("cat")
- private Animal animal;
- @Autowired
- private Dog dog;
- public void playWithCat() {
- System.out.println(this.animal.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
正常來說在 animal 上加入 @Inject 即可,但因為這邊有 ambiguous的問提,所以我們可以加上 Named annotation,指定要用 id 為 cat 的bean,就可以正常的執行程式。
@Inject、@Resource 與 Autowired 做的事情都是相同的,只是使用上有些許不同,這邊僅記錄簡單的部分,實際深入還需要額外看說明。
5. 讓Spring 掃描可注入的Bean
到現在所有讓Spring注入的來源都是定義在 beans.xml中,這邊將介紹如何透過 annotation設定,讓 Spring 可以找尋特定目錄中可注入的 bean,省掉在 beans.xml 中定義的步驟:![]() |
beans.xml |
同時可以將 beans.xml 中所有 bean 定義都移除掉:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
- <context:component-scan
- base-package="org.springframework.example">
- </context:component-scan>
- </beans>
接著程式碼的部分:
Cat.java
- package org.springframework.example;
- import org.springframework.stereotype.Component;
- @Component(value = "cat")
- public class Cat implements Animal{
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Meow Meow Meow~";
- }
- }
Dog.java
- package org.springframework.example;
- import org.springframework.stereotype.Component;
- @Component
- public class Dog implements Animal{
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Woof Woof Woof~";
- }
- }
Pet.java
- package org.springframework.example;
- import javax.inject.Inject;
- import javax.inject.Named;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- @Component
- public class Pet {
- @Inject
- @Named("cat")
- private Animal animal;
- @Autowired
- private Dog dog;
- public void playWithCat() {
- System.out.println(this.animal.makeSound());
- }
- public void playWithDog() {
- System.out.println(this.dog.makeSound());
- }
- }
這邊簡單說明, 在類別上加上 @Component 就是讓Spring 知道這個類別可以作為 bean 注入的來源,而在 Component內定義 value 等同是在 beans.xml 中定義的 id。
因此原本在 beans.xml 中的3個 bean,只要在原本類別上加上 @Component,Spring 就會拿來作為注入來源,因此 beans.xml 中的定義就可以移除掉,執行後的結果會與先前相同。
6. 透過 annotation 注入 bean property
beans.xml 中可以設定 bean的 property value,annotation也可以,假使現在要在 Cat 類別上加上 name 屬性,並透過 annotation 設定內容,可以透過下列方式做到:Cat.java:
- package org.springframework.example;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
- @Component(value = "cat")
- public class Cat implements Animal {
- private String name;
- @Autowired
- public void setName(@Value(value = "Kitty") String name) {
- this.name = name;
- }
- public Object getName() {
- return this.name;
- }
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Meow Meow Meow~";
- }
- }
要做到 Property Injection,基本上要用 setter來做到,並透過 @Value annotation來設定注入內容,同時搭配上 @Autowired,即可以對 Cat 類別的 name 做注入。
如果要注入其他的 bean 到 Cat中,只要使用 Autowired 即可,如同 Pet.java 的作法。
7. Annotation 中的 init 與 destroy:@PostConstruct & @PreDestroy
透過 Annotation 要做到 bean 初始化時執行與結束時執行某些事情,可以在 method 前面分別加上@PostConstruct & @PreDestroy,分別對應到 beans.xml 中的 init-method與destroy-method。這邊在Cat類別上加上一個初始化後執行的 method 與結束時要做的 method:
- package org.springframework.example;
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
- import org.springframework.stereotype.Component;
- @Component(value = "cat")
- public class Cat implements Animal {
- public String makeSound() {
- // TODO Auto-generated method stub
- return "Meow Meow Meow~";
- }
- @PostConstruct
- public void init() {
- System.out.println("Cat init");
- }
- @PreDestroy
- public void destroy() {
- System.out.println("Cat destroy");
- }
- }
執行結果:
![]() |
執行結果 |
結語
Spring annotation 設定部分大致上到這邊,接著會試著使用Spring 的 SPEL,SPEL 也可以搭配著 bean的 dependenc injection使用。這邊都是上這門 udemy課程後所做的一些筆記,如果有興趣,可以參考這門課程。
沒有留言:
張貼留言