Spring常用配置

记录Spring中Bean的配置、EL和资源调用、Bean的声明周期、Profile、事件。

Bean的Scope

​ Scope描述的是Spring容器如何新建Bean实例的,通过 @Scope 注解来实现,有以下几种:

  • Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。
  • Prototype:每次调用都新建一个Bean的实例。
  • Request:Web项目中,给每个http request 新建一个Bean实例。
  • Session:Web项目中,给每个http session新建一个Bean实例。
  • GlobalSession:这个只在portal应用中有用,给每个global http session新建一个Bean实例。

singleton 和 prototype 实例:

Singleton的Bean:

1
2
3
4
5
6
7
package scope;

import org.springframework.stereotype.Service;

@Service //默认为Singleton,相当于Scope("singleton")
public class DemoSingletonService {
}

Prototype的Bean:

1
2
3
4
5
6
7
8
9
package scope;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("prototype") //声明Scope为Prototype
public class DemoPrototypeService {
}

配置类:

1
2
3
4
5
6
7
package scope;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("scope")
public class ScopeConfig {
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package scope;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);
DemoSingletonService s1 = context.getBean(DemoSingletonService.class);
DemoSingletonService s2 = context.getBean(DemoSingletonService.class);

DemoPrototypeService p1 = context.getBean(DemoPrototypeService.class);
DemoPrototypeService p2 = context.getBean(DemoPrototypeService.class);

System.out.println("s1与s2是否相等: " + s1.equals(s2));
System.out.println("p1与p2是否相等: " + p1.equals(p2));
}
}


运行结果为:
s1与s2是否相等: true
p1与p2是否相等: false

Spring EL和资源调用

​ Spring EL-Spring 表达式语言支持在xml和注解中使用,类似于JSP的EL表达式,其中涉及调用各种资源包括普通文件、网址、配置文件、系统环境变量等。Spring主要在注解@Value 的参数中使用表达式。本文涉及到的有以下几种情况:

  • 注入普通字符
  • 注入操作系统属性
  • 注入表达式运算结果
  • 注入其他Bean 的属性
  • 注入文件内容
  • 注入网址内容
  • 注入属性文件

示例:

增加commons-io 可简化文件相关操作:

1
2
3
4
5
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>

在包内添加test.txt文件,内容随意~

在包内添加test.properties资源文件,内容如下:

1
2
book.author=yyyy
book.name=xxxx

编辑被注入的Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package el;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class DemoService {
@Value("其他类的属性") //表示注入普通的字符串
private String another;

public String getAnother(){
return another;
}

public void setAnother(String another) {
this.another = another;
}
}

编写演示配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package el;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;

@Configuration
@ComponentScan("el")
@PropertySource("classpath:el/test.properties") //注入配置文件
public class ElConfig {
@Value("I Love You!") //注入普通字符串
private String normal;

@Value("#{systemProperties.os.name}") //注入操作系统属性
private String osName;

@Value("#{ T(java.lang.Math).random() * 100.0 }") //注入表达式结果
private String randomNumber;

@Value("#{demoService.another}") //注入其他Bean属性
private String fromAnother;

@Value("classpath:el/test.txt") //注入文件资源
private Resource testFile;

@Value("http://www.baidu.com") //注入网址资源
private Resource testUrl;

@Value("${book.name}") //注入配置文件
private String bookName;

@Autowired
private Environment environment;

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig(){
return new PropertySourcesPlaceholderConfigurer();
}

public void outputResource() {
try {
System.out.println(normal);
System.out.println(osName);
System.out.println(randomNumber);
System.out.println(fromAnother);

System.out.println(IOUtils.toString(testFile.getInputStream()));
System.out.println(IOUtils.toString(testUrl.getInputStream()));
System.out.println(bookName);
System.out.println(environment.getProperty("book.author"));
}catch (Exception e){
e.printStackTrace();
}
}
}

编写运行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package el;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ElConfig.class);

ElConfig resourceService = context.getBean(ElConfig.class);

resourceService.outputResource();

context.close();
}
}

输出信息:
I Love You!
Windows 10
9.515003283753941
其他类的属性
文本测试
<!DOCTYPE html>
六月 26, 2018 5:04:41 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@531d72ca: startup date [Tue Jun 26 17:04:41 CST 2018]; root of context hierarchy
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

xxxx
yyyy

Bean 的初始化和销毁

​ 在实际开发中,经常会遇到在Bean使用之前和使用之后做些必要的操作,Spring对Bean的生命周期的操作提供了支持,在Java配置和注解配置下提供两种方式:

  • Java配置方式:使用@Bean 的initMethod 和destroyMethod (相当于xml配置的init-method 和 destroy-method)。
  • 注解方式:利用JSR-250的@PostConstruct 和@PreDestroy。

首先在maven配置文件中增加JSR250的支持

1
2
3
4
5
<dependency>
<groupId>javax.annotion</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>

