内容:条件创建bean;指定Scope;解决自动装配多个实现类;表达式语言SpEL
1,Spring之profile,通过@Profile注解给相应的bean设置创建条件
以下展示3种环境下DataSource的条件创建。
//只有当规定的profile激活时,其下的bean才会创建,未设置profile不受影响
@Configuration
public class DataSourceConfig {
@Bean(destroyMethod = "shutdown")
@Profile("dev")
//开发环境
public DataSource embeddedDataSource() {
return (DataSource) 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();
}
@Bean(destroyMethod = "close")
@Profile("qa")
//QA环境
public DataSource basicDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql:///mybase");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setInitialSize(10);
dataSource.setMaxTotal(20);
return (DataSource) dataSource;
}
}
测试类里,要激活设置的条件,即使用哪个环境下的DataSource,通过@ActiveProfiles注解进行激活。如下所示:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("dev")//设置为开发环境,其他环境的bean不创建
public class TestProfile {
……
}
当然,也可以通过xml进行profile的设置,即在beans下使用beans。如下:
<beans xmlns=……
<!-- 开发环境 -->
<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环境 -->
<beans profile="qa">
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
destroy-method="close" p:url="jdbc:mysql:///mybase" p:driverClassName="com.mysql.jdbc.Driver" p:username="root"
p:password="root" p:initialSize="10" p:maxTotal="20">
</bean>
</beans>
<!-- 生产环境 -->
<beans profile="prod">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource"></jee:jndi-lookup>
</beans>
</beans>
2,使用@Conditional+Condition接口实现条件化创建bean
先实现Condition接口:
public class MagicExistCondition implementsCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
Environment en = context.getEnvironment();
return en.containsProperty("magic");
}
}
然后在创建bean的时候引入@Conditional注解:
public class ConditionalCreateMagicBean {
@Bean
@Conditional(MagicExistCondition.class)
public MagicBean getMagicBean() {
return new MagicBean();
}
}
3,Spring默认创建的是单例,通过设置bean上的@scope注解,可以进行修改。
如:
@Component("banana")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//比直接使用"prototype"更加安全
public class Banana implements Fruit {
@Bean
@Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES)
//Scope里第二个属性解决了会话bean注入到单例bean的问题,因为会话bean是访问时才有的
//会话bean是委托给一个域代理bean的,
//然后域代理注入单例bean(cglib代理第二个属性值INTERFACES改为TARGET_CLASS)
public Orange getOrange() {
return new Orange();
}
}
当然,也可以通过xml配置:
<bean name="orange" class="scopeChoice.Orange"
scope="session">
<aop:scoped-proxy/><!-- 默认使用cglib代理 -->
<aop:scoped-proxy proxy-target-class="false"/><!-- 使用接口代理方式 -->
</bean>
4,我们使用自动装配@Autowired注解,如果一个接口有多个实现类,那么它不知道该自动装配哪个bean,此时可以使用@Primary 注解,表示优先使用此实现类。也可以通过具名化即@Qualifier解决。使用如下:
@Component("orange")
@Primary // 对应xml中bean属性设置primary="true"
public class Orange implements Fruit {
}
及
@Component("banana")
public class Banana implements Fruit {
@Bean
@Qualifier("method-orange")
public Orange getOrange() {
return new Orange();
}
}
测试类:对于非优先装配bean,要同时使用 @Autowired和@Qualifier注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FruitConfig.class)
public class TestFruit {
@Autowired //优先orange
private Fruit f1;
@Autowired
@Qualifier("method-orange")
private Fruit f2;
@Autowired
@Qualifier("banana")
private Fruit f3;
@Test
public void test() {
System.out.println(f1);
System.out.println(f2);
System.out.println(f3);
}
}
5,Spring强大的表达式语言SpEL
a,占位符:${} 用于从外部文件读取数据
在类的上面引入外部文件:
@Configuration
@PropertySource("classpath:/fruit.properties")
要使用占位符,必须配置一个PropertySourcesPlaceholderConfigurer bean(推荐)或一个PropertyPlaceholderConfigurer bean,如:
@Bean
//对应xml文件配置是context中的<context:property-placeholder/>
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
b.T()运算符 将括号里内容认为是java类型,直接调用常量或静态方法。如
#{T(System).currentTimeMillis()}
c,其他SpEL用法:#{} 如下:
/**
* #{'hello'}计算字面值
* #{user}引用其他id的bean,#{user.setUser()} 调用方法
* #{user.getUser()?.setName()} 多了一个问号,进行非空检查,如果空,后面的方法不予执行,返回null
* #{user.getUser()?:user} 常用三元表达式,如果空就以默认值代替
* #{str matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'} 正则表达式
* #{users[2].setName()} 处理集合
* #{array.?[str eq 'hehe']} 过滤集合,属于hehe的放在一个子集中
* 上面.?[] 还可以用.^[] 或者.$[] 或.![](投影算符)代替,返回第一个匹配项或最后一个匹配项或非指定项目的子集
*
*/