Spring Ioc容器

IoC容器

IoC 容器(也叫 Spring 容器)是 Spring 的核心。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。

Spring 中使用的对象都由 IoC 容器管理,不需要手动使用new运算符创建对象。由 IoC 容器管理的对象称为Spring BeanSpring Bean就是 Java 对象,和使用new运算符创建的对象没有区别。

Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。

Spring 提供 2 种不同类型的 IoC 容器,即BeanFactoryApplicationContext容器。

BeanFactory容器

BeanFactoryorg.springframework.beans.facytory.BeanFactory接口定义,采用懒加载,所以容器启动比较快。

简单来说,BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法。

BeanFactory接口有多个实现类,最常见的是org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配Bean的。

使用BeanFactory需要创建XmlBeanFactory类的实例,通过XmlBeanFactory类的构造函数来传递Resource对象。

1
2
Resource resource = new ClassPathResource("applicationContext.xml"); 
BeanFactory factory = new XmlBeanFactory(resource);

ApplicationContext容器

ApplicationContext继承了BeanFactory接口,由org.springframework.context.ApplicationContext接口定义,对象在启动容器时加载。

ApplicationContextBeanFactory的基础上增加了很多企业级功能,例如 AOP、国际化、事件支持等。

ApplicationContext接口有两个常用的实现类。

1.ClassPathXmlApplicationContext

该类从类路径ClassPath中寻找指定的 XML 配置文件,并完成ApplicationContext的实例化工作。

1
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);

configLocation参数用于指定 Spring 配置文件的名称和位置,如applicationContext.xml

2. FileSystemXmlApplicationContext

该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成ApplicationContext的实例化工作。

1
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);

它与ClassPathXmlApplicationContext的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如F:/workspaces/applicationContext.xml

在使用 Spring 框架时,可以通过实例化其中任何一个类创建 Spring 的ApplicationContext容器。通常会采用通过ClassPathXmlApplicationContext类实例化ApplicationContext容器的方式。

需要注意的是,BeanFactoryApplicationContext都是通过 XML 配置文件加载Bean的。二者的主要区别在于,如果Bean的某一个属性没有注入,则使用BeanFacotry加载后,在第一次调用getBean()方法时会抛出异常,而ApplicationContext则在初始化时自检,这样有利于检查所依赖的属性是否注入。

因此,在实际开发中,通常都选择使用ApplicationContext,而只有在系统资源较少时,才考虑使用BeanFactory

Bean定义

由 IoC 容器管理的对象称为BeanBean根据 Spring 配置文件中的信息创建。

Spring 容器可以被看作一个大工厂,而容器中的Bean就相当于该工厂的产品。如果希望这个大工厂能够生产和管理Bean,则需要告诉容器需要哪些Bean,以及需要以何种方式将这些Bean装配到一起。

Spring 配置文件支持两种格式,分别是XML文件格式和Properties文件格式。

  • Properties配置文件主要以key-value键值对的形式存在,只能赋值,不能进行其他操作,适用于简单的属性配置。
  • XML配置文件是树形结构,相对于Properties文件来说更加灵活。XML配置文件结构清晰,但是内容比较繁琐,适用于大型复杂的项目。

通常情况下,Spring 的配置文件使用XML格式,XML配置文件的根元素是<beans>,该元素包含了多个<bean>子元素,每一个<bean>子元素定义了一个Bean,并描述了该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" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!-- 使用id属性定义person1,其对应的实现类为com.mengma.person1 -->
<bean id="person1" class="com.mengma.damain.Person1" />
<!--使用name属性定义person2,其对应的实现类为com.mengma.domain.Person2-->
<bean name="Person2" class="com.mengma.domain.Person2"/>
</beans>

<bean>元素常用属性:

属性名称 描述
id 是一个Bean的唯一标识符,Spring 容器对Bean的配置和管理都通过该属性完成
name Spring 容器同样可以通过此属性对容器中的Bean进行配置和管理,name属性中可以为Bean指定多个名称,每个名称之间用逗号或分号隔开
class 该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的全限定名
scope 用于设定Bean实例的作用域,其属性值有singleton(单例)、prototype(原型)、request、sessionglobal Session。默认值是singleton
constructor-arg <bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的index属性指定构造参数的序号(从 0 开始),type属性指定构造参数的类型
property <bean>元素的子元素,用于调用Bean实例中的Set方法完成属性赋值,从而完成依赖注入。该元素的name属性指定Bean实例中的相应属性名
ref <property><constructor-arg>等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用
value <property><constractor-arg>等元素的子元素,用于直接指定一个常量值
list 用于封装List或数组类型的依赖注入
set 用于封装Set类型属性的依赖注入
map 用于封装Map类型属性的依赖注入
entry <map>元素的子元素,用于设置一个键值对。其key属性指定字符串类型的键值,refvalue子元素指定其值
init-method 容器加载Bean时调用该方法
destroy-method 容器删除Bean时调用该方法,该方法只在scope=singleton时有效
lazy-init 懒加载,值为true,容器在首次请求时才会创建Bean实例;值为false,容器在启动时创建Bean实例。该方法只在scope=singleton时有效

