Spring框架-Spring Bean

1. bean的配置

Spring可以看做一个大工厂,用于生产和管理Spring容器中的Bean,需要开发者将Bean配置在Spring的配置文件中。Spring框架支持XML和Properties两种格式的配置文件,实际开发中常用XML格式的配置文件。

从上一篇博客可知XML配置文件的根元素是,中包含多个子元素,每个元素定义一个Bean,并描述Bean如何装配到Spring容器中。元素常用属性及其子元素如下:

属性或子元素名称 描述
id 是一个Bean的唯一标识符,Spring容器对Bean的配置、管理都通过该属性来完成
name Spring容器同样可以通过此属性对容器中的Bean进行配置和管理,name属性中可以未Bean指定多个名称,每个名称之间用逗号或分号隔开
class 该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的权限定名
scope 用来设定Bean实例的作用域,其属性有singleton(单例)、prototype(原型)、request、session、global Session、application 和websocket。其默认值为singleton
constructor-arg 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的index属性指定构造参数的序号(从0开始),type属性指定构造参数的类型,参数值可以通过ref属性或value属性直接指定,也可以通过ref或value子元素指定
property 元素的子元素,用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入。该元素的name属性指定Bean实例中的相应属性名,ref属性或value属性用于指定参数值
ref 等元素的属性或子元素,可以用于指定对Bean工厂中某个Bean实例的引用
value 等元素的属性或子元素。可以用于直接指定一个常量值
list 用于封装List或数组类型的依赖注入
set 用于封装Set类型属性的依赖注入
entry 元素的子元素,用于设置一个键值对。其key属性指定字符串类型的键值,ref或value子元素指定其值,也可以通过value-ref或value属性指定其值
map 用于封装Map类型属性的依赖注入

2. bean的实例化

第一种:构造方法实例化(第一种是重点,第二三种已经没太多人用了)

1.创建一个类:

bean1.java:

1
2
3
4
5
6
7
package com.spring.demo;

public class Bean1 {
public void print(){
System.out.println("Bean1......");
}
}

这个bean只有一个print方法

2.在Spring配置文件Bean.xml 中 配置 bean:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 关键部分 -->
<bean id="bean1" class="com.spring.demo.Bean1"></bean>

</beans>

id 为 在 xml 里的这个 bean的标识。 class 为xml 里的这个bean 绑定的java类(bean)的全路径(包名+类名)

这个标签会自动寻找 Bean1类中的无参数构造函数来创建对象

3.测试代码从配置文件中取出Bean1对象:

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

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TextIoc {
@Test
public void textUser()
{
//1.获取spring文件
ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
//2.由配置文件返回对象
Bean1 b = (Bean1)context.getBean("bean1");
System.out.println(b);
b.print();
}
}

getBean() 返回的就是由spring 实例化的对象。

结果是:打印返回的对象b,和调用 b 的print()方法

第二种:使用静态工厂创建

1.创建一个类:

bean2.java:(就是要创建这个类的对象)

1
2
3
4
5
6
7
package com.spring.demo;

public class Bean2 {
public void print(){
System.out.println("Bean2......");
}
}

2.配置spring的配置文件

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 关键部分 -->
<bean id="bean2" class="com.spring.demo.Bean2Factory" factory-method="getBean2"></bean>
</beans>

表示创建的对象(bean)用bean2指代,使用到的class(类)为com.spring.demo 包 下的Bean2Factory类(作为工厂类)

factory-method = “getBean2”表示调用 class 下的 getBean2方法来创建对象(而且factory-method指定的方法必须为static静态方法)

3.定义静态工厂类

1
2
3
4
5
6
7
package com.spring.demo;

public class Bean2Factory {
public static Bean2 getBean2(){
return new Bean2();
}
}

注意静态工厂类的创建对象方法为静态

