3.1 环境和配置文件
实际情况中,不同的环境中所应使用的配置是不相同的。
3.1.1 配置配置文件Bean
是在runtime时生效的。
-
使用Java配置:
使用@Profile注解
@Configuration @Profile("dev") public class DevelopmentProfileConfig { @Bean(destroyMethod="shutdown") public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } } @Configuration @Profile("prod") public class ProductionProfileConfig { @Bean public DataSource dataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface( javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
从Spring 3.2开始,@Profile注解开始支持方法级别
@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(); } }
-
通过xml配置文件配置
使用profile属性
<?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" xsi:schemaLocation=" 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
<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="prod"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource" /> </beans>
3.1.2 激活配置文件
两个配置:
-
spring.profiles.active
-
spring.profiles.default
spring.profiles.active的值会决定使用哪个环境的配置文件 如果spring.profiles.active没有设置值,那么就会采用spring.profiles.default的值 如果这两个属性都没有设置值,那么在配置环境中定义的Bean将不会被创建
配置方法:
(1) 在DispatcherServlet中作为初始化参数
(2) 作为web应用的context参数
(3) 作为JNDI的入口参数
(4) 作为环境变量
(5) 作为JVM系统属性
(6) 在集成单元测试中使用@ActiveProfiles注解
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
3.2 有条件的Bean
Spring 4引入了@Conditional注解
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
public interface Condition {
boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
}
传入到@Conditional的参数可以是任何实现了Condition接口的实现类。
public class MagicExistsCondition implements Condition {
public boolean matches(
ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("magic");
}
}
深入探究一下Condition接口:
- ConditionContext
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
ClassLoader getClassLoader();
}
- AnnotatedTypeMetadata
public interface AnnotatedTypeMetadata {
boolean isAnnotated(String annotationType);
Map<String, Object> getAnnotationAttributes(String annotationType);
Map<String, Object> getAnnotationAttributes(
String annotationType, boolean classValuesAsString);
MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType);
MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
class ProfileCondition implements Condition {
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;
}
}
3.3 在自动装配中解决有歧义的Bean
@Autowired
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
@Component
public class Cake implements Dessert { ... }
@Component
public class Cookies implements Dessert { ... }
@Component
public class IceCream implements Dessert { ... }
然后就报错了NoUniqueBeanDefinitionException:
nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.desserteater.Dessert] is defined:
expected single matching bean but found 3: cake,cookies,iceCream
3.3.1 定义一个Primary Bean
- 使用@Primary注解
@Component
@Primary
public class IceCream implements Dessert { ... }
或
@Bean
@Primary
public Dessert iceCream() {
return new IceCream();
}
- 在xml中使用primary属性
<bean id="iceCream"
class="com.desserteater.IceCream"
primary="true" />
3.3.2 使用@Qualifier注解
- 和@Autowired(或@Inject)一起使用来注入Bean
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
- 和@Component(或@Bean)使用来声明Bean
@Component
@Qualifier("cold")
public class IceCream implements Dessert { ... }
- 自定义注解
JAVA不允许同一种注解在一个地方多次使用
// 这个是会报错的
@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert { ... }
@Autowired
@Qualifier("cold")
@Qualifier("creamy")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
于是我们开始搞自定义注解了
// 第一个注解
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }
// 第二个注解
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy { }
@Component
@Cold
@Creamy
public class IceCream implements Dessert { ... }
@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
网友评论