這篇開始講annotation,基本的annotation不了解的可看這篇
※@Autowired
※XML設定
三個VO
public class Cartoon {
public Cartoon() {}
}
public class Comic {
public Comic() {}
}
public class Book {
private Cartoon cartoon;
private Comic comic;
// setter/getter...
public Book() {}
}
applicationContext.xml
<bean id="ca" class="vo.Cartoon" /> <bean id="co" class="vo.Comic" /> <bean id="bo" class="vo.Book" p:cartoon-ref="ca" p:comic-ref="co" />
測試類
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 總共有幾個bean
System.out.println(ctx.getBeanDefinitionCount());
// 將全部的bean名稱印出來
for (String s : ctx.getBeanDefinitionNames()) {
System.out.println(s);
}
// 兩種寫法都可以
Book book1 = ctx.getBean("bo", Book.class);
Book book2 = (Book) ctx.getBean("bo");
System.out.println(book1.getComic());
System.out.println(book2.getCartoon());
((ClassPathXmlApplicationContext) ctx).close();
※結果:
3
ca
co
bo
vo.Comic@14acaea5
vo.Cartoon@46d56d67
※Annotation設定
Book.java
public class Book {
private Cartoon cartoon;
private Comic comic;
public Book() {}
public Cartoon getCartoon() {
return cartoon;
}
@Autowired
public void setCartoon(Cartoon cartoon) {
this.cartoon = cartoon;
}
public Comic getComic() {
return comic;
}
@Autowired
public void setComic(Comic comic) {
this.comic = comic;
}
}
※只是在setter上面加@Autowired
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /> <bean id="ca" class="vo.Cartoon" /> <bean id="co" class="vo.Comic" /> <bean id="bo" class="vo.Book" />
因為要用@Autowired,所以也要將相關的class宣告一下
@Autowired等同在 xml 設定的autowire
結果:
4
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#0
ca
co
bo
vo.Comic@6d00a15d
vo.Cartoon@51efea79
---------------------------------------------------------------------------------
還有很多的Annotation都要向上面的@Autowired一樣,都要宣告一支class,如果用了5個,就要宣告5支class,太麻煩了,所以spring提供了<context:component-scan />,所以xml修改如下:
<context:component-scan base-package="\" /> <bean id="ca" class="vo.Cartoon" /> <bean id="co" class="vo.Comic" /> <bean id="bo" class="vo.Book" />
※base-package裡面是要掃描哪些包,裡面寫包名,有很多包可用「,」隔開,而我是根目錄,所以就是「\」,但在 web 這樣寫會出「@EnableAsync annotation metadata was not injected」的錯
※結果:
8
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
ca
co
bo
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
vo.Comic@1f554b06
vo.Cartoon@694e1548
※<context:annotation-config />,就是開啟上面迴圈預設的 annotation 的意思,但這類的設定都是針對 ClassPathXmlApplicationContext 才有用;對 AnnotationConfigApplicationContext 就沒用,所以這個設定是強制只能使用 XML 的設定,所有的 annotation 都沒效
而 context:component-scan 有個屬性 annotation-config,預設就是 true 了,所以可有可無
use-default-filters="false",表示不要用 base-package 裡的 @Component 等 annotation,使用子標籤的方式過濾(include、exclude)
resource-pattern="ComicBook*.class" 表示條件再加上 ComicBook開頭,.class結尾的才掃瞄,但要注意是 .class,不是 .java
annotation-config 有子標籤
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />,表示排除 @Controller 及其子類的,如 @Component 是 @Controller、@Service、@Repository、@Configuration 的父類
型態是 annotation,表示 expression 是什麼 annotation
有排除,當然也有包括(context:include-filter)
若型態是 assignable,如 <context:exclude-filter type="assignable" expression="xxx.ooo.Book" />,表示 expression 是什麼class
※annotation 設定要用 @ComponentScan(basePackages="ooo.xxx"),只會針對 annotation 有用,3.1 才有,然後使用 new AnnotationConfigApplicationContext(Book.class);,但要注意 Book.java 不在 ooo.xxx 的套件裡,但只要有宣告 @ComponentScan ,也下了如 @Component 之類的注解,一樣也會抓到
屬性 value 為 basePackages 的別名,所以也可用 value,且只要是 annotation 叫 value 的,還可以省略不寫
屬性 nameGenerator,如預設@Component 裡面不寫東西,那 bean 名稱就是小寫開頭的 class 名稱,想改就要繼承裡面的類
---------------------------------------------------------------------------------
@Autowired還可以放在屬性上面,而這時setter就可以刪除了,如下:
public class Book {
@Autowired
private Cartoon cartoon;
@Autowired
private Comic comic;
public Book() {}
public Cartoon getCartoon() {
return cartoon;
}
public Comic getComic() {
return comic;
}
}
結果還是一樣
---------------------------------------------------------------------------------
如果將xml裡的ca拿掉就會出現 「org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [vo.Cartoon] found for dependency:」的錯
可以將ca的annotation變成@Autowired(required = false),表示不要報錯
※結果:
7
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
co
bo
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
vo.Comic@3c0f93f1
null
可以看到不會報錯,就變null了
※@Autowired 如果寫在 field 上,intellij 會出現警告,會建議寫在建構子比較好,因為有可能建構子有用到這個注入的變數,但注入順序是先注入建構子才會注入 field,所以這時會抓不到,所以會有警告
※@Required
表示一定要注入,但只能放在方法上Book.java
public class Book {
private Cartoon cartoon;
@Autowired
private Comic comic;
public Book() {
}
public Cartoon getCartoon() {
return cartoon;
}
// @Autowired
@Required
public void setCartoon(Cartoon cartoon) {
this.cartoon = cartoon;
}
public Comic getComic() {
return comic;
}
public void setComic(Comic comic) {
this.comic = comic;
}
}
※如上,因為Cartoon沒注入,所以會報「org.springframework.beans.factory.BeanInitializationException: Property 'cartoon' is required for bean 'bo'」的錯,這是只要把注解打開注入即可解決
※注意@Autowired可以放在屬性上的,如下:
public class Book {
@Autowired
private Cartoon cartoon;
@Autowired
private Comic comic;
public Book() {
}
public Cartoon getCartoon() {
return cartoon;
}
@Required
public void setCartoon(Cartoon cartoon) {
this.cartoon = cartoon;
}
public Comic getComic() {
return comic;
}
public void setComic(Comic comic) {
this.comic = comic;
}
}
※這時還是會報上面的錯,因為和@Required合用,就一定都要放在方法上,不然它還是認為你沒注入
※@Qualifier
@Autowired是個by type,一定要剛好只有一個才有辦法注入,例如將Comic.java修改如下:public class Comic extends Cartoon {}
※就只是多個繼承Cartoon,這時就會出「org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [vo.Cartoon] is defined: expected single matching bean but found 2: ca,co」的錯,錯誤也很明顯,它說ca和co都是Cartoon,所以無法注入,這時Book.java的Cartoon屬性可修改如下即可解決:
@Autowired
@Qualifier("ca")
private Cartoon cartoon;
※看起來也很清楚,如果型態一樣,就以名稱是ca的來注入
---------------------------------------------------------------------------------
※除了放在屬性,也可以放在方法上,和上面的@Required一樣,全部統一放在屬性或方法上,不要分開,不然還是會認為有很多型態
@Autowired
@Qualifier("ca")
public void setCartoon(Cartoon cartoon) {
this.cartoon = cartoon;
}
※也就是將ca的bean給參數名稱cartoon
※但如果有很多參數,就只好寫在參數上了,如下:
@Autowired
public Book(Comic com, @Qualifier("ca") Cartoon car) {}
※@Resource
※我用的時候,居然沒提示,所以我用maven下載jar<dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency>
和@Autowired很像,但是它可以不用 @Qualifier
// @Autowired
// @Qualifier("ca")
@Resource(name = "ca")
private Cartoon cartoon;
※上面的需求,它只要一行就可解決了
※@Resource有兩個重要屬性name和type
1.如果只有設定name,一定要剛好一筆,否則出錯
2.如果只有設定type,一定要剛好一筆,否則出錯
3.如果name和type都有設定,都要符合才注入,否則就出錯誤訊息
反正電腦是很笨的,一定要剛剛好,否則出錯
4.如果上面的例子沒有name="ca",會找 cartoon,找不到才找 type,但如果找到了卻不是 Cartoon 就會報錯
而官方說如果name和type都沒有設定,就等同name,但我試的結果還是可以注入,但一定要有預設建構子
Book.java
public class Book {
@Resource
private Cartoon xxx;
@Resource
private Comic ooo;
// setter/getter...
}
Cartoon.java 和 Comic.java
public class Cartoon {
private String cartoonName;
// setter/getter...
}
public class Comic {
private String comicName;
// setter/getter...
}
applicationContext.xml
<context:annotation-config /> <bean id="ca" class="vo.Cartoon" p:cartoonName="卡通" /> <bean id="co" class="vo.Comic" p:comicName="漫畫" /> <bean id="bo" class="vo.Book" />
測試類
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(ctx.getBeanDefinitionCount());
for (String s : ctx.getBeanDefinitionNames()) {
System.out.println(s);
}
Book book1 = ctx.getBean("bo", Book.class);
Book book2 = (Book) ctx.getBean("bo");
System.out.println(book1.getOoo().getComicName());
System.out.println(book2.getXxx().getCartoonName());
((ClassPathXmlApplicationContext) ctx).close();
※按官方所說xxx和ooo都沒有啊!但就是印的出卡通和漫畫,所以個人覺得都不寫應該是 by type 才對
※也可以在Comic.java的屬性或方法上加@Value("海賊王"),也可以塞值,這時xml就不用設了,但如果兩個都設,我試的結果是xml贏了
※最近發現其實 @Resource 裡面有寫,雖然 name 是寫空,但註解有寫是抓 name 的名稱
※@PostConstruct和@PreDestroy
這兩個annotation等同於第九篇的init-method 和 destroy-method,只是現在用的是annotation而已public class Book {
@Resource(name = "ca")
private Cartoon cartoon;
@Autowired
private Comic comic;
public Book() {
}
@PostConstruct
public void init() {
System.out.println("init");
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
}
※可以使用很多個@PostConstruct和@PreDestroy
沒有留言:
張貼留言