您的位置:首页 > 编程语言 > Java开发

Spring学习笔记(二) IoC容器与Bean

2016-09-19 21:18 711 查看
本文为Spring的学习笔记,有参考极客学院的wiki内容。

(我的博客原文地址:https://chaycao.github.io/2016/09/19/StudyNotesOfSpring2/ 请多指教!)

IoC容器

Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁

Spring容器使用依赖注入(DI)管理组成一个应用程序的组件。这些对象被称为 Spring Beans

通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装

配置元数据的三种表示方式: XML,Java 注释、 Java 代码

下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类配置元数据来生成完全配置和可执行的系统应用程序



Spring提供了两种不同类型的容器

- Spring BeanFactory 容器

最简单的容器,给DI提供了基本支持,由org.springframework.beans.factory.BeanFactory接口来定义。

- Spring ApplicationContext 容器

该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。

ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常建议使用 ApplicationContext。 BeanFactory 仍然可以用于轻量级的应用程序,如移动设备或基于 applet 的应用程序,其中它的数据量和速度是显著。

BeanFactory 容器

在Spring中,有大量对BeanFactory接口的实现。其中,最常见的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。

以 笔记(一) 中的 HelloWorld实例 为例。

HelloWorld.javaBeans.xml 内容不变

下面是MainApp.java的内容:

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MainApp {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory
(new ClassPathResource("Beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
}
}


第一步利用框架提供的 XmlBeanFactory() API 去生成工厂 bean 以及利用 ClassPathResource() API 去加载在路径 CLASSPATH 下可用的 bean 配置文件。XmlBeanFactory() API 负责创建并初始化所有的对象,即在配置文件中提到的 bean。

第二步利用第一步生成的 bean 工厂对象的 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象,该对象最后可以用于实际的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。

Spring ApplicationContext容器

最常被使用的 ApplicationContext 接口实现:

- FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器* XML 文件的完整路径*

- ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。

- WebXmlApplicationContext:该容器会在一个* web 应用程序的范围内*加载在 XML 文件中已被定义的 bean。

在 笔记(一) 中的 HelloWorld实例 中,我们已经使用过了 ClassPathXmlApplicationContext 容器。 WebXmlApplicationContext将在后面的章节使用,这里写一个 FileSystemXmlApplicationContext 的例子:

HelloWorld.java 和 Beans.xml 的内容不变

下面是 MainApp.java 的内容:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext
("C:/Users/ZARA/workspace/HelloSpring/src/Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}


第一步生成工厂对象。加载完指定路径下 bean 配置文件后,利用框架提供的 FileSystemXmlApplicationContext API 去生成工厂bean。FileSystemXmlApplicationContext 负责生成和初始化所有的对象,比如,所有在 XML bean 配置文件中的 bean。

第二步利用第一步生成的上下文中的 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。

Bean 定义

bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的。

配置元数据具有以下信息:

- 如何创建一个 bean

- bean 的生命周期的详细信息

- bean 的依赖关系

上述所有的配置元数据转换成一组构成每个 bean 定义的下列属性

- class : 强制的,指定用来创建 bean 的 bean 类

- name : 指定唯一的 bean 标识符。在基于 XML 的配置元数据中,可用ID或name属性指定 bean 标识符

- scope : 指定由特定的 bean 定义创建的对象的作用域

- constructor-arg : 用来注入依赖关系,后面讨论

- properties : 用来注入依赖关系,后面讨论

- autowiring mode : 用来注入依赖关系,后面讨论

- lazy-initialization mode : 延迟初始化 bean

- initialization 方法 : 在 bean 的所有必须的属性被容器设置后,调用回调方法

- destruction 方法 : 当包含该 bean 的容器被销毁时,调用回调方法

下面给出一个基于 XML 配置文件的例子,给出不同的 bean 定义:

<?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"> 
<!-- A simple bean definition -->
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- A bean definition with lazy init set on -->
<bean id="..." class="..." lazy-init="true">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- A bean definition with initialization method -->
<bean id="..." class="..." init-method="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- A bean definition with destruction method -->
<bean id="..." class="..." destroy-method="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

</beans>


Bean 作用域

Spring 框架支持以下五个作用域 :

1. singleton : 将 bean 的定义限制在每一个 Spring IoC容器中的一个单一实例(默认)

2. prototype : 将单一 bean 的定义限制在任意数量的对象实例。

3. request : 将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。

4. session : 将 bean 的定义限制为 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。

5. global-session : 将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。

本章只讨论前两个,后三个当讨论到 web-aware Spring ApplicationContext 的时候讨论。

singleton 作用域

如果作用域为 singleton,那么 Spring IoC容器中只能创建一个由该 bean 定义的对象的实例

该单一实例将存储在这种单例 bean 的高速缓存中,以及针对该 bean 的所有后续的请求和引用都返回缓存对象。

默认作用域始终是 singleton ,也可以在 bean 的配置文件中配置:

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
<!-- collaborators and configuration for this bean go here -->
</bean>


下面看一个例子,MainApp.java的内容如下

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.setMessage("I'm object A");
objA.getMessage();
HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
objB.getMessage();
}
}


输出结果如下:

Your Message : I'm object A
Your Message : I'm object A


prototype 作用域

如果作用域设置为 prototype,那么每次特定的 bean 发出请求时 Spring IoC 容器就创建对象的新的 Bean 实例

一般,有状态的 bean 使用 prototype 作用域,无状态的 bean 使用 singleton 作用域。

可以在 XML 配置文件中设置作用域为 prototype

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="prototype">
<!-- collaborators and configuration for this bean go here -->
</bean>


下面给出一个例子,MainApp.java文件内容如下:

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.setMessage("I'm object A"); objA.getMessage(); HelloWorld objB = (HelloWorld) context.getBean("helloWorld"); objB.getMessage(); } }


