Spring溫故而知新 - bean的裝配(續)
按條件裝配bean
就是當滿足特定的條件時Spring容器才創建Bean,Spring中通過@Conditional註解來實現條件化配置bean
package com.sl.ioc; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class AnimalConfig { @Bean("dog") @Conditional(DogCondition.class) public Dog DogInstance() { return new Dog(); } @Bean("cat") @Conditional(CatCondition.class) public Cat CatInstance() { return new Cat(); } }
@Conditional和 :Condition接口的實現
public @interface Conditional {/** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); } public interface Condition { /** * Determine if the condition matches. * @param context the condition context *@param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked * @return {@code true} if the condition matches and the component can be registered, * or {@code false} to veto the annotated component‘s registration */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
Conditional註解通過value傳入一個類,實現Condition接口,通過實現Condition接口中matches方法決定是否需要裝配Bean,如果滿足條件需要創建bean則返回true,否則返回false
自己定義兩個繼承Condition接口的類:通過ConditionContext查找當前環境中是否存在dog或者cat屬性,如果存在,則創建對應的bean對象,具體實現如下:
package com.sl.ioc; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class DogCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); boolean flag= environment.containsProperty("dog"); return flag; } }
package com.sl.ioc; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class CatCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); boolean flag= environment.containsProperty("cat"); return flag; } }
package com.sl.ioc; import org.springframework.stereotype.Component; @Component public interface Animal { void Say(); } package com.sl.ioc;import org.springframework.stereotype.Component; @Component public class Cat implements Animal { @Override public void Say() { System.out.println("I am a cat"); } } package com.sl.ioc; import org.springframework.stereotype.Component; @Component public class Dat implements Animal { @Override public void Say() { System.out.println("I am a dog"); } }
測試代碼:
public class TestClass { @Test public void TestGetDoInstance() { System.setProperty("dog",""); ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class); String[] beanNames = context.getBeanDefinitionNames(); for(String bean : beanNames) { System.out.println(bean); } } }
運行測試可以看到輸出的beanname中會包含dog的bean:
自動裝配的歧義處理
Spring自動裝配時如果存在多個bean能夠匹配的話,那麽這種情況會阻礙Spring通過屬性、構造函數或方法進行裝配。針對這種情況,Spring提供了多種 可選方案來解決這個問題,可以選擇一個bean作為首選的bean,或者使用限定符來確定唯一bean
1:使用首選Bean
Spring提供@Primary註解來設置首選Bean,當初選自動裝配歧義時,會選擇裝配帶有@Primary的bean
沿用上面的示例代碼,嘗試裝載animal
@Component public class AnimalInstance { @Autowired public Animal animal; }
當Spring嘗試註入animal實例時,由於Dog和Cat都繼承自Animal,所以此處產生了歧義,下面通過使用@Primary指定首選bean
@Component @Primary //指定首選bean public class Cat implements Animal { @Override public void Say() { System.out.println("I am a cat"); } }
同樣也可以使用XML配置來實現:<bean>元素提供了primary屬性來設置首選bean
<bean id="cat" class="com.sl.ioc.Cat" primary ="true" >
測試代碼:
public class TestClass { @Test public void TestGetDoInstance() { //應用上下文 ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class); AnimalInstance animalInstance = context.getBean(AnimalInstance.class); animalInstance.animal.Say(); } }
運行結果:
首選項只是標識一個優先選擇裝載的bean,如果配置了多個@Primary,那麽將帶來新的歧義,Spring依然無法完成自動裝配,可以通過下面限定符來解決這個問題
2:使用限定符
Spring提供@Qualifier註解來指定想要註入的具體bean。例如上面的示例,如果指定註入dog:
package com.sl.ioc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class AnimalInstance { @Autowired @Qualifier("dog") public Animal animal; }
解釋一下:@Qualifier("dog")表示指定的bean具有”dog”限定符,spring中bean如果沒有指定限定符,會使用默認限定符,即使用beanID作為限定符。所以上面是恰好使用了dog bean的ID作為了限定符。也可以寫成如下方式:
@Component @Qualifier("specialdog") //為bean指定限定符 public class Dog implements Animal { @Override public void Say() { System.out.println("I am a dog"); } }
@Component public class AnimalInstance { @Autowired @Qualifier("specialdog") //使用上面定義的限定符 public Animal animal; }
Bean的作用域
Spring容器在創建bean實例的同時,還允許指定bean實例的作用域,常見作用域有一下幾種:
1:單例作用域(Singleton)
2:原型作用域(Prototype)
3:會話作用域(Session)
4:請求作用域(Request)
5:全局會話作用域(globalSession)
Singleton作用域
在整個應用中,Spring IOC容器為使用singleton模式的bean只創建一個實例,Spring將會緩存Bean實例,任何對該類型beand請求都會返回該實例。單例也是Spring默認的作用域。具體使用如下,通過XML配置
<bean id="beanid" class="com.sl.ioc.xxx" scope="singleton" ></bean>
<bean>元素提供了scope屬性來設置singleton作用域
對應的註解:
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
Prototype原型作用域
每次註入或者從Spring容器中獲取時都創建一個新的bean實例:
<bean id="beanid" class="com.sl.ioc.xxx" scope="prototype" ></bean>
<bean>元素提供了scope屬性來設置singleton作用域
對應的註解:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Session會話作用域
在web應用中,針對每個會話,Spring容器根據bean定義創建的bean實例,只在當前會話Session中有效,XML配置如下:
<bean id="beanid" class="com.sl.ioc.xxx" scope="session" ></bean>
針對某個HTTP Session,Spring容器會根據bean定義創建一個新的bean實例,該bean僅在當前HTTP Session內有效。所以可以根據需要放心的更改bean實例的內部狀態,而不影響其他Http Session中bean實例。當HTTP Session最終被廢棄的時候,在該HTTP Session作用域內的bean也會被銷毀掉。
Request 請求作用域
<bean id="beanid" class="com.sl.ioc.xxx" scope="globalSession" ></bean>
在web應用中,針對每次請求,Spring容器根據bean定義創建新的bean實例,只在當前請求內有效
<bean id="beanid" class="com.sl.ioc.xxx" scope="request" ></bean>
該bean實例只在當前請求內有效,在請求處理完成之後bean也會被銷毀掉
globalSession全局會話作用域
類似於session作用域,只是其用於portlet環境的web應用。如果在非portlet環境將視為session作用域。
Spring溫故而知新 - bean的裝配(續)