【Spring】高级装配
前言
前面讲解了
bean
的核心装配技术,其可应付很多中装配情况,但Spring
提供了高级装配技术,以此实现更为高级的bean
装配功能。
高级装配
配置profile bean
将所有不同
bean
定义放置在一个或多个profile
中,在将应用部署到每个环境时,要确保对应的profile
处于激活
状态。如配置了如下数据源,并使用profile
注解定义。
JavaConfig配置profile
- 开发环境中的数据源配置
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
注解一起使用,上述放在两个不同配置类可以转化为两个方法放在同一个配置类中。
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
才会被激活。
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"
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
。
<?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
。
激活profile
Spring
依赖spring.profiles.active
和spring.profiles.default
两个属性确定哪个profile
处于激活状态,如果设置了spring.profiles.active
,那么其值用于确定哪个profile
是激活状态,如果未设置,则查找spring.profiles.defaults
的值;如果均未设置,则没有激活的profile
,只会创建那些没有定义在profile
中的bean
。如下是在web.xml
中设置spring.profiles.default
。
<?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
。
@Runwith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={PersistenceTestConfig.class})
@ActiveProfiles("dev")
public class PersistenceTest {
...
}
条件化的bean
使用
@Conditional
注解,如果给定条件计算结果为true
,那么创建bean
,否则不创建。
- MagicBean
@Bean
@Condition(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
- MagicExistsCondition
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
或使用限定符。
标识首选bean
使用
Primary
注解标识首选bean。
@Component
@Primary
public class IceCream implements Dessert {}
或者使用xml配置首选bean
<bean id="iceCream"
class="com.dessertteater.IceCream"
primary="true" />
如果配置多个首选
bean
,那么也将无法工作。
限定自动装配的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;
}
bean的作用域
默认情况下,
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
。
在XML中声明作用域代理
需要使用
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表达式语言
注入外部的值
声明属性源并通过
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
的作用域等等。
【Spring】高级装配的更多相关文章
- Spring高级装配
Spring高级装配 目录 一.Profile(根据开发环境创建对应的bean) 二.条件化的创建bean(根据条件创建bean) 三.处理自动装配歧义性(指定首选bean.限定符限制bean) 四. ...
- Spring高级装配(一) profile
Spring高级装配要学习的内容包括: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 以上属于高级一点的bean装配技术,如果你没有啥 ...
- Spring高级装配bean
目录 spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 一.环境与profile 配置profile bean 在软件开发的时候,有一个 ...
- (二)spring 高级装配-Condition -条件化的bean
Condition:满足某个特定条件的情况下创建bean 条件化配置bean: a:@Conditional 指定一个class ,它指明了通过条件对比的类.如果没有指定class则通过Condito ...
- (一)spring 高级装配-@Profile
1.环境与profile 示例:数据库配置 a:通过@Bean注解,通过EmbeddedDatabaseBuilder创建数据源 @Bean(destroyMethod="shutdown& ...
- Spring高级装配(二) 条件化的bean
如果你希望一个bean在特定的条件下才会出现: 应用的类路径下包含特定的库时才创建 只有当某个特定的bean也声明之后才会创建 某个特定的环境变量设定之后才创建某个bean 在Spring 4之前,很 ...
- (三)Spring 高级装配 bean的作用域@Scope
1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...
- Spring实战(四)Spring高级装配中的bean profile
profile的原意为轮廓.剖面等,软件开发中可以译为“配置”. 在3.1版本中,Spring引入了bean profile的功能.要使用profile,首先要将所有不同的bean定义整理到一个或多个 ...
- 【Spring 核心】高级装配
高级装配用来适应开发和生产 不同环境下的软切换 一.环境与profile 1.开发环境下的profile package com.bonc.config; import javax.sql.DataS ...
随机推荐
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(86)-日程管理-fullcalendar插件用法
前言 本文分享fullcalendar用法,最后面提供代码下载 说到日程管理,基于JQuery的插件FullCalendar当之无愧,完整的API稳定和调用方式,非常易于扩展!可以用于系统的个人历程管 ...
- MongoDb 入门教程
MongoDb 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. 它是可扩展的高性能数据存储解决方案,经常被用于非关系型数据的存储,能存储海量的数据. 常 ...
- Spring《错误集合,总结更新》
1.这几天配置springmvc 使用注解,并且自动扫描注解,当我单个配置,不用自动扫描,出现下面错误,找了很多人跟我看,配置也没问题,但是就是显示不出东西,所说的类也去看了,没有问题 這是我的模拟数 ...
- ios 初体验<UILabel控件>
创建控件: UILabel *label = [[UILabel alloc]init]; //设置控件大小 label.frame = CGRectMake(50,100,300,40);//分别为 ...
- CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用
作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...
- Brotli、Deflate、Zopfli、LZMA、LZHAM、Bzip2六种无损数据压缩性能比较
这里比较了六种数据压缩算法,结果表明Brotli算法会代替普遍使用的Deflate算法.分别用Canterbury compression corpus,web contentcorpus,和 enw ...
- 解决 SQL 注入的另类方法
本文是翻译,版权归原作者所有 原文地址(original source):https://bitcoinrevolt.wordpress.com/2016/03/08/solving-the-prob ...
- 团队项目beta 汇总
一.冲刺计划安排 http://www.cnblogs.com/KKlist/p/6864124.html 二.七天的敏捷冲刺 第一天(2017.05.19) http://www.cnblogs.c ...
- 团队作业4——第一次项目冲刺(Alpha版本)4.25
团队作业4--第一次项目冲刺(Alpha版本) Day four: 会议照片 每日站立会议: 项目进展 今天是项目的Alpha敏捷冲刺的第四天,先大概整理下昨天已完成的任务以及今天计划完成的任务.今天 ...
- 201521123014 《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 泛型(编写的代码可被不同类型的对象所重用) Java中一个集合可以放任何类型的对象,因为任何对象都 is-a ...