IoC 是控制反转的意思,简单来说,就是创建对象的时候不是你主动创建,而是由 Spring 框架负责控制对象的生命周期和对象间的关系。
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件,这些对象被称为 Spring Beans。
Spring IoC 容器的设计
Spring IoC 容器的设计主要是基于 BeanFactory 和 ApplicationContext 两个接口,其中 ApplicationContext 是 BeanFactory 的子接口。在绝大部分工作场景下,我们都是使用 ApplicationContext 作为 Spring IoC 的容器。
被称作 Bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,可以通过XML或者注解创建。
Spring IoC 和 Bean 的关系如下图。
springbean-ioc.jpg下面通过一个例子来加深理解。新建一个 Maven 项目,在 pom.xml 中添加 Spring 依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wyk</groupId>
<artifactId>springdemo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.version>4.3.18.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
在项目中添加一个实体类 Drinks 。
package com.wyk.springdemo.pojo;
public class Drinks {
private String fruit; //水果类型
private String sugar; // 糖分描述
private Integer size; // 型号
public String getFruit() {
return fruit;
}
public void setFruit(String fruit) {
this.fruit = fruit;
}
public String getSugar() {
return sugar;
}
public void setSugar(String sugar) {
this.sugar = sugar;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
@Override
public String toString() {
return "一杯型号为" + this.size + this.sugar + this.fruit;
}
}
在 src/main/resources 下面创建一个XML配置文件 spring-cfg.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-4.0.xsd">
<bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖的" />
<property name="size" value="2" />
</bean>
</beans>
创建一个主应用程序类,调用前面的实体类。运行程序,可以打印出实体类。
package com.wyk.springdemo;
import com.wyk.springdemo.pojo.Drinks;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 初始化ApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
// 获取bean
Drinks orange = (Drinks)ctx.getBean("orangeJuice");
System.out.println(orange);
}
}
多个 bean 之间可以相互引用。 新建实体类 JuiceMaker 。
package com.wyk.springdemo.pojo;
public class JuiceMaker {
private String beverageShop;
private Drinks source;
public String getBeverageShop() {
return beverageShop;
}
public void setBeverageShop(String beverageShop) {
this.beverageShop = beverageShop;
}
public Drinks getSource() {
return source;
}
public void setSource(Drinks source) {
this.source = source;
}
public String makeJuice() {
String juice = "这是一杯由" + beverageShop + "饮品店, 提供的型号为" + source.getSize()
+ source.getSugar() + source.getFruit();
return juice;
}
}
在 spring-cfg.xml 中,添加新实体类的 bean , 并引用原来的 bean 。
<bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker">
<property name="beverageShop" value="pig" />
<property name="source" ref="orangeJuice" />
</bean>
在主程序中获取 bean 。
public class MainApp {
public static void main(String[] args) {
// 初始化ApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
JuiceMaker maker = (JuiceMaker)ctx.getBean("juiceMaker");
System.out.println(maker.makeJuice());
}
}
运行程序,可以查看结果。
[图片上传失败...(image-75819c-1549887547771)]
Spring IoC 容器初始化
Spring IoC 容器初始化包括2个步骤,即 bean 的定义和依赖注入。bean 的定义分成3步:
- Resource定位。Spring IoC根据开发者的配置定位资源,包括通过XML或者注解等。
- BeanDefination 的载入。将Resource定位到的信息,保存到 Bean 定义中。
- BeanDefination 的注册。 将 BeanDefination 中的信息发布到 Spring IoC 容器中。
三步完成后, Bean就在 Spring IoC 容器中被定义了,但并没有被初始化。 Bean 有一个配置选项 lazy-init 。其含义是是否初始化 Spring Bean 。默认值为 false ,即默认会自动化初始 Bean。 如果将其设置为 true ,那么只有会在获取的时候才会完成依赖注入。
Spring Bean
Bean 中可以包含如下属性。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | 这个属性指定由特定的 bean 定义创建的对象的作用域。 |
constructor-arg | 用来注入依赖关系的。 |
properties | 用来注入依赖关系的 |
autowiring mode | 用来注入依赖关系的。 |
lazy-initialization mode | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
initialization 方法 | 在 bean 的所有必需的属性被容器设置之后,调用回调方法。 |
destruction 方法 | 当包含该 bean 的容器被销毁时,使用回调方法。 |
Spring Bean 生命周期
Spring Bean 的生命周期由 Spring IoC容器控制,包括从初始化到销毁整个过程。
springbean-life.png具体步骤比较复杂,首先介绍下初始化的步骤。
- 如果 Bean 实现了接口 BeanNameAware 的 setBeanName 方法,那么它就会调用这个方法。
- 如果 Bean 实现了接口 BeanFactoryAware 的 setBeanFactory 方法,那么他就会调用这个方法。
- 如果 Bean 实现了接口 ApplicationContextAware 的 setApplicatonContext 方法,且 Spring IoC 容器也必须是一个 ApplicationContext 接口的实现类,那么才会调用这个方法,否则不会调用。
- 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessBeforeInitialization 方法,那么它就会调用这个方法。
- 如果 Bean 实现了接口 BeanFactoryPostProcessor 的 afterPropertiesSet,那么它会调用这个方法。
- 如果 Bean 自定义了初始化方法,它就会调用已定义的初始化方法。
- 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessAfterInitialization 方法,完成了这些调用, Bean就完成了初始化。
当服务器正常关闭,或其它事件关闭 Spring IoC 容器,它就会销毁 Bean。
- 如果 Bean 实现了接口 DisposableBean 的 destory 方法,那么就会调用它。
- 如果定义了自定义的销毁方法,那么就会调用它。
需要注意的一点是,BeanPostProcessor 接口针对所有的 Bean, 而其他接口只针对单个的 Bean。
下面修改前面的项目,进行测试。
创建 BeanPostProcessor 的实现类。
package com.wyk.springdemo.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BeanPostProcessorImpl implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("[" + bean.getClass().getSimpleName() + "]对象" + beanName
+ "开始实例化");
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("[" + bean.getClass().getSimpleName() + "]对象" + beanName
+ "实例化完成");
return bean;
}
}
修改 JuiceMaker 实体类, 添加相关接口。
package com.wyk.springdemo.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class JuiceMaker implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,
InitializingBean, DisposableBean {
private String beverageShop;
private Drinks source;
public String getBeverageShop() {
return beverageShop;
}
public void setBeverageShop(String beverageShop) {
this.beverageShop = beverageShop;
}
public Drinks getSource() {
return source;
}
public void setSource(Drinks source) {
this.source = source;
}
public void init() {
System.out.println("[" + this.getClass().getSimpleName() + "]执行自定义初始化方法");
}
public void myDestroy() {
System.out.println("[" + this.getClass().getSimpleName() + "]执行自定义销毁方法");
}
public String makeJuice() {
String juice = "这是一杯由" + beverageShop + "饮品店, 提供的型号为" + source.getSize()
+ source.getSugar() + source.getFruit();
return juice;
}
public void setBeanName(String arg0) {
System.out.println("[" + this.getClass().getSimpleName()
+ "]调用BeanNameAware接口的setBeanName方法");
}
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out.println("[" + this.getClass().getSimpleName()
+ "]调用BeanFactoryAware接口的setBeanFactory方法");
}
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
System.out.println("[" + this.getClass().getSimpleName()
+ "]调用ApplicationContextAware接口的setApplicationContext方法");
}
public void afterPropertiesSet() throws Exception {
System.out.println("[" + this.getClass().getSimpleName()
+ "]调用InitializingBean接口的afterPropertiesSet方法");
}
public void destroy() {
System.out.println("调用DisposableBean的destroy方法");
}
}
修改 Bean 的配置文件 spring-cfg.xml, 添加 beanPostProcessor 和实体的初始化和销毁方法。
<?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-4.0.xsd">
<!-- BeanPostProcessor定义 -->
<bean id="beanPostProcessor" class="com.wyk.springdemo.pojo.BeanPostProcessorImpl" />
<bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖的" />
<property name="size" value="2" />
</bean>
<bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker"
init-method="init" destroy-method="myDestroy">
<property name="beverageShop" value="pig" />
<property name="source" ref="orangeJuice" />
</bean>
</beans>
修改主程序进行测试。
package com.wyk.springdemo;
import com.wyk.springdemo.pojo.JuiceMaker;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 改为ClassPathXmlApplicationContext,方便后面关闭
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
JuiceMaker maker = (JuiceMaker)ctx.getBean("juiceMaker");
System.out.println(maker.makeJuice());
//关闭
ctx.close();
}
}
运行程序,查看控制台的输出。
二月 06, 2019 11:01:51 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@156ce6a: startup date [Wed Feb 06 11:01:51 CST 2019]; root of context hierarchy
二月 06, 2019 11:01:51 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring-cfg.xml]
[Drinks]对象orangeJuice开始实例化
[Drinks]对象orangeJuice实例化完成
[JuiceMaker]调用BeanNameAware接口的setBeanName方法
[JuiceMaker]调用BeanFactoryAware接口的setBeanFactory方法
[JuiceMaker]调用ApplicationContextAware接口的setApplicationContext方法
[JuiceMaker]对象juiceMaker开始实例化
[JuiceMaker]调用InitializingBean接口的afterPropertiesSet方法
[JuiceMaker]执行自定义初始化方法
[JuiceMaker]对象juiceMaker实例化完成
这是一杯由pig饮品店, 提供的型号为2少糖的橙汁
二月 06, 2019 11:01:52 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@156ce6a: startup date [Wed Feb 06 11:01:51 CST 2019]; root of context hierarchy
调用DisposableBean的destroy方法
[JuiceMaker]执行自定义销毁方法
Process finished with exit code 0
网友评论