Bean作用域

在配置文件中,除了可以定义Bean的属性值和相互之间的依赖关系,还可以声明Bean的作用域。例如,如果每次获取Bean时,都需要一个Bean实例,那么应该将Beanscope属性定义为prototype,如果 Spring 需要每次都返回一个相同的Bean实例,则应将Beanscope属性定义为singleton

作用域的种类

Spring 容器在初始化一个Bean的实例时,同时会指定该实例的作用域。Spring 支持以下 6 种作用域。

  1. singleton
    默认值,单例模式,表示在容器中只有一个Bean实例,Bean以单例的方式存在。
  2. prototype
    原型模式,表示每次通过容器获取Bean时,容器都会创建一个Bean实例。
  3. request
    每次 HTTP 请求,容器都会创建一个Bean实例。该作用域只在当前 HTTP Request内有效。
  4. session
    同一个 HTTP Session共享一个Bean实例,不同的Session使用不同的Bean实例。该作用域仅在当前 HTTP Session内有效。
  5. application
    同一个 Web 应用共享一个Bean实例,该作用域在当前ServletContext内有效。
    类似于singleton,不同的是,singleton表示每个 IoC 容器中仅有一个Bean实例,而同一个 Web 应用中可能会有多个 IoC 容器,但一个 Web 应用只会有一个ServletContext,也可以说application才是 Web 应用中货真价实的单例模式。
  6. websocket
    websocket的作用域是WebSocket,即在整个WebSocket中有效。

request、session、application、websocket作用域只能在 Web 环境下使用,如果使用ClassPathXmlApplicationContext加载这些作用域中的任意一个的Bean,就会抛出以下异常。

1
java.lang.IllegalStateException: No Scope registered for scope name 'xxx'

singleton

singleton是 Spring 容器默认的作用域,当Bean的作用域为singleton时,Spring 容器中只会存在一个共享的Bean实例,该Bean实例将存储在高速缓存中,并且所有对Bean的请求,只要id与该Bean定义相匹配,都会返回该缓存对象。

通常情况下,这种单例模式对于无会话状态的Bean(如 DAO 层、Service 层)来说,是最理想的选择。

在 Spring 配置文件中,可以使用<bean>元素的scope属性,将Bean的作用域定义成singleton

1
<bean id="..." class="..." scope="singleton"/>

prototype

使用prototype作用域的Bean会在每次请求该Bean时都会创建一个新的Bean实例。prototype作用域适用于需要保持会话状态的 Bean

在 Spring 配置文件中,可以使用<bean>元素的scope属性,将Bean的作用域定义成prototype

1
<bean id="..." class="..." scope="prototype"/>

Bean生命周期

Spring 根据Bean的作用域来选择管理方式。对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;而对于prototype作用域的Bean,Spring 只负责创建,当容器创建了Bean的实例后,Bean的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。

Bean生命周期执行流程

Spring 容器在确保一个Bean能够使用之前,会进行很多工作。Bean的生命周期流程:

Bean生命周期的整个执行过程描述如下:

  1. Spring 启动,查找并加载需要被 Spring 管理的Bean,并实例化Bean
  2. 利用依赖注入完成Bean中所有属性值的配置注入。
  3. 如果Bean实现了BeanNameAware接口,则 Spring 调用BeansetBeanName()方法传入当前Beanid值。
  4. 如果Bean实现了BeanFactoryAware接口,则 Spring 调用setBeanFactory()方法传入当前工厂实例的引用。
  5. 如果Bean实现了ApplicationContextAware接口,则 Spring 调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
  6. 如果Bean实现了BeanPostProcessor接口,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation()Bean进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
  7. 如果Bean实现了InitializingBean接口,则 Spring 将调用afterPropertiesSet()方法。
  8. 如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
  9. 如果BeanPostProcessorBean关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。
  10. 如果在<bean>中指定了该Bean的作用范围为scope="singleton",则将该Bean放入 Spring IoC 的缓存池中,将触发 Spring 对该Bean的生命周期管理;如果在<bean>中指定了该Bean的作用范围为scope="prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring 不再管理该Bean
  11. 如果Bean实现了DisposableBean接口,则 Spring 会调用destory()方法将 Spring 中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则 Spring 将调用该方法对Bean进行销毁。

