Spring IoC的概念
Spring框架是Java世界最为成功的框架,它最为核心的理念是IoC(控制反转)和AOP(面向切面编程)。
Spring的概述
在Spring中,它会认为一切Java类都是资源,而资源都是Bean,容纳这些Bean的是Spring所提供的IoC容器,所以Spring是一种基于Bean的编程。
Spring提供了以下策略:
- 对于POJO的潜力开发,提供轻量级和低侵入的编程,可以通过配置(XML、注解等)来扩展POJO的功能,通过依赖注入的理念去扩展功能,建议通过接口编程,强调OOD的开发模式理念,降低系统耦合度,提高系统可读性和可扩展性。
- 提供切面编程,尤其是把企业的核心应用——数据库应用,通过切面消除了以前复杂的try…catch…finally…代码结构,使得开发人员能够把精力更加集中于业务开发而不是技术本身,也避免了try…catch…finally…语句的滥用。
- 为了整合各个框架和技术的应用,Spring提供了模板类,通过模板可以整合各个框架和技术,比如支持Hibernate开发的HibernateTemplate、支持MyBatis开发的SqlSessionTemplate、支持Redis开发的RedisTemplate等,这样就把各种企业用到的技术框架整合到Spring中,提供了统一的模板,从而使得各种技术用起来更简单。
Spring IoC概述
主动创建对象
如果需要橙汁,那么就等于需要橙子、开水、糖,这些都是原料,而搅拌机是工具。如果需要主动创建对象,那么要对此创建对应的对象——JuiceMaker和Blender。
public class Blender {
/**
* 搅拌
*
* @param water
* 水描述
* @param fruit
* 水果描述
* @param sugar
* 糖描述
* @return 果汁
*/
public String mix(String water, String fruit, String sugar) {
String juice = "这是一杯由液体:" + water + "\n水果:" + fruit + "\n糖量:" + sugar + "\n组成的果汁";
return juice;
}
}
public class JuiceMaker {
private Blender blender = null;// 搅拌机
private String water;// 水描述
private String fruit;// 水果
private String sugar;// 糖分描述
public Blender getBlender() {
return blender;
}
public void setBlender(Blender blender) {
this.blender = blender;
}
public String getWater() {
return water;
}
public void setWater(String water) {
this.water = water;
}
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 String makeJuice() {
blender = new Blender();
return blender.mix(water, fruit, sugar);
}
}
被动创建对象
假设已经提供了果汁制造器(JuiceMaker2),那么只需要对其进行描述就可以得到果汁了。假设饮品店还会给我们提供这样的一个描述(Source),清单如下
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class JuiceMaker2 implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
private String beverageShop = null;
private Source source = null;
public String getBeverageShop() {
return beverageShop;
}
public void setBeverageShop(String beverageShop) {
this.beverageShop = beverageShop;
}
public Source getSource() {
return source;
}
public void setSource(Source source) {
this.source = source;
}
public String makeJuice() {
String juice = "这是一杯由" + beverageShop + "饮品店,提供的" + source.getSize() + source.getSugar() + source.getFruit();
return juice;
}
public void init() {
System.out.println("【" + this.getClass().getSimpleName() + "】执行自定义初始化方法");
}
public void destroy() {
System.out.println("【" + this.getClass().getSimpleName() + "】执行自定义销毁方法");
}
@Override
public void setBeanName(String arg0) {
System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanNameAware接口的setBeanName方法");
}
@Override
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanFactoryAware接口的setBeanFactory方法");
}
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
System.out.println(
"【" + this.getClass().getSimpleName() + "】调用ApplicationContextAware接口的setApplicationContext方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【" + this.getClass().getSimpleName() + "】调用InitializingBean接口的afterPropertiesSet方法");
}
}
public class Source {
private String fruit;// 类型
private String sugar;// 糖分描述
private String 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 String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
显然并不需要去关注果汁是如何制造出来的,系统采用XML这个清单进行描述:
<bean id="Source" class="com.ssm.learn.pojo.Source">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖" />
<property name="size" value="大杯" />
</bean>
这里对果汁进行了描述,接着需要选择饮品店,假设选择的是贡茶,那么会有代码:
<bean id="juiceMaker2" class="com.ssm.learn.pojo.JuiceMaker2">
<property name="beverageShop" value="贡茶" />
<property name="source" ref="source" />
</bean>
这里将饮品店设置为贡茶,这样就指定了贡茶为我们提供服务,而订单则引用我们之前的定义,使用下面的代码就能得到一杯果汁了。
JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
String juice = juiceMaker2.makeJuice();
Spring IoC阐述
控制反转是一种通过描述(在Java中可以是XML或者注解)并通过第三方去产生或获取特定对象的方式。
Spring会提供IoC容器来管理对应的资源。
主动创建的模式,责任归于开发者,被动的模式下,责任归于IoC容器。基于这样的被动形式,就说对象被控制反转了。
Spring IoC容器
Spring IoC容器的设计
Spring IoC容器的设计主要是基于BeanFactory和ApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,换句话说BeanFactory是Spring IoC容器所定义的最底层接口,而ApplicationContext是其高级接口之一,并且对BeanFactory功能做了许多有用的扩展,所以在绝大部分的工作场景下,都会使用ApplicationContext作为Spring IoC容器。
BeanFactory提供了Spring IoC最底层的设计,其源码如下:
public interface BeanFactory{
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeanException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolveableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
其中:
- getBean的多个方法用于获取配置给SpringIoC容器的Bean。
- isSingleton用于判断是否单例,如果为真,其意思是该Bean在容器中作为一个唯一单例存在的。
- isPrototype如果判断为真,意思是当从容器中获取Bean,容器就生成了一个新的实例,默认情况下Spring会为Bean创建一个单例,也就是说默认情况下isSingleton返回true,而isPrototype返回false。
- 关于type的匹配,这是一个按Java类型匹配的方式
- getAliases方法是获取别名的方法。
这里认识一个ApplicationContext的子类——ClassPathXmlApplicationContext。先创建一个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-4.0.xsd">
<!--BeanPostProcessor定义 -->
<bean id="beanPostProcessor" class="com.ssm.chapter9.bean.BeanPostProcessorImpl" />
<!--DisposableBean定义 -->
<bean id="disposableBean" class="com.ssm.chapter9.bean.DisposableBeanImpl" />
<bean id="source" class="com.ssm.chapter9.pojo.Source">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖" />
<property name="size" value="大杯" />
</bean>
<bean id="juiceMaker2" class="com.ssm.chapter9.pojo.JuiceMaker2"
destroy-method="destroy" init-method="init">
<property name="beverageShop" value="贡茶" />
<property name="source" ref="source" />
</bean>
</beans>
这里定义了两个Bean,这样Spring IoC容器在初始化的时候就能找到它们,然后使用ClassPathXmlApplicationContext容器就可以将其初始化,代码如下:
ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring-cfg.xml");
JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
System.out.println(juiceMaker2.makeJuice());
这样就会使用Application的实现类ClassPathXmlApplicationContext去初始化Spring IoC容器,然后开发者就可以通过IoC容器来获得资源了。
Spring IoC容器的初始化和依赖注入
Bean的定义和初始化在Spring IoC容器中是两大步骤,它是先定义,然后初始化和依赖注入的。
Bean的定义分为3步:
- Resource定位,这步是Spring IoC容器根据开发者的配置,进行资源定位,在Spring的开发中,通过XML或者注解都是十分常见的方式,定位的内容是由开发者所提供的。
- BeanDefinition的载入,这个时候只是将Resource定位到的信息,保存到Bean定义中(BeanDefinition)中,此时并不会创建Bean的实例。
- BeanDefinition的注册,这个过程就是将BeanDefinition的信息发布到SpringIoC容器中,注意,此时仍旧没有对应的Bean的实例创建。
做完了这3步,Bean就在Spring IoC容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给Bean,那么它还不能完全使用。对于初始化和依赖注入,Spring Bean还有一个配置选项——lazy-init,其含义就是是否初始化Spring Bean。在没有任何配置的情况下,它的默认值为default,实际值为false,也就是Spring IoC默认会自动初始化Bean。如果将其设置为true,那么只有当我们使用Spring IoC容器的getBean方法获取它,它才会进行Bean的初始化,完成依赖注入。
Spring Bean的生命周期
Spring IoC容器的本质目的就是为了管理Bean。对于Bean而言,在容器中存在其生命周期,它的初始化和销毁也需要一个过程。
Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。
若容器注册了以上各种接口,程序那么将会按照以上的流程进行。下面将仔细讲解各接口作用。
各种接口方法分类
Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:
- Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法
- Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
- 容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
- 工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。
具体过程描述
- 如果Bean实现了接口BeanNameAware的setBeanName方法,那么它就会调用这个方法
- 如果Bean实现了接口BeanFactoryAware的setBeanFactory方法,那么它就会调用这个方法
- 如果Bean实现了接口ApplicationContextAware的setApplicationContext方法,且Spring IoC容器也必须是一个ApplicationContext接口的实现类,那么才会调用这个方法,否则是不调用的。
- 如果Bean实现了接口BeanPostProcessor的postProcessBeforeInitialization方法,那么它就会调用这个方法。
- 如果Bean实现了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就会调用这个方法。
- 如果Bean自定义了初始化方法,它就会调用已定义的初始化方法。
- 如果Bean实现了接口BeanPostProcessor的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean就完成了初始化,那么Bean就生存在Spring IoC容器中了,使用者就可以从中获取Bean的服务。当服务器正常关闭,或者遇到其他关闭Spring IoC容器的事件,它就会调用对应的方法完成Bean的销毁,其步骤如下:
-
- 如果Bean实现了接口DisposableBean的destory方法,那么就会调用它。
-
- 如果定义了自定义销毁方法,那么就会调用它。
注意:所有的Spring IoC容器最低的要求是实现BeanFactory接口而已,而非ApplicationContext接口。如果采用了非ApplicationContext子类创建SpringIoC容器,那么即使是实现了ApplicationContextAware的setApplicationContext方法,它也不会在生命周期之中被调用。
当一个Bean实现了上述的接口,我们只需要在Spring IoC容器中定义它就可以了,Spring IoC容器会自动识别。
代码演示过程
1、首先是一个简单的Spring Bean,调用Bean自身的方法和Bean级生命周期接口方法,为了方便演示,它实现了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这4个接口,同时有2个方法,对应配置文件中的init-method和destroy-method。如下:
package com.ssm.learn.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
public class Person implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
private String name;
private String address;
private int phone;
private BeanFactory beanFactory;
private String beanName;
public Person() {
System.out.println("【构造器】调用Person的构造器实例化");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("【注入属性】注入属性name");
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
System.out.println("【注入属性】注入属性address");
this.address = address;
}
public int getPhone() {
return phone;
}
public void setPhone(int phone) {
System.out.println("【注入属性】注入属性phone");
this.phone = phone;
}
@Override
public String toString() {
return "Person [address=" + address + ", name=" + name + ", phone="
+ phone + "]";
}
// 这是BeanFactoryAware接口方法
@Override
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out
.println("【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()");
this.beanFactory = arg0;
}
// 这是BeanNameAware接口方法
@Override
public void setBeanName(String arg0) {
System.out.println("【BeanNameAware接口】调用BeanNameAware.setBeanName()");
this.beanName = arg0;
}
// 这是InitializingBean接口方法
@Override
public void afterPropertiesSet() throws Exception {
System.out
.println("【InitializingBean接口】调用InitializingBean.afterPropertiesSet()");
}
// 这是DiposibleBean接口方法
@Override
public void destroy() throws Exception {
System.out.println("【DiposibleBean接口】调用DiposibleBean.destory()");
}
// 通过<bean>的init-method属性指定的初始化方法
public void myInit() {
System.out.println("【init-method】调用<bean>的init-method属性指定的初始化方法");
}
// 通过<bean>的destroy-method属性指定的初始化方法
public void myDestory() {
System.out.println("【destroy-method】调用<bean>的destroy-method属性指定的初始化方法");
}
}
2、接下来是演示BeanPostProcessor接口的方法,如下:
package com.ssm.learn.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BeanPostProcessorImpl implements BeanPostProcessor {
public BeanPostProcessorImpl() {
super();
System.out.println("这是BeanPostProcessor实现类构造器!!");
// TODO Auto-generated constructor stub
}
@Override
public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
System.out.println("BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!");
return arg0;
}
@Override
public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
System.out.println("BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!");
return arg0;
}
}
如上,BeanPostProcessor接口包括2个方法postProcessAfterInitialization和postProcessBeforeInitialization,这两个方法的第一个参数都是要处理的Bean对象,第二个参数都是Bean的name。返回值也都是要处理的Bean对象。这里要注意。
- InstantiationAwareBeanPostProcessor 接口本质是BeanPostProcessor的子接口,一般我们继承Spring为其提供的适配器类InstantiationAwareBeanPostProcessor Adapter来使用它,如下:
package com.ssm.learn.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import java.beans.PropertyDescriptor;
public class InstantiationAwareBeanPostProcessorImpl extends InstantiationAwareBeanPostProcessorAdapter {
public InstantiationAwareBeanPostProcessorImpl() {
super();
System.out.println("这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!");
}
// 接口方法、实例化Bean之前调用
@Override
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法");
return null;
}
// 接口方法、实例化Bean之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法");
return bean;
}
// 接口方法、设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法");
return pvs;
}
}
这个有3个方法,其中第二个方法postProcessAfterInitialization就是重写了BeanPostProcessor的方法。第三个方法postProcessPropertyValues用来操作属性,返回值也应该是PropertyValues对象。
- 演示工厂后处理器接口方法,如下:
package com.ssm.learn.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
public BeanFactoryPostProcessorImpl() {
super();
System.out.println("这是BeanFactoryPostProcessor实现类构造器!!");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
System.out.println("BeanFactoryPostProcessor调用postProcessBeanFactory方法");
BeanDefinition bd = arg0.getBeanDefinition("person");
bd.getPropertyValues().addPropertyValue("phone", "110");
}
}
- 配置文件如下beans.xml,使用ApplicationContext,处理器不用手动注册:
<?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="beanPostProcessor" class="com.ssm.learn.bean.BeanPostProcessorImpl">
</bean>
<bean id="instantiationAwareBeanPostProcessor" class="com.ssm.learn.bean.InstantiationAwareBeanPostProcessorImpl">
</bean>
<bean id="beanFactoryPostProcessor" class="com.ssm.learn.bean.BeanFactoryPostProcessorImpl">
</bean>
<bean id="person" class="com.ssm.learn.pojo.Person" init-method="myInit"
destroy-method="myDestory" scope="singleton">
<property name="name" value="张三"/>
<property name="address" value="广州"/>
<property name="phone" value="159000000"/>
</bean>
</beans>
- 测试一下:
package com.ssm.learn.main;
import com.ssm.learn.pojo.Person;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class main {
public static void main(String[] args) throws BeansException {
System.out.println("现在开始初始化容器");
ApplicationContext factory = new ClassPathXmlApplicationContext("spring-cfg.xml");
System.out.println("容器初始化成功");
//得到Preson,并使用
Person person = factory.getBean("person", Person.class);
System.out.println(person);
System.out.println("现在开始关闭容器!");
((ClassPathXmlApplicationContext)factory).registerShutdownHook();
}
}
关闭容器使用的是实际是AbstractApplicationContext的钩子方法。
结果如下:
现在开始初始化容器
3月 05, 2020 1:07:35 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d012ddd: startup date [Thu Mar 05 01:07:35 CST 2020]; root of context hierarchy
3月 05, 2020 1:07:35 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring-cfg.xml]
这是BeanFactoryPostProcessor实现类构造器!!
BeanFactoryPostProcessor调用postProcessBeanFactory方法
这是BeanPostProcessor实现类构造器!!
这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!
InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法
【构造器】调用Person的构造器实例化
InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法
【注入属性】注入属性name
【注入属性】注入属性address
【注入属性】注入属性phone
【BeanNameAware接口】调用BeanNameAware.setBeanName()
【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()
BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!
【InitializingBean接口】调用InitializingBean.afterPropertiesSet()
【init-method】调用<bean>的init-method属性指定的初始化方法
BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!
InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法
容器初始化成功
Person [address=广州, name=张三, phone=110]
现在开始关闭容器!
【DiposibleBean接口】调用DiposibleBean.destory()
【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
参考资料:
- https://www.cnblogs.com/zrtqsk/p/3735273.html
- https://zhuanlan.zhihu.com/p/52537298