4.测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class TextIoc {
@Test
public void textUser()
{
//1.获取spring文件
ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
//2.由配置文件返回对象
Bean3 b = (Bean2)context.getBean("bean2");
System.out.println(b);
b.print();
}
}

第三种:使用实例工厂创建

1
2
3
4
5
6
7
package com.spring.demo;

public class Bean3 {
public void print(){
System.out.println("Bean3......");
}
}

2.配置spring的配置文件:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 关键部分 -->
<bean id="bean3factory" class="com.spring.demo.Bean3Factory"></bean>
<bean id="bean3" factory-bean="bean3factory" factory-method="getBean3"></bean>
</beans>

第八行: :为在xml配置文件中定义一个bean,这个bean的id 为bean3factory ,创建所用到的类(class)为com.spring.demo 包下的 Bean3Factory类

第九行:

在定义一个bean,id 为bean3,生产它的工厂类为 bean3factory,调用 bean3factory生产 bean3的方法为 (bean3factory的)getBean3()方法

3.(非静态)工厂类

1
2
3
4
5
6
7
package com.spring.demo;

public class Bean3Factory {
public Bean3 getBean3(){
return new Bean3();
}
}

4.测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class TextIoc {
@Test
public void textUser()
{
//1.获取spring文件
ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
//2.由配置文件返回对象
Bean3 b = (Bean3)context.getBean("bean3");
System.out.println(b);
b.print();
}
}

运行:

打印出对象,和调用bean3的print方法

静态工厂类与非静态工厂类的区别是,前者不需要创建对象,直接可以调用静态方法创建bean;后者则要先创建对象,然后再通过对象调用其方法创建bean。

3. Bean的作用域

在Spring中不仅可以完成Bean的实例化,还可以为Bean指定作用域。

Spring Framework中,总共定义了6种bean 的作用域,其中有4种作用域只有当应用为web应用的时候才有效,并且Spring还支持自定义作用域。

下表描述了这6种作用域:

Scope Description
singleton (默认的)使用singleton定义的Bean在Spring容器中只有一个Bean实例
prototype 一个bean定义可以有多个bean实例。
request 一个bean定义对应于单个HTTP 请求的生命周期。也就是说,每个HTTP 请求都有一个bean实例,且该实例仅在这个HTTP 请求的生命周期里有效。该作用域仅适用于WebApplicationContext环境。
session 一个bean 定义对应于单个HTTP Session 的生命周期,也就是说,每个HTTP Session 都有一个bean实例,且该实例仅在这个HTTP Session 的生命周期里有效。该作用域仅适用于WebApplicationContext环境。
application 一个bean 定义对应于单个ServletContext 的生命周期。该作用域仅适用于WebApplicationContext环境。
websocket 一个bean 定义对应于单个websocket 的生命周期。该作用域仅适用于WebApplicationContext环境。
3.1.1 singleton作用域

singleton作用域表示在整个Spring容器中一个bean定义只生成了唯一的一个bean实例,被Spring容器管理。所有对这个bean的请求和引用都会返回这个bean实例。

下面的图说明了singleton作用域是如何工作的:

在这里插入图片描述

上图中,有3个地方引用了这个bean,这3个引用指向的都是同一个bean实例。

singleton作用域是Spring中默认的作用域,可以在定义bean的时候指定或者不指定都可以,如下:

1
2
3
4
5
<!-- 不指定作用域,默认是singleton -->
<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- 显示指定作用域为singleton -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
3.1.2 prototype作用域

prototype作用域表示的是一个bean定义可以创建多个bean实例,有点像一个类可以new多个实例一样。

也就是说,当注入到其他的bean中或者对这个bean定义调用getBean()时,都会生成一个新的bean实例。

作为规则,应该对所有有状态的bean指定prototype作用域,对所有无状态的bean指定singleton作用域。

下图描述了prototype作用域是如何工作的:

在这里插入图片描述

上图中,每个引用对应一个新的bean实例。