Spring 为Bean提供了细致全面的生命周期过程,通过实现特定的接口或<bean>的属性设置,都可以对Bean的生命周期过程产生影响。

了解 Spring 生命周期的意义就在于,可以利用Bean在其存活期间的指定时刻完成一些相关操作。一般情况下,会在Bean被初始化后和被销毁前执行一些相关操作。

Spring 提供了 3 种方法实现初始化回调和销毁回调:

  • 实现InitializingBeanDisposableBean接口;
  • 在 XML 中配置init-methoddestory-method
  • 使用@PostConstruct@PreDestory注解。

在一个Bean中有多种生命周期回调方法时,优先级为:注解 > 接口 > XML。不建议使用接口和注解,这会让pojo类和 Spring 框架紧耦合。

初始化回调

1. 使用接口

org.springframework.beans.factory.InitializingBean接口提供了以下方法:void afterPropertiesSet() throws Exception;

可以实现以上接口,在afterPropertiesSet方法内指定Bean初始化后需要执行的操作。

1
2
3
4
5
6
7
8
<bean id="..." class="..." />

public class User implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("调用接口:InitializingBean,方法:afterPropertiesSet,无参数");
}
}

2. 配置XML

可以通过init-method属性指定Bean初始化后执行的方法。

1
2
3
4
5
6
7
<bean id="..." class="..." init-method="init"/>

public class User {
public void init() {
System.out.println("调用init-method指定的初始化方法:init" );
}
}

3. 使用注解

使用@PostConstruct注解标明该方法为Bean初始化后的方法。

1
2
3
4
5
6
public class ExampleBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct注解指定的初始化方法:init" );
}
}

销毁回调

1. 使用接口

org.springframework.beans.factory.DisposableBean接口提供了以下方法:

1
void destroy() throws Exception;

您可以实现以上接口,在destroy方法内指定Bean初始化后需要执行的操作。

1
2
3
4
5
6
7
<bean id="..." class="..." />
public class User implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("调用接口:DisposableBean,方法:destroy,无参数");
}
}

2. 配置XML

可以通过destroy-method属性指定Bean销毁后执行的方法。

1
2
3
4
5
6
<bean id="..." class="..." destroy-method="destroy"/>
public class User {
public void destroy() {
System.out.println("调用destroy-method指定的销毁方法:destroy" );
}
}

3. 使用注解

使用@PreDestory注解标明该方法为 Bean 销毁前执行的方法。

1
2
3
4
5
6
public class ExampleBean {
@PreDestory
public void destroy() {
System.out.println("@PreDestory注解指定的初始化方法:destroy" );
}
}

默认的初始化和销毁方法

如果多个Bean需要使用相同的初始化或者销毁方法,不用为每个bean声明初始化和销毁方法,可以使用default-init-methoddefault-destroy-method属性。

1
2
3
4
5
6
7
8
9
10
<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-3.0.xsd"
default-init-method="init"
default-destroy-method="destroy">
<bean id="..." class="...">
...
</bean>
</beans>

后置处理器BeanPostProcessor

BeanPostProcessor接口也被称为后置处理器,通过该接口可以自定义调用初始化前后执行的操作方法。

BeanPostProcessor接口源码如下:

1
2
3
4
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

postProcessBeforeInitializationBean实例化、依赖注入后,初始化前调用。postProcessAfterInitializationBean实例化、依赖注入、初始化都完成后调用。

