這篇開始講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
沒有留言:
張貼留言