前面讲解了
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
的做用域等等。