Spring裝配Bean(五)profile註解和解決自動注入的歧義性
配置profile bean
Spring為環境相關的bean所提供的解決方案其實和構建時候的方案沒有太大區別,Spring會根據環境決定該建立那個bean和
不建立那個bean。
Spring的bean profile的功能。要使用profile,首先將所有不同的bean定義到一個或者多個profile之中,在將應用部署到每個環境中,要確保對應的profile處於啟用(active)的狀態
* Java配置中,使用@Profile註解指定某個bean屬於那個profile
首先建立一個bean,用在開發環境和生產環境
@Configuration
@Profile("dev")
public class DevConfiguration {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld("dev environment .....");
}
}
@Configuration
@Profile("prod")
public class ProductConfiguration {
public HelloWorld helloWorld(){
return new HelloWorld("product environment .... ");
}
}
從Spring3.2開始,profile註解可以用在方法上,上面改為
@Configuration
public class HellloConfiguration {
@Bean
@Profile("dev")
public HelloWorld devhelloWorld(){
return new HelloWorld("dev environment .....");
}
@Bean
@Profile("prod")
public HelloWorld prodhelloWorld(){
return new HelloWorld("product environment .....");
}
}
只有規定的profile被啟用,對應的bean才會被建立,沒有指定profile的Bean始終都會建立
* XML中配置profile
* Beans標籤使用profile屬性表示當前的bean的profile
<?xml version="1.0" encoding="UTF-8"?>
<beans profile="dev" 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.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
">
<bean id="helloWorld" class="com.erong.service.HelloWorld">
<property name="message" value="dev bean ....."></property>
</bean>
</beans>
需要注意的是xsi:schemaLocation中定義的beans的xsd檔案必須是3.1之後的,只有profile被啟用並且屬性值相同的bean才能建立
* 重複使用元素來指定多個profile
-- profile_dev.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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
">
<context:annotation-config></context:annotation-config>
<beans profile="dev">
<bean id="helloWorld" class="com.erong.service.HelloWorld">
<constructor-arg value="dev hello world bean ...."></constructor-arg>
</bean>
</beans>
<beans profile="business">
<bean id="helloWorld" class="com.erong.service.HelloWorld">
<property name="message" value="business bean ....."></property>
</bean>
</beans>
<beans profile="prod">
<bean id="helloWorld" class="com.erong.service.HelloWorld">
<property name="message" value="prod bean ....."></property>
</bean>
</beans>
</beans>
所有的bean定義到一個xml檔案中,定義了三個bean,但是執行時候只會建立一個bean,取決於處於啟用狀態的是profile
* 啟用profile
Spring在確定那個profile處於啟用狀態,需要依賴兩個獨立的屬性:spring.profiles.active和spring.profiles.default
如果設定了spring.profiles.active屬性的話,它的值就會用來確定那個profile是啟用的。
如果沒有設定,將使用spring.profiles.default的值,兩個屬性都沒有設定將會不啟用profile
設定方式:
1. 作為DispatcherServlet的初始化引數
2. 作為Web應用的上下文引數
3. 作為JNDI條目
4. 作為環境變數
5. 作為JVM系統屬性
<!-- 為上下文設定預設的profile -->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
<!-- 為servlet設定預設profile -->
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.bing.servlet.MyServlet</servlet-class>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>
注意到,profile使用的都是複數形式,可以同時啟用多個profile,使用逗號隔開
* 使用profile進行測試
當執行整合測試時,如果配置中的bean定義在profile中,那麼在執行測試中,需要有一種方式來啟用合適的profile
Spring提供了@ActiveProfiles註解,我們可以使用它指定執行測試時要啟用那個profile。
測試類:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:profile_dev.xml")
@ActiveProfiles(value={"dev"})
public class CDPlayerTest {
@Autowired
private HelloWorld helloWorld;
@Test
public void test(){
System.out.println(
helloWorld.getMessage());
}
}
最後應該輸出:
Your Message: dev hello world bean ....
測試過程中發現的問題:
1. 建立bean的時候,使用property元素對Bean進行賦值操作,呼叫的無參的構造器,並且呼叫的是對應的setter方法進行設值
2. 如果使用constructor-arg元素,必須存在相應引數的構造器方法建立Bean
* 條件化的bean
假設希望一個或多個bean只有在應用的類路徑下包含特定的庫時才建立。或者希望某個bean在另外某個特定的bean宣告之後才會建立。Spring4之後,引入一個全新的註解@Conditional註解,它可以用到帶有@Bean註解的方法上。
如果給定的條件計算結果為true,就建立這個bean
1. JavaConfig配置類
@Configuration
public class CDConfig {
@Bean
@Conditional(MagicExistCondition.class)
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
}
@Conditional將會根據類MagicExistCondition的matches方法返回的結果判斷是否建立Bean
-- MagicExistCondition 實現介面 org.springframework.context.annotation.Condition
public class MagicExistCondition implements Condition {
/**
* 檢查環境變數是否存在magic屬性
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {
Environment env = context.getEnvironment();
return env.containsProperty("magic");
}
}
對於matches方法中的ConditionContext介面,提供的方法我們能夠檢查
public interface ConditionContext {
/**
* 根據返回的BeanDefinitionRegistry,檢查bean的定義
*/
BeanDefinitionRegistry getRegistry();
/**
* 藉助返回的ConfigurableListableBeanFactory,檢查bean是否存在
*/
ConfigurableListableBeanFactory getBeanFactory();
/**
* 返回的Environment,來確定環境變數是否存在及值是什麼
*/
Environment getEnvironment();
/**
* 獲取載入的資源
*/
ResourceLoader getResourceLoader();
/**
* 檢查類是否存在
*/
ClassLoader getClassLoader();
}
AnnotatedTypeMetadata這個介面,能夠檢查@Bean註解方法上還存在其他註解
Note:@Profile註解底層使用到了@Conditional註解,並且引用ProCondition作為Condition介面的實現
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
從原始碼看
1. 獲取Profile 註解的所有屬性的鍵值對
2. 檢查當前環境中生效的Profile和當前Bean的Profile的value的值是否相同,相同,返回true,建立這個bean
@Profile在使用時候也會通過Conditon介面的實現類,判斷Bean的profile是否處在啟用狀態,從而判斷是否建立Bean。
* 處理自動裝配的歧義性
如果存在多個類實現同一個介面,每個類都建立一個bean,自動注入的時候會選擇哪個Bean,Spring無法判斷就會丟擲異常
解決:
1. 使用@Primary註解或者xml中primary = true ,標識Bean是優先選擇的注入的
@Bean
@Primary
@Conditional(MagicExistCondition.class)
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
<bean id="helloWorld" class="com.erong.service.HelloWorld" primary="true">
<constructor-arg value="dev hello world bean ...."></constructor-arg>
</bean>
2. 使用@Qualifier註解限定裝配的Bean和@Autowire註解或者@Inject註解搭配使用,值為Bean的ID
@Autowired
@Qualifier("cdplayer")
private CDPlayer cdplayer;
* 建立自定義的限定符
可以為bean設定自己的限定符,而不是依賴於將bean ID 作為限定符
> bean的宣告上新增@Qualifier註解,設定Bean自己的限定符號
@Component
@Qualifier("compactDisc")
public class SgtPeppers implements CompactDisc {
@Bean
@Qualifier("compactDisc")
public SgtPeppers getInstance(){
return new SgtPeppers();
}
這樣,建立的Bean的限定符號就為compactDisc,自動注入的時候@Qualifier使用這個限定符注入
note:
1. @Bean註解放在方法上,如果方法存在形參,會自動掃描匹配的bean自動注入
2. @Bean註解建立的Bean的限定符,可以通過@Qualifier指定
3. @Bean建立的Bean,Bean ID可以同時是指定的限定符,也可以是方法名
* Bean的自定義限定符號相同,需要自定自己的限定符註解
@Target({java.lang.annotation.ElementType.FIELD,
java.lang.annotation.ElementType.METHOD,
java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
相關文章
- Spring入門(八):自動裝配的歧義性Spring
- Spring-04 Bean的自動裝配SpringBean
- 淺嘗Spring註解開發_自定義註冊元件、屬性賦值、自動裝配Spring元件賦值
- 省掉bean自定義spring mvc註解注入json值BeanSpringMVCJSON
- 2、spring注入及自動裝配Spring
- Spring裝配Bean(七) Spring的執行時注入SpringBean
- SpringBoot自動裝配原理之Configuration以及@Bean註解的使用Spring BootBean
- bean 的自動裝配Bean
- Spring入門(二):自動化裝配beanSpringBean
- spring註解開發(一)Bean注入SpringBean
- SpringBoot(14)—註解裝配BeanSpring BootBean
- Spring bean 裝配SpringBean
- Bean的自動裝配及作用域Bean
- Spring裝配Bean(六)Bean的作用域SpringBean
- Spring裝配Bean(四)SpringBean
- Spring裝配Bean(一)SpringBean
- Spring裝配Bean(二)SpringBean
- 二、Spring裝配BeanSpringBean
- Spring Boot 基礎: 使用 `@ConfigurationProperties` 實現自定義屬性的自動裝配Spring Boot
- 常用的自動裝配註解@Autowired @RequiredArgsConstructor @AllArgsConstructorUIStruct
- springboot自動裝配(1)---@SpringBootApplication註解怎麼自動裝配各種元件Spring BootAPP元件
- Spring自動裝配BeansSpringBean
- Spring IOC 一——Spring容器裝配BeanSpringBean
- 初識Spring —— Bean的裝配(一)SpringBean
- 初識Spring —— Bean的裝配(二)SpringBean
- Spring @Autowired 註解自動注入流程是怎麼樣?Spring
- Spring Boot 自動裝配原理Spring Boot
- Spring注入bean報錯 Error creating bean with name的網上找不到的解決方案SpringBeanError
- 【Spring註解驅動開發】使用@Autowired@Qualifier@Primary三大註解自動裝配元件,你會了嗎?Spring元件
- Spring-註解注入Spring
- SpringBoot自動裝配-自定義StartSpring Boot
- 【Spring 註解】@Configuration和@BeanSpringBean
- Spring 原始碼分析之 bean 依賴注入原理(注入屬性)Spring原始碼Bean依賴注入
- SpringBoot中根據屬性動態註冊Spring BeanSpring BootBean
- 關於Spring的bean注入SpringBean
- Spring @Profile註解使用和原始碼解析Spring原始碼
- 深入理解Spring框架的自動裝配原理Spring框架
- spring框架半自動註解Spring框架