前面講解了
bean
的核心裝配技術,其可應付不少中裝配狀況,但Spring
提供了高級裝配技術,以此實現更爲高級的bean
裝配功能。java
將全部不一樣
bean
定義放置在一個或多個profile
中,在將應用部署到每一個環境時,要確保對應的profile
處於激活
狀態。如配置了以下數據源,並使用profile
註解定義。web
package com.hust.grid.leesf.ch3; import javax.activation.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; @Configuratoin @Profile("dev") public class DevelopmentProfileConcifg { @Bean(destroyMethod = "shutdown") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } }
package com.hust.grid.leesf.ch3; 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; @Configuration public class ProductionProfileConfig { @Bean @Profile("prod") public DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
只有在
prod profile
激活時,纔會建立對應的bean
。在Spring 3.1
以前只能在類級別上使用@Profile
註解,從Spring 3.2
以後,能夠從方法級別上使用@Profile
註解,與@Bean
註解一塊兒使用,上述放在兩個不一樣配置類能夠轉化爲兩個方法放在同一個配置類中。spring
package com.hust.grid.leesf.ch3; 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 DataSourceConfig { @Bean(destroyMethod = "shutdown") @Profile("dev") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } @Bean @Profile("prod") public DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
注意:儘管配置類中配置了不一樣的
Profile
,但只有規定的profile
激活時,對應的bean
纔會被激活。sql
<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" profile="dev"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql" /> <jdbc:script location="classpath:test-data.sql" /> </jdbc:embedded-database> </beans>
或者使用
beans
元素定義多個profile
。apache
<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <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> <beans profile="qa"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close" p:url="jdbc:h2:tcp://dbserver/~/test" p:driverClassName="org.h2.Driver" p:username="sa" p:password="password" p:initialSize="20" p:maxActive="30" /> </beans> <beans profile="prod"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource" /> </beans> </beans>
三個
bean
的ID
都是dataSource
,在運行時會動態建立一個bean
,這取決激活的哪一個profile
。session
Spring
依賴spring.profiles.active
和spring.profiles.default
兩個屬性肯定哪一個profile
處於激活狀態,若是設置了spring.profiles.active
,那麼其值用於肯定哪一個profile
是激活狀態,若是未設置,則查找spring.profiles.defaults
的值;若是均未設置,則沒有激活的profile
,只會建立那些沒有定義在profile
中的bean
。以下是在web.xml
中設置spring.profiles.default
。app
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" ...> <context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param> <servlet> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet>
Spring
提供了@ActiveProfiles
註解啓用profile
。tcp
@Runwith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={PersistenceTestConfig.class}) @ActiveProfiles("dev") public class PersistenceTest { ... }
使用
@Conditional
註解,若是給定條件計算結果爲true
,那麼建立bean
,不然不建立。學習
@Bean @Condition(MagicExistsCondition.class) public MagicBean magicBean() { return new MagicBean(); }
package com.hust.grid.leesf.ch3; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.ClassUtils; public class MagicExistsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); return env.containsProperty("magic"); } }
以下代碼網站
@Autowired public void setDessert(Dessert dessert) { this.dessert = dessert; }
其中Dessert
爲一個接口,其有多個子類。
@Component public class Cake implements Dessert {} @Component public class Cookies implements Dessert {} @Component public class IceCream implements Dessert {}
此時,會發現不止一個
bean
能夠匹配,Spring
會拋出異常,能夠將某個bean
設置爲首選的bean
或使用限定符。
使用
Primary
註解標識首選bean。
@Component @Primary public class IceCream implements Dessert {}
或者使用xml配置首選bean
<bean id="iceCream" class="com.dessertteater.IceCream" primary="true" />
若是配置多個首選
bean
,那麼也將沒法工做。
使用
@Qualifier
註解進行限定。
@Autowired @Qualifier("iceCream") pulbic void setDessert(Dessert dessert) { this.dessert = dessert; }
能夠爲
bean
設置本身的限定符,而不依賴將bean ID
做爲限定符,在bean
的聲明上使用@Qualifier
註解,其能夠與@Component
組合使用。
@Component @Qualifier("cold") public class IceCream implements Dessert {}
這樣,使用以下。
@Autowired @Qualifier("cold") pulbic void setDessert(Dessert dessert) { this.dessert = dessert; }
若是多個
bean
都具有相同特性的話,那麼也會出現問題,沒法肯定惟一bean
,如定義@Cold
註解
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) #Qualifier public @interface Cold {}
這樣就可使用以下註解進行定義
@Component @Cold @Creamy public class IceCream implements Dessert {}
經過自定義註解後,而後能夠經過多個註解的組合肯定惟一一個符合條件的
bean
。
@Autowired @Cold @Creamy pulbic void setDessert(Dessert dessert) { this.dessert = dessert; }
默認狀況下,
Spring
上下文中全部bean
都是做爲以單例形式建立的。但有時候須要多個不一樣的bean
實例,Spring
定義了多種做用域,包括:
bean
實例。Spring
應用上下文獲取時,都會建立一個新的bean
實例。Web
應用中,爲每一個會話建立一個bean
實例。Web
應用中,爲每一個請求建立一個bean
實例。使用@Scope
註解肯定bean
的做用域,如將以下bean
聲明爲原型。
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class NotePad {}
當使用xml文件配置時以下
<bean id="notepad" class="com.hust.grid.leesf.Notepad" scope="prototype" />
在
Web
應用中,可能須要實例化在會話和請求範圍內共享的bean
,如電商網站,須要會話做用域。
@Component @Scope( value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES) public ShoppingCart cart() {}
須要將
ShoppingCart bean
注入到單例StoreService bean
中。
@Component public class StoreService { @Autowired public void setShoppingCart(ShoppingCart shoppingCart) { this.shoppingCart = shoppingCart; } }
此時,因爲
ShoppingCart
是會話做用域,直到某個用戶建立了會話後,纔會出現ShoppingCart
實例,而且Spring
會注入一個代理至StoreService
中,這個代理與ShoppingCart
有相同的方法,當處理時須要將調用委託給會話做用域內真正的ShoppingCart
。
須要使用
Spring aop
命名空間的新元素
<bean id="cart" class="com.hust.grid.leesf.ShoppingCart" scope="session"> <aop:scoped-proxy /> </bean>
上述狀況會使用
CGLib
建立目標類的代理,但也可將proxy-target-class
屬性設置爲false
,進而要求它生成基於接口的代理。
<bean id="cart" class="com.hust.grid.leesf.ShoppingCart" scope="session"> <aop:scoped-proxy proxy-target-class="false" /> </bean>
爲使用
<aop:scoped-proxy>
元素,須要在XML中聲明spring-aop.xsd
命名空間。
不使用硬編碼注入,想讓值在運行時肯定,
Spring
提供了以下兩種方式。
聲明屬性源並經過
Spring
的Environment
來檢索屬性。
... @Configuration @PropertySource("classpath:/com/hust/gird/leesf/app.properties") public class ExpressiveConfig { @Autowired Environment env; @Bean public BlankDisc disc() { return new BlankDisc( env.getProperty("disc.title"), env.getProperty("disc.artist")); } }
經過在
app.properties
中配置對應的屬性完成注入。還可以使用佔位符完成注入。
public BlankDisc( @Value("${disc.title}") String title, @Value("${disc.artist}") String artist) { this.title = title; this.artist = artist; }
爲使用佔位符,須要配置
PropertySourcesPlaceholderConfigurer
。
@Bean public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); }
或者在XML配置文件中使用
<context:property-placeholder />
,這樣會生成一個PropertySourcesPlaceholderConfigurer
的bean
。
本篇學習了更爲高級的裝配技巧,如
Spring profile
,還有條件化裝配bean
,以及bean
的做用域等等。