1、使用@Profile註解來實如今不一樣環境下建立不一樣的Beanjava
舉例:mysql
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; @Configuration public class DataSourceProfiles { @Bean @Profile("development") public DataSource deveDataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://11.11.11.11:3306/demodb"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } @Bean @Profile("qa") public DataSource qaDataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://11.11.11.11:3306/demodb"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } @Bean @Profile("product") public DataSource productDataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://11.11.11.11:3306/demodb"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } @Bean public SomeOtherBean getBean(){ return new SomeOtherBean(); } }
上面的例子中,「development」這個Profile激活時,會建立development環境的Bean和SomeOtherBean,其餘兩個Bean不會被建立。spring
Spring有兩個獨立的屬性來肯定哪一個Profile被激活:spring.profile.active和spring.profile.default,若是設置了spring.profile.active屬性,則它的值用來肯定激活的Profile,若是沒有設置spring.profile.active,可是設置了spring.profile.default,則spring.profile.default的值用來肯定激活的Profile,若是spring.profile.active和spring.profile.default均沒有設置,則沒有激活的Profile,此時只會建立哪些沒有定義在Profile中的Bean。有多種方式來定義這兩個屬性:sql
2、條件化的裝配Beanide
能夠設置不一樣的條件來控制Bean的建立:測試
舉例:當環境變量中設置了magc屬性時,才建立MagicExistCondition這個Bean,進一步的,只有MagicExistCondition建立後,才建立MagicBean這個Bean,具體實現方式以下:spa
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 MagicExistCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata){ Environment env = context.getEnvironment(); return env.containsProperty("magic"); } }
上述代碼中MagicExistCondition實現了Condition接口,只有實現了這個接口的類,才能夠做爲條件類,用在@Conditional註解中。Condition這個接口很簡單,如上面的例子,只須要實現matches方法便可。code
接下來,判斷MagicExistCondition被建立後,才建立MagicBean這個Bean:blog
@Bean @Conditional(MagicExistCondition.class) public MagicBean magicBean(){ reutrn new MagicBean(); }
再次看看Condition接口中的matche方法,這個方法有兩個參數:ConditionContext和AnnotatedTypeMetadata。接口
ConditionContext是一個接口:
package org.springframework.context.annotation; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; public interface ConditionContext { BeanDefinitionRegistry getRegistry(); ConfigurableListableBeanFactory getBeanFactory(); Environment getEnvironment(); ResourceLoader getResourceLoader(); ClassLoader getClassLoader(); }
getRegistry()方法返回的BeanDefinitionRegistry能夠檢查Bean的定義
getBeanFactory()方法返回的ConfigurableListableBeanFactory能夠檢查Bean是否存在,甚至進一步探查Bean的屬性
getEnvironment()方法返回的Environment檢查環境變量是否存在以及它的值是什麼
getResourceLoader()方法返回的ResourceLoader用於探查所加載的資源
getClassLoader()方法返回的ClassLoader用於加載並檢查類是否存在
AnnotatedTypeMetadata也是接口:
import java.util.Map; import org.springframework.util.MultiValueMap; public interface AnnotatedTypeMetadata { boolean isAnnotated(String var1); Map<String, Object> getAnnotationAttributes(String var1); Map<String, Object> getAnnotationAttributes(String var1, boolean var2); MultiValueMap<String, Object> getAllAnnotationAttributes(String var1); MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2); }
該接口主要用來探查帶有@bean註解的類上面是否還有其餘的註解,而且檢查那些註解的屬性值。舉個例子,回到@Profile這個註解,這個註解用於控制當前@bean註解的類在特定Profile激活時才被建立,那麼,@Profile註解是如何實現這個功能的呢,這裏就須要藉助AnnotatedTypeMetadata這個接口了,從Spring 4開始,@Profile註解基於@Conditional和Condition來實現:
首先看看@Condtion註解:
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Conditional({ProfileCondition.class}) public @interface Profile { String[] value(); }
其中的ProfileCondition類實現了Condition接口的matches方法:
package org.springframework.context.annotation; import java.util.Iterator; import java.util.List; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.MultiValueMap; class ProfileCondition implements Condition { ProfileCondition() { } public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getEnvironment() != null) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { Iterator var4 = ((List)attrs.get("value")).iterator(); Object value; do { if (!var4.hasNext()) { return false; } value = var4.next(); } while(!context.getEnvironment().acceptsProfiles((String[])((String[])value))); return true; } } return true; } }
能夠看到:matches方法中,首先給metadata.getAllAnnotationAttributes()方法傳遞Profile.class.getName()這個Profile Bean的名字,獲得全部註解的屬性,並逐個遍歷判斷,藉助acceptsProfiles方法來見擦好該Profile是否被激活!!!
3、處理自動裝配的歧義性
當一個接口有多個實現類時,若是某個類須要注入接口類,此時會拋出NoUniqueBeanDefinitionException異常,解決方法是: