先上程式碼:
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:
<?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>基本程式大致如上,到這邊程式是沒辦法正常執行的,因為還沒設定Spring bean 注入方式。
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:
<?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,讓beans.xml中存在兩個Cat 類別,id 分別為 bigCat 與smallCat,在 runtime 時Spring 會拋出 Exception,因為無法判斷要注入哪一個Cat,當然我們可以將變數 cat 改名為 smallCat或是 bigCat就可以排除問題(autowired byName),但這邊嘗試用 Qualifier解決。
我們可以在 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:
<?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>將 beans.xml調回最初的設定。
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課程後所做的一些筆記,如果有興趣,可以參考這門課程。











沒有留言:
張貼留言