请注意,上图中的例子不适用于生产环境。因为DAO通常来说是无状态的bean,应该指定它的作用域为singleton比较合适。

xml中可以这样定义prototype作用域:

1
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

和其他作用域不同的是,Spring并不管理作用域为prototypebean的整个生命周期。Spring容器实例化它、配置它、组装它,然后就将bean交给给使用者了,之后就不会对这个bean进行管理了。因此,Spring不会调用该bean的销毁生命周期回调,使用者必须自己销毁 这个bean并释放资源。如果想让Spring来销毁它并释放资源,请使用自定义的bean post-processor

4. Bean的生命周期

Spring框架可以管理singleton作用域的生命周期,不可以管理prototype的,因为对于prototype作用域的bean,Spring只负责创建,当容器创建了Bean的实例后,Bean实例就交给了客户端的代码管理,Spring容器不再跟踪其生命周期,而且不会管理那些配置作用域为prototype的bean。

Spring中 bean的生命周期是一个很复杂的过程,可借鉴Servlet的生命周期:

1.实例化

2.初始化(init)

3.接受请求(service)

4.销毁(destroy)

Bean的生命周期如下:

​ 1、实例化一个Bean--也就是我们常说的new;

​ 2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

​ 3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

​ 4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

​ 5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

​ 6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

​ 7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

​ 8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;

​ 注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。

​ 9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

​ 10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

5. Bean的装配方式

Bean的装配可以理解为依赖关系注入Bean的装配方式即Bean依赖注入的方式.Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配(其中最常用的是基于注解的装配),接下来将对前两种进行详细讲解.

1.基于xml的装配

(1)创建Java类,提供有参,无参构造方法,以及属性的set方法.

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
package xml;

import java.util.List;

public class User {
private String username;
private Integer password;
private List<String> list;

public void setUsername(String username) {
this.username = username;
}

public void setPassword(Integer password) {
this.password = password;
}

public void setList(List<String> list) {
this.list = list;
}

public User(String username, Integer password, List<String> list) {
super();
this.username = username;
this.password = password;
this.list = list;
}
public User() {
super();
}

public User(String i) {
username = s;
}
@Override
public String toString() {
return "User{" + "username='" + username + '\'' + ", password=" + password + ", list=" + list + '}';
}
}

(2)配置xml

1
2
3
4
5
6
7
<bean id="user1" class="xml.User">
<constructor-arg index="0" value="tom"/>
</bean>

<bean id="user2" class="xml.User" >
<property name="username" value="cat"/>
</bean>

(3)测试类

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(applicationContext.getBean("user1"));
System.out.println(applicationContext.getBean("user2"));
}
}

运行结果如下:

User{username=’tom’, password=null, list=null}

User{username=’cat’, password=null, list=null}

2.Annotation注解:

基于XML的装配可能会导致XML配置文件过于臃肿,给后续的维护和升级带来一定的困难。为此,Spring提供了对Annotation(注解)技术的全面支持。

(1)xml配置

<context:component-scan base-package=”annotation” />

上面为自动扫描包下所有Bean.但是不会自动装配,在此结合注解使用.

(2)UserDao接口的配置(略)以及UserDaoImpl类:

1
2
3
4
5
6
7
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userdao...save...");
}
}

(3)UserService接口的配置(略)以及UserServiceImpl类:

1
2
3
4
5
6
7
8
9
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
this.userDao.save();
}
}

(4)UserController

1
2
3
4
5
6
7
8
@Controller("userController")
public class UserController {
@Resource(name="userService")
private UserService userService;
public void save(){
this.userService.save();
}
}

(5)测试类

1
2
3
4
5
6
7
8
public class Test1 {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController = (UserController) applicationContext.getBean("userController");
userController.save();
}
}

运行结果如下:

userdao…save…

(参考自:<<JavaEE企业级开发应用教程>>)

-------------本文结束感谢您的阅读-------------
0%