输出:

Your Message : I'm object A
Your Message : null


Bean 生命周期

当一个bean被实例化时,可能需要执行一些初始化使它转换成可用状态。当bean不再需要,并且从容器中移除时,可能做一些清楚工作。

本章只讨论两个重要的生命周期回调方法,它们在bean的初始化和销毁是必须的。

初始化回调

第一种方式:实现相应的接口

org.springframework.beans.factory.InitializingBean 接口指定一个单一的方法:

void afterPropertiesSet() throws Exception;


通过实现上述接口,初始化工作就可以在 afterPropertiesSet() 方法中执行,如下所示:

public class ExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}


第二种方式:在基于 XML 的配置元数据中, init-method 属性指定带有void无参数方法的名称。例如:

<bean id="exampleBean"
class="examples.ExampleBean" init-method="init"/>


下面是类的定义:

public class ExampleBean {
public void init() {
// do some initialization work
}
}


销毁回调

第一种方式:实现相应的接口

org.springframework.beans.factory.DisposableBean 接口指定一个单一的方法:

void destroy() throws Exception;


通过实现上述接口,结束工作就可以在 destroy() 方法中执行,如下所示:

public class ExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}


第二种方式:在基于 XML 的配置元数据中, destory-method 属性指定带有void无参数方法的名称。例如:

<bean id="exampleBean"
class="examples.ExampleBean" destroy-method="destroy"/>


下面是类的定义:

public class ExampleBean {
public void destroy() {
// do some destruction work
}
}


如果在非 web 应用程序环境中使用 Spring 的 IoC 容器(例如丰富的客户端桌面环境),那么在 JVM 中要注册关闭hook,确保正常关闭,让所有资源被释放,可以在单个 bean 上调用 destory 方法。

建议:使用第二种方式,因为 XML 配置在命名方法上具有极大的灵活性。

默认的初始化和销毁方法

如果有太多具有相同名称的初始化或者销毁方法的 Bean,那么不需要在 bean 上声明初始化方法销毁方法。框架使用元素中 default-init-methoddefault-destory-method 属性提供了灵活地配置这种情况,如下所示:

<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" default-init-method="init"
default-destroy-method="destroy">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

</beans>


Bean 后置处理器

BeanPostProcessor 接口定义回调方法,你可以在 Spring 容器通过插入一个或多个 BeanPostProcessor 的实现来完成实例化,配置和初始化一个bean之后实现一些自定义逻辑回调方法

当有多个 BeanPostProcessor 实现类时,可通过设置 BeanPostProcessor 接口实现的 Ordered 接口提供的 order 属性来控制这些 BeanPostProcessor 实现类的执行顺序。

ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器,然后通过在容器中创建 bean,在适当的时候调用它。

下面是一个例子:

这里是 HelloWorld.java 的内容:

public class HelloWorld {
private String message;
public void setMessage(String message){
this.message  = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
public void init(){
System.out.println("Bean is going through init.");
}
public void destroy(){
System.out.println("Bean will destroy now.");
}
}


下面 InitHelloWorld 实现了 BeanPostProcessor 接口,并且实现接口定义的两个方法 postProcessBeforeInitialization() postProcessAfterInitialization(),第一个参数为bean的Object对象,第二个参数为bean的名称。当初始化任意一个bean前,回调第一个函数。初始化任意一个bean后,回调第二个函数。

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InitHelloWorld implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeforeInitialization : " + beanName);
return bean;  // you can return any other object as well
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("AfterInitialization : " + beanName);
return bean;  // you can return any other object as well
}
}


下面是 MainApp.java 的内容。注册一个在 AbstractApplicationContext 类中声明的关闭 hook 的 registerShutdownHook() 方法,确保正常关闭,并且调用相关的 destory 方法。

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


下面是 init 和 destory 方法需要的配置文件 Beans.xml 文件:

<?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="helloWorld" class="com.chaycao.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</bean>

<bean class="com.chaycao.InitHelloWorld" />

</beans>


输出:

BeforeInitialization : helloWorld
Bean is going through init.
AfterInitialization : helloWorld
Your Message : Hello World!
Bean will destroy now.


Bean 定义继承

子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值。

Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置。

例子:

下面是配置文件 Beans.xml ,“helloworld” bean 有两个属性 message1 和 message2 。然后使用 parent 属性把 “helloIndia” bean 定义为 “helloWorld” bean 的孩子,并继承它的属性,同时自己还添加一个属性 message3。

<?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="helloWorld" class="com.chaycao.HelloWorld">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
</bean>

<bean id="helloIndia" class="com.chaycao.HelloIndia" parent="helloWorld">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>

</beans>


下面是 HelloWorld.java 的内容:

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


下面是 HelloIndia.java 的内容:

public class HelloIndia {
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("India Message1 : " + message1);
}

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

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


下面是 MainApp.java 的内容:

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

HelloIndia objB = (HelloIndia) context.getBean("helloIndia");
objB.getMessage1();
objB.getMessage2();
objB.getMessage3();
}
}


输出:

World Message1 : Hello World!
World Message2 : Hello Second World!
India Message1 : Hello India!
India Message2 : Hello Second World!
India Message3 : Namaste India!


在创建 “helloIndia” bean 的同时并没有传递 message2,但由于 Bean 定义的继承,所以传递了 message2。

Bean 定义模板

在定义一个 Bean 定义模板时,不指定class的属性,将abstract的属性置为 true,如下所示:

<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="beanTeamplate" abstract="true">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
<property name="message3" value="Namaste India!"/>
</bean>

<bean id="helloIndia" class="com.chaycao.HelloIndia" parent="beanTeamplate">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>

</beans>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 框架
相关文章推荐