Bean 的 Scope
Scope 描述的是Spring 容器如何新建Bean的实例的。Spring的Scope有以下几种,通过@Scope 注解来实现的。
- Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配,全容器共享一个实例。
- Prototype:每次调用新建一个Bean的实例。
- Request:Web项目中,给每一个http request 新建一个Bean实例。
- Session:Web项目中,给每一个http session 新建一个Bean实例。
- GlobalSession: 这个只在portal应用中有用,给每一个global http session 新建一个Bean实例。
另外,在Spring Batch中还有一个Scope 是使用@StepScope。
示例来演示Singleton和Prototype,分别从Spring容器中获得2次的Bean,判断Bean的实例是否相等。
- 编写Singleton的Bean
@Service
//不指定默认为Singleton,相当于@Scope("sigleton")。
public class DemoSingletonService{
}
- 编写Prototype的Bean
@Service
@Scope("prototype")//声明Scope为Prototype
public class DemoPrototypeService{
}
- 配置类
@Configuration
@ComponentScan("com.cn.lr.scope")
public class ScopeConfig{
}
- 运行
public class Main{
public static void main(String [] args){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
DemoPrototypeService p1 =
new DemoPrototypeService();
DemoPrototypeService p2 =
new DemoPrototypeService();
DemoSingletonService s1 =
new DemoSingletonService();
DemoSingletonService s2 =
new DemoSingletonService();
System.out.println("s1与s2是否相等:"+
s1.equals("s2"));
System.out.println("p1与p2是否相等:"+
p1.equals("p2"));
context.close();
}
}
- 结果
s1与s2是否相等:true
p1与p2是否相等:false
Spring EL 和资源调用
Spring EL-Spring表达式语言,支持在xml和注解中使用表达式,类似于JSP的EL表达式语言。
Spring开发中经常涉及调用各种资源的情况,包含普通文件、网址、配置文件、系统变量等,我们可以使用Spring的表达式语言实现资源的注入。
Spring 主要在注解@Value 的参数中使用表达式。
@Configuration
@ComponentScan("com.cn.lr.el")
@PropertySource("classpath:com/cn/lr/el/test.properties") //7
public class ElConfig{
//注入普通字符
@Value("I am lr")
private String normal;
//注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String osName;
//注入表达式结果
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
//注入其他Bean属性
@Value("#{demoService.another}")
private String fromAnother;
//注入文件资源
@Value("classpath:com/cn/lr/el/test.txt")
private Resource testFile;
//注入网址资源
@Value("http://www.baidu.com")
private Resource testUrl;
//7
@Value("${book.name}")
private String bookName;
//7
@Autowired
private Environment environment;
//7
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigure(){
return new PropertySourcesPlaceholderConfigurer();
}
}
解析注解7:注入配置文件需要使用@PropertySource指定文件地址,若使用@Value注入,则要配置一个PropertySourcesPlaceholderConfigurer的Bean。注意,@Value("\${book.name}")使用的是"$",而不是"#"。
注入Properties还可以从Environment中获得。environment.getProperty(key);
Bean的初始化和销毁
实际开发的时候,经常会遇到在Bean使用之前或者之后做些必要的操作,Spring 对Bean 的生命周期的操作提供了支持。在使用Java配置和注解配置下提供如下两种方式:
Java配置方式:使用@Bean 的initMethod和destoryMethod(相当于xml配置的init-method和destory-method)。
注解方式:利用JSR-250的@PostConstruct 和@PreDestory。
- 使用@Bean形式的Bean。
public class BeanWayService{
public void init(){
System.out.println("@Bean-init-method");
}
public BeanWayService(){
super();
System.out.println("初始化构
造函数-BeanWayService");
}
public void destory(){
System.out.println("@Bean-destory-method");
}
}
- 使用JSR250形式的Bean。
//首先在项目中添加JSR250支持
//<dependency>
//<groupId>javax.annotation</groupId>
//<artifactId>jsr250-api</artifactId>
//<version>1.0</version>
//</dependency>
public class JSR250WayService{
@PostConstruct //在构造函数执行完成之后执行
public void init(){
System.out.println("jsr250-init-method");
}
public JSR250WayService(){
super();
System.out.println("初始化构
造函数-JSR250WayService");
}
@PreDestory //在Bean销毁之前执行
public void destory(){
System.out.println("jsr250-destory-method");
}
}
- 配置类
@Configuration
@ComponentScan("com.cn.lr.prepost")
public class PrePostConfig{
//initMethod和destoryMethod指定BeanWayService类的
//init和destory方法在构造之后、Bean销毁之前执行。
@Bean(initMethod="init",destoryMethod="destory")
BeanWayService beanWayService(){
return new BeanWayService();
}
@Bean
JSR250WayService jsr250WayService(){
return new JSR250WayService();
}
}
- 运行
public class Main{
public static void main(String [] args){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
PrePostConfig.class);
BeanWayService beanWayService =
context.getBean(BeanWayService.class);
JSR250WayService jsr250WayService =
context.getBean(JSR250WayService.class);
context.close();
}
}
- 结果
初始化构造函数-BeanWayService
@Bean-init-method
初始化构造函数-JSR250WayService
jsr250-init-method
程序关闭后:
jsr250-destory-method
@Bean-destory-method
Profile
profile 为在不同环境下使用不同的配置提供了支持(开发环境下的配置和生产环境下的配置肯定是不同的,例如,数据库的配置)。
- 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。在开发中使用@Profile注解类或者方法,达到在不同情况下选择实例化不同的Bean。
- 通过设定jvm的spring.profiles.active参数来设定配置环境。
- Web项目设置在Servlet的context parameter中。
- Servlet 2.5 及以下:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.Di
spatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>production</param-value>
</init-param>
</servlet>
- Servlet 3.0 及以上:
public class WebInit implements
WebApplicationInitializer{
public void onStartup(ServletContext container)
throws ServletException{
container.setInitParameter("spring.profiles.
default", "dev");
}
}
- 注解
@Profile
示例Bean。
public class DemoBean{
private String content;
public DemoBean(String content){
super();
this.content = content;
}
//省略 getter setter 方法
}
Profile 配置
@Configuration
public class ProfileConfig{
@Bean
@Profile("dev")//Profile为dev时实例化devDemoBean
public DemoBean devDemoBean(){
return new DemoBean("from development profile");
}
@Bean
@Profile("prod")//Profile为prod时实例化prodDemoBean
public DemoBean prodDemoBean(){
return new DemoBean("from production profile");
}
}
运行
public class Main{
public static void main(String [] args){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
//先将活动的Profile配置为prod
context.getEnvironment()
.setActiveProfiles("prod");
//后置注册Bean配置类,不然会报Bean未定义的错误
context.register(ProfileConfig.class);
//刷新容器
context.refresh();
DemoBean demoBean =
context.getBean(DemoBean.class);
System.out.println(demoBean.getContent());
context.close();
}
}
结果
from production profile
将context.getEnvironment().setActiveProfiles(“dev”);效果则是:
from development profile
事件(Application Event)
Spring的事件(Application Event)为Bean与Bean之间的消息通信提供了支持。当一个Bean处理完一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让另外一个Bean监听当前Bean所发送的事件。
Spring的事件需要遵循如下流程:
- 自定义事件,继承ApplicationnEvent
- 定义事件监听器,实现ApplicationListener。
3.使用容器发布事件
- 自定义事件
public class DemoEvent extends ApplicationEvent{
private static final long serialVersionUID = 1L;
private String msg;
public DemoEvent(String msg){
super();
this.msg = msg;
}
//省略 getter setter 方法
}
- 事件监听器
@Component
public class DemoListener
implements ApplicationListener<DemoEvent>{//1
public void onApplicationEvent(DemoEvent event){//2
String msg = event.getMsg();
System.out.println("我(bean-demoListener)接收
到了bean-demoPublisher发布的消息:"+ msg);
}
}
//1:实现ApplicationListener接口,并指定监听的事件类型
//2:使用onApplicationEvent方法对消息进行接收处理。
- 事件发布类
@Component
public class DemoPublisher{
//注入ApplicationContext用来发布事件
@Autowired
ApplicationContext applicationContext;
public void publish(String msg){
//使用ApplicationnContext的publishEvent方法来发布
ApplicationContext.publishEvent(new
DemoEvent(this, msg));
}
}
- 配置类
@Configuration
@ComponentScan("com.cn.lr.event")
public class EventConfig{
}
- 运行
public class Main{
public static void main(String [] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
DemoPublisher demoPublisher =
context.getBean(DemoPublisher.class);
demoPublisher.publish("hello application event");
context.close();
}
}
- 结果
我(bean-demoListener)接收到了bean-demoPublisher发布的消息:hello application event
Spring 的java配置方式
Java配置是Spirng4.x推荐的配置方式,可以完全替代xml配置
@Configuration 和 @Bean 这两个注解实现的。
@Configuration 作用于类上,相当于一个xml配置文件
@Bean 作用于方法上,相当于xml配置中的
容器使用的是AnnotationConfigApplicationContext
Spring读取外部的资源配置文件
通过@propertySource可以指定读取的配置文件,通过@Value注解取值。
ps:@PropertySource(Value={“classpath:jdbc.properties”})
@Value("${jdbc.url}")
如果有多个配置文件,怎么办?
可写多个。@PropertySource(Value={“classpath:jdbc.properties”,“xx”,“xxx”})
如果配置文件不存在,怎么办?
ignoreResourceNotFount=true 忽略不存在的。
@PropertySource(Value={“classpath:jdbc.properties”,“xx”,“xxx”},ignoreResourceNotFount=true)
Spring加载xml配置
通过spring提供的@ImportResource来加载xml配置。例如:@ImportResource({“classpath:some-context.xml”,“classpath:another-context.xml”})