当需要添加多个后置处理器实现类时,默认情况下 Spring 容器会根据后置处理器的定义顺序来依次调用。也可以通过实现Ordered接口的getOrder方法指定后置处理器的执行顺序。该方法返回值为整数,默认值为 0,值越大优先级越低。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package net.biancheng;
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Message : " + message);
}
public void init() {
System.out.println("Bean正在初始化");
}
public void destroy() {
System.out.println("Bean将要被销毁");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package net.biancheng;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
public class InitHelloWorld implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("A Before : " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("A After : " + beanName);
return bean;
}
@Override
public int getOrder() {
return 5;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package net.biancheng;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
public class InitHelloWorld2 implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("B Before : " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("B After : " + beanName);
return bean;
}
@Override
public int getOrder() {
return 0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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-3.0.xsd">
<bean id="helloWorld" class="net.biancheng.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!" />
</bean>
<!-- 注册处理器 -->
<bean class="net.biancheng.InitHelloWorld" />
<bean class="net.biancheng.InitHelloWorld2" />
</beans>
1
2
3
4
5
6
7
8
9
10
11
package net.biancheng;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
context.registerShutdownHook();
}
}

运行结果如下。

1
2
3
4
5
6
7
B Before : helloWorld
A Before : helloWorld
Bean正在初始化
B After : helloWorld
A After : helloWorld
Message : Hello World!
Bean将要被销毁

由运行结果可以看出,postProcessBeforeInitialization方法是在Bean实例化和依赖注入后,自定义初始化方法前执行的。而postProcessAfterInitialization方法是在自定义初始化方法后执行的。由于getOrder方法返回值越大,优先级越低,所以InitHelloWorld2先执行。

Bean继承

Bean定义可以包含很多配置信息,包括构造函数参数、属性值和容器的一些具体信息,如初始化方法、销毁方法等。子Bean可以继承父Bean的配置数据,根据需要,子Bean可以重写值或添加其它值。

需要注意的是,Spring Bean定义的继承与 Java 中的继承无关。您可以将父Bean的定义作为一个模板,其它子Bean从父Bean中继承所需的配置。

在配置文件中通过parent属性来指定继承的父Bean

示例

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

public class HelloWorld {
private String message1;
private String message2;

public void setMessage1(String message) {
this.message1 = message;
}

public void setMessage2(String message) {
this.message2 = message;
}

public void getMessage1() {
System.out.println("World Message1 : " + message1);
}

public void getMessage2() {
System.out.println("World Message2 : " + message2);
}
}
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
package net.biancheng;

public class HelloChina {
private String message1;
private String message2;
private String message3;

public void setMessage1(String message) {
this.message1 = message;
}

public void setMessage2(String message) {
this.message2 = message;
}

public void setMessage3(String message) {
this.message3 = message;
}

public void getMessage1() {
System.out.println("China Message1 : " + message1);
}

public void getMessage2() {
System.out.println("China Message2 : " + message2);
}

public void getMessage3() {
System.out.println("China Message3 : " + message3);
}
}

在配置文件中,分别为HelloWorld中的message1message2赋值。使用parent属性将HelloChain定义为HelloWorld的子类,并为HelloChain中的message1message3赋值。Beans.xml文件代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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-3.0.xsd">

<bean id="helloWorld" class="net.biancheng.HelloWorld">
<property name="message1" value="Hello World!" />
<property name="message2" value="Hello World2!" />
</bean>

<bean id="helloChina" class="net.biancheng.HelloChina" parent="helloWorld">
<property name="message1" value="Hello China!" />
<property name="message3" value="Hello China3!" />
</bean>
</beans>
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
package net.biancheng;

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

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.getMessage1();
objA.getMessage2();

HelloChina objB = (HelloChina) context.getBean("helloChina");
objB.getMessage1();
objB.getMessage2();
objB.getMessage3();
}
}

//运行结果如下。
//World Message1 : Hello World!
//World Message2 : Hello World2!
//China Message1 : Hello China!
//China Message2 : Hello World2!
//China Message3 : Hello China3!

由结果可以看出,我们在创建helloChina时并没有给message2赋值,但是由于Bean的继承,将值传递给了message2

Bean定义模板

您可以创建一个Bean定义模板,该模板只能被继承,不能被实例化。创建Bean定义模板时,不用指定class属性,而是指定abstarct="true"将该Bean定义为抽象Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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-3.0.xsd">

<bean id="beanTeamplate" abstract="true">
<property name="message1" value="Hello World!" />
<property name="message2" value="Hello World2!" />
<property name="message3" value="Hello World3!" />
</bean>

<bean id="helloChina" class="net.biancheng.HelloChina" parent="beanTeamplate">
<property name="message1" value="Hello China!" />
<property name="message3" value="Hello China!" />
</bean>
</beans>

Spring实例化Bean

在 Spring 中,实例化Bean有三种方式,分别是构造器实例化、静态工厂方式实例化和实例工厂方式实例化。

构造器实例化

构造器实例化是指 Spring 容器通过Bean对应的类中默认的构造函数实例化Bean

在项目的src目录下创建一个名为com.mengma.instance.constructor的包,在该包下创建一个实体类Person1

1
2
package com.mengma.instance.constructor;
public class Person1 {}