使用@Bean 形式的Bean如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package prepost;

public class BeanWayService { //此类不预先声明为Bean,在配置类中手动创建成为Bean
public void init(){
System.out.println("@Bean-init-method");
}

public BeanWayService(){
super();
System.out.println("初始化构造函数-BeanWayService");
}

public void destroy(){
System.out.println("@Bean-destroy-method");
}
}

使用JSR250形式的Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package prepost;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class JSR250WayService {
@PostConstruct //在构造函数执行完之后执行
public void init(){
System.out.println("jsr250-init-method");
}

public JSR250WayService(){
super();
System.out.println("初始化构造函数-JSR250WayService");
}

@PreDestroy //在Bean销毁之前执行
public void destroy(){
System.out.println("jsr250-destroy-method");
}
}

配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package prepost;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("prepost")
public class PrePostConfig {
@Bean(initMethod = "init", destroyMethod = "destroy") //initMethod和destroyMethod指定BeanWayService类的init和destroy方法在构造函数之后,Bean销毁之前执行
BeanWayService beanWayService(){
return new BeanWayService();
}

@Bean //这里因为在JSR250WayService类中的方法已经指定了,所以不用上述那样
JSR250WayService jsr250WayService(){
return new JSR250WayService();
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package prepost;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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-destroy-method
@Bean-destroy-method

Profile使用

Profile为在不同的环境下使用不同的配置提供了支持:

  • 通过设定Environment 的ActiveProfiles 来设定当前context 需要使用的环境配置。在开发中使用@Profile 注解类或者方法,达到在不同情况下选择实例化不同的Bean。
  • 通过设定jvm的spring.profile.active参数来设置配置环境。
  • Web项目设置在Servlet 的context parameter中。(也就是上下文参数)

下面进行简单的代码演示,示例Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package profile;

public class DemoBean {
private String content;
public DemoBean(String content){
super();
this.content = content;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Profile 配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package profile;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.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时实例化为proDemoBean。
public DemoBean prodDemoBean(){
return new DemoBean("from production profile");
}
}

Main类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package profile;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

context.getEnvironment().setActiveProfiles("prod"); //将活动的Profile设置为prod。
context.register(ProfileConfig.class); //后置注册Bean配置类,不然会报Bean未定义的错误。
context.refresh();

DemoBean demoBean = context.getBean(DemoBean.class);

System.out.println(demoBean.getContent());

context.close();
}
}


运行结果为:
六月 29, 2018 9:17:42 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:17:42 CST 2018]; root of context hierarchy
from production profile
六月 29, 2018 9:17:42 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:17:42 CST 2018]; root of context hierarchy



如果将 "pro" 改为 "dev"
六月 29, 2018 9:18:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:18:37 CST 2018]; root of context hierarchy
from development profile
六月 29, 2018 9:18:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:18:37 CST 2018]; root of context hierarchy

事件(Application)

​ Spring 的事件(Application Event) 为Bean 和 Bean 之间的消息通信提供了支持。当一个Bean 处理完一个任务之后,希望另一个Bean知道并能做出相应的处理,这时我们就能让另一个Bean 监听当前Bean 所发送的事件。(讲到这里,相当了Servlet中也有类似的事件监听,或许是差不多的东西)

Spring 实现事件监听流程:

  1. 自定义事件,继承ApplicationEvent。
  2. 定义事件监听器,实现ApplicationListener。
  3. 使用容器发布事件。

首先,自定义事件,为了让消息更加可视化,传入参数msg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package event;

import org.springframework.context.ApplicationEvent;

public class DemoEvent extends ApplicationEvent {
private String msg;

public DemoEvent(Object source, String msg){
super(source);
this.msg = msg;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}
}

第二步,实现事件监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
package event;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class DemoListener implements ApplicationListener<DemoEvent> { //实现ApplicationListener 接口,并且指定监听的事件类型

public void onApplicationEvent(DemoEvent event) { //使用onApplicationEvent 方法对消息进行接受处理。
String msg = event.getMsg();
System.out.println("我(bean-DemoListener)接收到了bean-demoPublisher发布的消息:" + msg);
}
}

事件发布类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class DemoPublisher {
@Autowired
ApplicationContext context; //注入ApplicationContext用来发布事件。

public void publish(String msg){
context.publishEvent(new DemoEvent(this, msg)); //使用publishEvent方法来发布事件。
}
}

配置类:

1
2
3
4
5
6
7
8
9
package event;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("event")
public class EventConfig {
}

运行函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package event;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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();
}
}

运行结果如下所示:
六月 29, 2018 9:56:20 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:56:20 CST 2018]; root of context hierarchy
我(bean-DemoListener)接收到了bean-demoPublisher发布的消息:hello application event
六月 29, 2018 9:56:20 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@67424e82: startup date [Fri Jun 29 21:56:20 CST 2018]; root of context hierarchy

可以看出,DemoListener成功监听到了DemoPublisher发布的事件。

0%