三、spring中高級裝配(1)
大概看了一下第三章的內容,我從項目中仔細尋找,始終沒有發現哪裏有這種配置,但是看完覺得spring還有這麽牛B的功能啊,spring的厲害之處,這種設計程序的思想,很讓我感慨。。。
一、環境與profile
(1)配置profile bean
面對這樣的需求:想出一種方法來配置DataSource,使其在每種環境下都會選擇最為合適的配置,你會如何做呢?看看spring所提供的解決方案。spring中引入了bean profile的功能。在Java配置中,可以使用@Profile 註解指定某個bean屬於哪一個profile。
1)在Java類中配置profile bean
packagecom.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;//@Profile註解應用在累計別上。它會告訴spring這個配置類中的bean只有在dev profile 激活時才會創建,沒有激活,則不會創建 @Configuration @Profile("dev") public class DevelopmentProfileConfig{ return new EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2). addScript("classpath:schema.sql").addScript("classpath:test- data.sql").build(); }package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jndi.JndiObjectFactoryBean; //@Profile註解應用在累計別上。它會告訴spring這個配置類中的bean只有在prod profile 激活時才會創建,沒有激活,則不會創建 @Configuration @Profile("prod") public class ProductiontProfileConfig{ JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } //可以將這兩個bean的聲明放置在同一個配置類中 package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.jndi.JndiObjectFactoryBean; @Configuration public class DateSourceConfig{ //為dev profile裝配bean public DataSource embeddedDataSource(){ return new EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2). addScript("classpath:schema.sql").addScript("classpath:test- data.sql").build(); } //為prod profile 裝配bean public DataSource jbdiDataSource(){ JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
2)在XML中配置profile bean
//xml中配置的實際效果和在Java類中配置是一樣的 //dev profile 的bean <beans profile="dev"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql" /> <jdbc:script location="classpath:test-data.sql" /> </jdbc:embedded-database> </beans> //qa profile 的bean <beans profile="qa"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close" p:url="jdbc:h2:tcp://deserver/~/test" p:driverClassName="org.h2.Driver" p:username="sa" p:password="password" p:initialSize="20" p:maxActive="30"> </bean> </beans> //prod profile 的bean <beans> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataSource" resource-ref="true" proxy-interfase="javax.sql.DataSource" /> </beans>
2)激活 profile
profile bean 配置OK了,如何來激活配置的bean呢?依賴兩個獨立的屬性:spring.profiles.active 和 spring.profiles.default。設置了spring.profiles.active屬性的話,那麽它的值就會來確定哪個profile是激活的,如果設置了spring.profiles.default屬性,那麽spring 會查找spring.profiles.default的值,如果兩個屬性都設置的話,肯定會優先spring.profiles.active屬性對應的profile bean 裝配
多種方式設置這兩個屬性:
1))作為DispatcherServlet的初始化參數
2))作為web應用的上下文參數
3))作為JNDI條目
4))作為環境變量
5))作為JVM的系統屬性
6))在集成測試上,使用@ActiveProfiles註解設置
web.xml 中設置默認的profile
//為上下文設置默認的profile <context-param> <param-name>spring.profile.default</param-name> <param-value>dev</param-value> </context-param> //為Servlet設置默認的profile 可以列出多個profile名稱,並以逗號分隔來實現 <servlet> <servlet-name>app-servlet</servlet-name> <servlet-class> org.springframework.web.servlet.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet>
二、條件化的bean
spring4引入了一個新的@Conditional註解,它可以用到帶有@Bean註解的方法上。如果給定的條件計算結果為true,則會創建這個bean,否則的話,這個bean會被忽略。
//條件化的創建bean @Bean @Conditional(MagicExistsCondition.class) public MagicBean magicBean(){ return new MagicBean(); } //@Conditional 將會通過Condition 接口進行條件對比 public interface Condition{ public boolean matches(ConditionContext ctcx,AnnotatedTypeMetadata metadata); } //在Contidion 中檢查是否存在magic屬性,滿足這個條件,matches()方法返回true,則所有的@Conditional 註解上引用MagicExistsCondition的bean都會被創建 public class MagicExistsCondition implements Condition{ public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){ Environment env = context.getEnvironment(); return env.containsProperty("magic"); } }
註:這裏講解了兩個比較特殊的接口,一個是ConditionContext,一個是AnnotatedTypeMetadata,做一個說明:
public interface ConditionContext{ //檢查bean的定義 BeanDefinitonRegistry getRegistry(); //檢查bean是否存在,甚至探查bean的屬性 ConfigurableListableBeanFactory getBeanFactory(); //檢查環境變量是否存在以及它的值是什麽 Environment getEnvironment(); //返回ResourceLoader所加載的資源 ResourceLoader getResourceLoader(); //返回ClassLoader加載並檢查類是否存在 ClassLoader getClassLoader(); } //檢查帶有@Bean註解的方法上還有什麽其他的註解 public interface AnnotatedTypeMetadata{ boolean isAnnotated(String annotationType); Map<String,Object> getAnnotationAttributes(String annotationType); Map<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString); MultiValueMap<String,Object> getAnnotationAttributes(String annotationType); MultiValueMap<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString); }
註:spring4 中對@Profile註解進行了重構,使其基於@Conditional 和 Condition實現,@Profile在spring4 中的實現:
/* @Profile本身也使用了@Conditional註解,並且引用了ProfileCondition作為Condition實現,ProfileCondition在做出決策的過程中,考慮到了ConditionContext和AnnotatedTypeMetadata中多個因素 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) @Document @Conditional(ProfileCondition.class) public @interface Profile{ String[] value(); }
三、spring中高級裝配(1)