com.mengma.instance.constructor包下创建 Spring 的配置文件applicationContext.xml

1
2
3
4
5
6
7
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="person1" class="com.mengma.instance.constructor.Person1" />
</beans>

在上述配置中,定义了一个idperson1Bean,其中class属性指定了其对应的类为Person1

com.mengma.instance.constructor包下创建一个名为InstanceTest1的测试类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mengma.instance.constructor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest1 {
@Test
public void test() {
// 定义Spring配置文件的路径
String xmlPath = "com/mengma/instance/constructor/ApplicationContext.xml";
// 初始化Spring容器,加载配置文件,并对bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 通过容器获取id为person1的实例
System.out.println(applicationContext.getBean("person1"));
}
}

上述文件中,首先在test()方法中定义了 Spring 配置文件的路径,然后 Spring 容器会加载配置文件。在加载的同时,Spring 容器会通过实现类Person1中默认的无参构造函数对Bean进行实例化。

静态工厂方式实例化

也可以使用静态工厂的方式实例化Bean。此种方式需要提供一个静态工厂方法创建Bean的实例。

1. 创建实体类

在项目的src目录下创建一个名为com.mengma.instance.static_factory 的包,并在该包下创建一个实体类Person2

2. 创建静态工厂类

com.mengma.instance.static_factory包下创建一个名为MyBeanFactory的类,并在该类中创建一个名为createBean()的静态方法,用于创建Bean的实例。

1
2
3
4
5
6
7
package com.mengma.instance.static_factory;
public class MyBeanFactory {
// 创建Bean实例的静态工厂方法
public static Person2 createBean() {
return new Person2();
}
}

3. 创建 Spring 配置文件

com.mengma.instance.static_factory包下创建 Spring 的配置文件applicationContext.xml

1
2
3
4
5
6
7
8
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="person2" class="com.mengma.instance.static_factory.MyBeanFactory"
factory-method="createBean" />
</beans>

上述代码中,定义了一个idperson2Bean,其中class属性指定了其对应的工厂实现类为MyBeanFactory,而factory-method属性用于告诉 Spring 容器调用工厂类中的createBean()方法获取Bean的实例。

4. 创建测试类

com.mengma.instance.static_factory包下创建一个名为InstanceTest2的测试类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mengma.instance.static_factory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest2 {
@Test
public void test() {
// 定义Spring配置文件的路径
String xmlPath = "com/mengma/instance/static_factory/applicationContext.xml";
// 初始化Spring容器,加载配置文件,并对bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 通过容器获取id为person2实例
System.out.println(applicationContext.getBean("person2"));
}
}

实例工厂方式实例化

还有一种实例化Bean的方式就是采用实例工厂。在这种方式中,工厂类不再使用静态方法创建Bean的实例,而是直接在成员方法中创建Bean的实例。

同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向其实例化的类,而是通过factory-bean属性配置一个实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。

1. 创建实体类

src目录下创建一个名为com.mengma.instance.factory的包,在该包下创建一个Person3类。

2. 创建实例工厂类

com.mengma.instance.factory包下创建一个名为MyBeanFactory的类。

1
2
3
4
5
6
7
8
9
10
package com.mengma.instance.factory;
public class MyBeanFactory {
public MyBeanFactory() {
System.out.println("person3工厂实例化中");
}
// 创建Bean的方法
public Person3 createBean() {
return new Person3();
}
}

上述代码中,使用默认无参的构造方法输出person3工厂实例化中语句,使用createBean成员方法创建Bean的实例。

3. 创建 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" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!-- 配置实例工厂 -->
<bean id="myBeanFactory" class="com.mengma.instance.factory.MyBeanFactory" />
<!-- factory-bean属性指定一个实例工厂,factory-method属性确定使用工厂中的哪个方法 -->
<bean id="person3" factory-bean="myBeanFactory" factory-method="createBean" />
</beans>

上述代码中,首先配置了一个实例工厂Bean,然后配置了需要实例化的Bean。在idperson3Bean中,使用factory-bean属性指定一个实例工厂,该属性值就是实例工厂的id属性值。使用factory-method属性确定使用工厂中的createBean()方法。

4. 创建测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mengma.instance.factory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest3 {
@Test
public void test() {
// 定义Spring配置文件的路径
String xmlPath = "com/mengma/instance/factory/applicationContext.xml";
// 初始化Spring容器,加载配置文件,并对bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 通过容器获取id为person3实例
System.out.println(applicationContext.getBean("person3"));
}
}
打赏
  • Copyrights © 2017-2023 WSQ
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信