Spring提供兩種技巧,可以幫助我們減少XML的配置數量。
1.自動裝配(autowiring)有助於減少甚至消除配置<property>元素和<constructor-arg>元素,讓Spring自動識別如何裝配Bean的依賴關系。
2.自動檢測(autodiscovery)比自動裝配更進一步,讓Spring自動識別哪些類需要被配置成Spring Bean,從而減少對<bean>元素的使用。
一、4種類型的自動裝配
1.byName,把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。如果沒有跟屬性的名字相匹配的Bean,則該屬性不進行裝配。
<bean id="instrument" class="cn.edu.stu.springidol.Saxphone" /> <bean id="kenny2" class="cn.edu.stu.springidol.Instrumentalist" autowire="byName"> <property name="song" value="Jingle Bells" /> </bean>
Saxphone的id屬性與kenny的instrument屬性的名字是一樣的。
缺點:需要假設Bean的名字與其他Bean的屬性名字一樣。
2.byType,把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中,如果沒有跟屬性的類型相匹配的Bean,則該屬性不被裝配。
<bean id="saxphone" class="cn.edu.stu.springidol.Saxphone" /> <bean id="kenny2" class="cn.edu.stu.springidol.Instrumentalist" autowire="byType"> <property name="song" value="Jingle Bells" /> </bean>
缺點:如果Spring尋找到多個Bean的類型與需要自動裝配的類型都相匹配,Spring不會猜測哪一個Bean更適合裝配,而是拋出一個異常,所以應用只允許存在一個Bean與需要自動裝配的屬性類型相匹配。
兩種解決方法可以解決byType帶來的歧義:
1.聲明候選Bean的primary屬性為true,但默認的primary屬性是true的,所以必須聲明除候選Bean外的其他非候選Bean的primary屬性為false
2.設置在自動裝配時被忽略的Bean,autowire-candidate屬性為false,要求Spring在自動裝配時,忽略Bean作為候選Bean
3.constructor,把與Bean的構造器入參具有相同類型的其他Bean自動裝配到Bean構造器的對應入參中。
如果通過構造器注入來配置Bean,可以移除<constructor-arg>元素
<bean id="duke2" class="cn.edu.stu.springidol.PoeticJuggler" autowire="constructor" />
缺點:當發現多個Bean匹配構造器入參,Spring不會猜測哪一個更適合,此外,如果一個類有多個構造器都滿足自動裝配條件,Spring也不會猜測哪一個更適合。
4.autodetect,首先嘗試使用constructor進行自動裝配,如果失敗,再嘗試使用byType進行自動裝配。
2.5之前還可以使用,3.0之后就沒有了。
默認自動裝配:
在<beans>上增加一個default-autowire屬性,作用於Spring配置文件的所有Bean,可以用<bean>元素的autowire屬性覆蓋<beans>的默認自動裝配策略。
還可以使用混合使用自動裝配和顯示裝配。
二、使用注解裝配
Spring容器默認禁用注解裝配,啟用方式的使用context命名空間配置中的<context:annotation-config>元素。
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config /> </beans>
1.使用@Autowired
不僅可以標注setter方法,還可標注需要自動裝配Bean引用的任意方法,構造器,屬性。
可選的自動裝配,屬性不一定非要裝配,null值也可以接受,通過設置required屬性為false來配置自動裝配是可選的。但是當使用構造器裝配時,只有一個構造器可以將required屬性設置為true,其他使用@Autowired注解標注的構造器只能將required設置為false,當使用@Autowired標注多個構造器,Spring選擇滿足裝配條件中入參最多的那個。
限定歧義性的依賴,可能有至少兩個Bean完全滿足裝配條件,如何選擇。
使用Qualifier("beanID")明確指定注入ID為beanID的Bean
或者:
XML聲明Bean
<bean id="saxphone" class="cn.edu.stu.springidol.Saxphone"> <qualifier value="stringed" /> </bean>
@Autowired
@Qualifier("stringed")
private Instrument instrument;
還可以創建自定義的限定器。
2.@Inject實現基於標准的自動裝配
可以用來自動裝配屬性、方法和構造器,與@Autowired不同的是,沒有required屬性。
限定@Inject所標注的屬性,使用@Named("beanID")
創建自定義限定器,與@Autowired類似。
在注解中使用表達式:
@Value("Eruption")
private String song;
@Value("#{systemProperties.myFavoriteSong}")
private String song;
三、自動檢測Bean
<context:annotation-config>有助於消除<property>和<constructor-arg>元素,但仍需要使用<bean>顯示定義Bean。
<context:component-scan>除了完成<context:annotation-config>一樣的工作,還允許spring自動檢測Bean和定義Bean,這意味着不使用<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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="cn.edu.stu.springidol"> </context:component-scan> </beans>
<context:component-scan>會掃描指定的包及其所有子包,並找出能夠自動注冊為Spring的類。
如何知道哪些類需要注冊為Spring Bean?
1.為自動檢測標注Bean
<context:component-scan>會查找使用構造型注解所標注的類
i.@Component,通用的構造性注解,標識該類為Spring組件
ii.@Controller,標識該類定義為Spring MVC controller
iii.@Repository,標識該類定義為數據倉庫
iv.@Service,標識該類定義為服務
v.使用@Component標注的任意自定義注解
import org.springframework.stereotype.Component; @Component public class Guitar implements Instrument { @Override public void play() { System.out.println("Strum Strum Strum"); } }
Guitar會被自動地注冊為Spring Bean,ID默認為無限定類名,這里是guitar,可以通過@Component("anotherName")顯式命名ID。
2.過濾組件掃描
使用<context:include-filter>告知哪些類需要注冊為Spring Bean,使用<context:exclude-filter>告知哪些類不需要注冊為Spring Bean。
5種過濾器類型:
i.annotaion,使用指定注解所標注的類,通過expression屬性指定要掃描的注解
ii.assignable,掃描派生於expression屬性指定類型的那些類
iii.aspject,掃描expression屬性所指定的AspectJ表達式所匹配的那些類
iv.custom,使用自定義的org.springframework.core.type.TypeFilter實現類,該類由expression指定
v.regex,掃描類的名稱與expression屬性所指定的正則表達式所匹配的那些類
四、使用Spring基於Java的配置(為了不使用XML為目的)
1.創建基於Java的配置
首先啟動<context:component-scan>,這需要少量的XML配置,
<context:component-scan>會自動注冊那些使用某種構造型注解所標注的Bean,但它也會自動加載使用@Configuration注解所標注的類。
2.定義一個配置類(類放在base-package指定的包下)
@Configuration public class SpringIdolConfig { }
3.聲明一個簡單Bean
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SpringIdolConfig { @Bean public Performer duke() { return new Juggler(); } }
使用@Bean注解標注的duke方法會告知Spring我們希望該方法定義的Bean被注冊進Spring的應用上下文中,因此,在其他Bean的聲明方法中引用這個方法,Spring會攔截該方法的調用,並嘗試在應用上下文中查找該Bean,而不是讓方法創建一個新的實例。