Spring之简介及其IoC的概念

目录

1 Spring概述

2 Spring IoC概述

2.1 主动创建对象

2.2 被动创建对象

2.3 Spring IoC阐述

3 Spring IoC容器

3.1 Spring IoC容器的设计

3.2 Spring IoC容器的初始化和依赖注入

3.3 Spring Bean的生命周期 


Spring框架可以说是Java世界最为成功的框架,在企业实际应用中,大部分的企业架构都基于Spring框架。它的成功来自于理念,而不是技术,它最为核心的理念是IoC(控制反转)和AOP(面向切面编程),其中IoC是Spring的基础,而AOP则是其重要的功能,最为典型的当属数据库事务的使用。应该说Spring框架已经融入到了Java EE开发的各个领域。


1 Spring概述

Spring从2004年第一个版本至今已经十多年了。Spring的出现是因为当时SUN公司EJB的失败,尤其是在EJB2的时代,EJB2需要许多配置文件,还需要配合很多抽象概念才能运用。虽然EJB3克服了配置方面的冗余,但是对于Java EE开发而言,更为致命的是对EJB容器的依赖,也就是EJB只能运行在EJB容器中,EJB容器的笨重,给一些企业应用带来了困难,企业更喜欢轻便、容易开发和测试的环境。而在EJB开发中,需要选择EJB容器(比如WildFly、WebSphere、GlassFish、WebLogic等),然后通过这些EJB容器发布Bean,应用则可以通过EJB容器获得对应的Bean。

这一方式存在两方面的问题。首先,它比较缓慢,从容器中得到Bean需要大量的远程调用、反射、代理、序列化和反序列化等复杂步骤,对开发者的理解是一大挑战。其次,对EJB容器依赖比较重,难以达到快速开发和测试的目的,对于测试人员而言需要部署和跟踪EJB容器,所以EJB2和EJB3都在短暂的繁荣后迅速走向了没落。

EJB的没落造就了Spring的兴起。在Spring中,它会认为一切Java类都是资源,而资源都是Bean,容纳这些Bean的是Spring所提供的IoC容器,所以Spring是一种基于Bean的编程。Spring在EJB2失败的缝隙中出现,它是由一位澳大利亚的工程师——Rod Johnson所提倡的,它深刻地改变了Java开发世界,迅速使得Spring取代了EJB成为了实际的开发标准。

Spring不依赖于Spring所提供的API,也就是无侵入性或者低侵入性,即使Java应用离开了Spring依旧可以运用,这使得Spring更加灵活,拥有即插即拔的功能。其次,Spring不是去取代当时存在的EJB、Hibernate、JDO等技术,而是将这些框架和技术整合到Spring中去,这就意味着Spring所提供的是一个支持它们开发的模板。

基于这些理念,Spring提供了以下策略:

  • 对于POJO的潜力开发,提供轻量级和低侵入的编程,可以通过配置(XML、注解等)来扩展POJO的功能,通过依赖注入的理念来扩展功能,建议通过接口编程,强调OOD的开发模式理念,降低系统耦合度,提高系统可读性和可扩展性。
  • 提供切面编程,尤其是把企业的核心应用——数据库应用,通过切面消除了以前复杂的try...catch...finally...代码结构,使得开发人员能够把精力更加集中于业务开发而不是技术本身,也避免了try...catch...finally...语句的滥用。
  • 为了整合各个框架和技术的应用,Spring提供了模板类,通过模板可以整合各个框架和技术,比如支持Hibernate开发的HibernateTemplate、支持MyBatis开发的SqlSessionTemplate、支持Redis开发的RedisTemplate等,这样就把各种企业用到的技术框架整合到Spring中,提供了统一的模板,从而使得各种技术用起来更简单。

2 Spring IoC概述

控制反转是一个比较抽象的概念,这里用一个例子来说明。在实际生活中,人们要用到一样东西时,基本想法是找到东西。比如想喝杯橙汁,在没有饮品店的日子里,最直观的做法是,要买果汁机、橙子,准备开水。请注意这是你自己“主动”创造的过程,也就是一杯橙汁需要主动创造。然而到了今时今日,由于饮品店的盛行,已经没有必要自己去榨橙汁了。想喝橙汁的话,找到饮品店的联系方式,通过电话、微信等渠道描述你的需求、地址、联系方式等,下订单等待,过会就会有人送上橙汁了。请注意你并没有“主动”创造橙汁,也就是橙汁是由饮品店创造的,而不是你,但是也完全达到了你的要求。

上面只是举了一个很简单的例子,但是这个例子却包含了控制反转的思想,现实中系统的开发者是一个团队,团队由许多开发者组成。现在假设你在一个电商网站负责开发工作,你熟悉商品交易流程,但是对财务却不怎么熟悉,而团队中有些成员对于财务处理十分熟悉,在交易的过程中,商品交易流程需要调度财务的相关接口,才能得以实现,那么你的期望应该是:

  • 熟悉财务流程的成员开发对应的接口。
  • 接口逻辑尽量简单,内部复杂的业务并不需要自己去了解,你只要通过简单的调用就能使用。
  • 通过简单的描述就能获取这个接口实例,且描述应该尽量简单。

其实这完全可以和橙汁的例子进行类比,橙汁就等同于财务接口,而熟悉财务的同事就等同于饮品店,而你描述的橙汁需求、联系方式和地址,就等同于获取财务接口实例的描述。到这里有一个事实需要注意,财务接口对象的创建不是自己的行为,而是财务开发同事的行为,但是也完全达到了你的要求。而在潜意识里你会觉得对象应该由你主动创建,但是事实上这并不是你真实的需要,因为也许你对某一领域并不精通,这个时候你可以把创建对象的主动权转交给别人,这就是控制反转的概念。对于财务开发者而言,他们也不了解交易系统的细则,他们同样希望由你开发交易接口。为了更好地阐述上面的抽象描述,我们用Java代码的形式模拟主动创建和被动创建的过程。

2.1 主动创建对象

如果需要橙汁,那么就等于需要橙子、开水、糖,这些是原料,而搅拌机是工具。如果需要主动创造果汁,那么要对此创建对应的对象——JuiceMaker和Blender,代码如下:

public class Blender {

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

主动创建橙汁,需要我们实现自己可能不太熟悉的工作——如何搅拌橙汁,比如这里的mix方法,显然这不是一个好的办法,而对象果汁(juice)是需要依赖于水(water)、水果(fruit)、糖(sugar)和搅拌机(blender)来实现的,这个关系也会要求我们自己来维护。

在现实中,一个复杂的系统面对着成百上千种情况,如果都需要这样维护,那么会十分复杂,更多的时候,我们并不希望了解这类过程,因为有些东西我们并不需要知道过程,只需要获得最后的结果。正如果汁的例子那样,现实生活中我们更加希望的是通过对饮品店的描述得到果汁,只想对结果进行描述,得到我们所需要的东西,这就是被动创建对象了。

2.2 被动创建对象

假设已经提供了果汁制造器(JuiceMaker2),那么只需要对其进行描述就可以得到果汁了。假设饮品店还会给我们提供这样的一个描述(Source),它们的描述如下:

public class JuiceMaker2 {

    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 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.hys.spring.example1.pojo.Source">
		<property name="fruit" value="橙汁"></property>
		<property name="sugar" value="少糖"></property>
		<property name="size" value="大杯"></property>
	</bean>

这是对果汁进行了描述,接着需要选择饮品店,假设选择的是贡茶,那么会有如下代码的描述:

        <bean id="juiceMaker2"
		class="com.hys.spring.example1.pojo.JuiceMaker2">
		<property name="beverageShop" value="贡茶"></property>
		<property name="source" ref="source"></property>
	</bean>

这里将饮品店设置为贡茶,这样就指定了贡茶为我们提供服务,而订单则引用我们之前的定义,这样使用下面的代码就能得到一杯果汁了:

        JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
        System.out.println(juiceMaker2.makeJuice());

这个过程,果汁是由贡茶所制造的,我们并不关心制造的过程,我们所需要关心的是对果汁如何描述,选择哪个店来制造,这才是现今人们的习惯。

2.3 Spring IoC阐述

有了上面的实例,下面我们阐述控制反转的概念:控制反转是一种通过描述(在Java中可以是XML或者注解)并通过第三方来产生或获取特定对象的方式。

正如被动创建的果汁,是通过上述XML的描述所得到的。果汁制造器依赖于饮品店和订单来制造果汁的,而饮品店是别人来创造的,我们只需要知道它能生产果汁就可以了,并不需要去理解如何创建果汁。

在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。Spring会提供IoC容器来管理对应的资源。

同理,当熟悉财务的同事完成对财务接口模块的开发,就可以将其服务发布到Spring IoC的容器里,这个时候你只需要过程描述得到对应的财务接口,就可以完成对应的财务操作了,而财务模块是如何工作的,它又需要依赖哪些对象,都是由熟悉财务模块的同事完成的,这些并不需要你去理解,你只需要知道它能完成对应的财务操作就可以了。同样的,对于熟悉交易的你,也把交易接口模块发布到Spring IoC容器中,这样财务开发人员就可以通过容器获取交易接口得到交易明细了,交易模块如何工作,又依赖于哪些对象,他也是不需要知道的,可见Spring IoC容器带来了许多使用的便利。

对于测试人员也一样,也许他早早把财务模块测试好了,需要测试交易模块,他并不希望非常细致地了解交易模块,他只需要从Spring IoC容器中获取就可以了。而他的测试代码也只需要从Spring IoC容器获取交易模块的内容,至于内部复杂的依赖并不是他所需要关注的内容,这样就有利于测试人员对模块的测试,降低测试人员测试的复杂度。

这就是一种控制反转的理念,虽然这个理念的一个坏处是理解上的困难,但是它最大的好处在于降低对象之间的耦合,在一个系统中有些类,具体如何实现并不需要去理解,只需要知道它有什么用就可以了。只是这里对象的产生依靠于IoC容器,而不是开发者主动的行为。主动创建的模式,责任归于开发者,而在被动的模式下,责任归于IoC容器。有了这样的被动形式,我们就说对象被控制反转了。基于降低开发难度,对模块解耦,同时也更加有利于测试的原则,Spring IoC理念在各种Java EE开发者中广泛应用。


3 Spring IoC容器

从上面的例子中,我们知道了Spring IoC容器的作用,它可以容纳我们所开发的各种Bean,并且我们可以从中获取各种发布在Spring IoC容器里的Bean,并且通过描述可以得到它。

3.1 Spring IoC容器的设计

Spring IoC容器的设计主要是基于BeanFactory和ApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,换句话说BeanFactory是Spring IoC容器所定义的最底层接口,而ApplicationContext是其高级接口之一,并且对BeanFactory功能做了许多有用的扩展,所以在绝大部分的工作场景下,都会使用ApplicationContext作为Spring IoC容器,其设计如下图所示,图中展示的是Spring相关的IoC容器接口的主要设计:

这是张重要的设计图,从中我们可以看到BeanFactory位于设计的最底层,它提供了Spring IoC最底层的设计,为此,让我们先看看它的源码,如下(注意:笔者使用的是spring-framework-5.0.8.RELEASE的版本。不同版本的Spring,源码会有所不同):

public abstract interface BeanFactory
{
  public static final String FACTORY_BEAN_PREFIX = "&";
  
  public abstract Object getBean(String paramString)
    throws BeansException;
  
  public abstract <T> T getBean(String paramString, @Nullable Class<T> paramClass)
    throws BeansException;
  
  public abstract Object getBean(String paramString, Object... paramVarArgs)
    throws BeansException;
  
  public abstract <T> T getBean(Class<T> paramClass)
    throws BeansException;
  
  public abstract <T> T getBean(Class<T> paramClass, Object... paramVarArgs)
    throws BeansException;
  
  public abstract boolean containsBean(String paramString);
  
  public abstract boolean isSingleton(String paramString)
    throws NoSuchBeanDefinitionException;
  
  public abstract boolean isPrototype(String paramString)
    throws NoSuchBeanDefinitionException;
  
  public abstract boolean isTypeMatch(String paramString, ResolvableType paramResolvableType)
    throws NoSuchBeanDefinitionException;
  
  public abstract boolean isTypeMatch(String paramString, @Nullable Class<?> paramClass)
    throws NoSuchBeanDefinitionException;
  
  @Nullable
  public abstract Class<?> getType(String paramString)
    throws NoSuchBeanDefinitionException;
  
  public abstract String[] getAliases(String paramString);
}

由于这个接口的重要性,在这里有必要进行一些基本阐述:

  • getBean的多个方法用于获取配置给Spring IoC容器的Bean。从参数类型看可以是字符串,也可以是Class类型,由于Class类型可以扩展接口也可以继承父类,所以在一定程度上会存在使用父类类型无法准确获得实例的异常,比如获取学生类,但是学生子类有男学生和女学生两类,这个时候通过学生类就无法从容器中得到实例,因为容器无法判断具体的实现类(@Autowired和@Qualifier注解一起搭配使用可以解决该问题,笔者会在后续的文章中介绍它们的使用,这里先卖个关子)。
  • isSingleton用于判断是否单例,如果判断为真,其意思是该Bean在容器中是作为一个唯一单例存在的。而isPrototype则相反,如果判断为真,意思是当你从容器中获取Bean,容器就为你生成了一个新的实例。在默认情况下,Spring会为Bean创建一个单例,也就是默认情况下isSingleton返回true,而isPrototype返回false。
  • 关于type的匹配,这是一个按Java类型匹配的方式。
  • getAliases方法是获取别名的方法。

这就是Spring IoC最底层的设计,所有关于Spring IoC的容器将会遵守它所定义的方法。

从上图可以看到,为了扩展更多的功能,ApplicationContext接口扩展了许许多多的接口,因此它的功能十分强大,而WebApplicationContext也扩展了它,在实际应用中常常会使用的是ApplicationContext接口,因为BeanFactory的方法和功能较少,而ApplicationContext的方法和功能较多。而具体的ApplicationContext的实现类会使用在某一个领域,比如Spring MVC中的GenericWebApplicationContext,就广泛使用于Java Web工程中。

通过之前的果汁例子,我们来认识一个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">
	<bean id="source" class="com.hys.spring.example1.pojo.Source">
		<property name="fruit" value="橙汁"></property>
		<property name="sugar" value="少糖"></property>
		<property name="size" value="大杯"></property>
	</bean>
	<bean id="juiceMaker2"
		class="com.hys.spring.example1.pojo.JuiceMaker2">
		<property name="beverageShop" value="贡茶"></property>
		<property name="source" ref="source"></property>
	</bean>
</beans>

这里定义了两个Bean,这样Spring IoC容器在初始化的时候就能找到它们,然后使用ClassPathXmlApplicationContext容器就可以将其初始化,代码如下:

        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/hys/spring/example1/config/spring-cfg.xml");
        JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
        System.out.println(juiceMaker2.makeJuice());

这样就会使用ApplicationContext的实现类ClassPathXmlApplicationContext来初始化Spring IoC容器,然后开发者就可以通过IoC容器来获得资源了。

3.2 Spring IoC容器的初始化和依赖注入

Bean的定义和初始化在Spring IoC容器中是两大步骤,它是先定义,然后初始化和依赖注入的。

Bean的定义分为3步:

  1. Resource定位,这步是Spring IoC容器根据开发者的配置,进行资源定位,在Spring的开发中,通过XML或者注解都是十分常见的方式,定位的内容是由开发者所提供的。
  2. BeanDefinition的载入,这个时候只是将Resource定位到的信息,保存到Bean定义(BeanDefinition)中,此时并不会创建Bean的实例。
  3. BeanDefinition的注册,这个过程就是将BeanDefinition的信息发布到Spring IoC容器中,注意,此时仍旧没有对应的Bean的实例创建。

做完了这3步,Bean就在Spring IoC容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给Bean,那么它还不能完全使用。对于初始化和依赖注入,Spring还有一个配置选项——lazy-init,其含义就是是否初始化Spring Bean。在没有任何配置的情况下,它的默认值为default,实际值为false,也就是Spring IoC默认会自动初始化Bean。如果将其设置为true,那么只有当我们使用Spring IoC容器的getBean方法获取它时,它才会进行Bean的初始化,完成依赖注入。

3.3 Spring Bean的生命周期 

Spring IoC容器的本质目的就是为了管理Bean。对于Bean而言,在容器中存在其生命周期,它的初始化和销毁也需要一个过程,在一些需要自定义的过程中,我们可以插入代码来改变它们的一些行为,以满足特定的需求,这就需要使用到Spring Bean生命周期的知识了。

生命周期主要是为了了解Spring IoC容器初始化和销毁Bean的过程,通过对它的学习就可以知道如何在初始化和销毁的时候加入自定义的方法,以满足特定的需求。下图展示了Spring IoC容器初始化和销毁Bean的过程:

从上图可以看到,Spring IoC容器对Bean的管理还是比较复杂的,Spring IoC容器在执行了初始化和依赖注入后,会执行一定的步骤来完成初始化,通过这些步骤我们就能自定义初始化,而在Spring IoC容器正常关闭的时候,它也会执行一定的步骤来关闭容器,释放资源。除需要了解整个生命周期的步骤外,还要知道这些生命周期的接口是针对什么而言的,首先介绍生命周期的步骤:

  • 如果Bean实现了接口BeanNameAware的setBeanName方法,那么它就会调用这个方法。
  • 如果Bean实现了接口BeanFactoryAware的setBeanFactory方法,那么它就会调用这个方法。
  • 如果Bean实现了接口ApplicationContextAware的setApplicationContext方法,且Spring IoC容器也必须是一个ApplicationContext接口的实现类,那么才会调用这个方法,否则是不调用的。
  • 如果Bean实现了接口BeanPostProcessor的postProcessBeforeInitialization方法,那么它就会调用这个方法。
  • 如果Bean实现了接口InitializingBean的afterPropertiesSet方法,那么它就会调用这个方法。
  • 如果Bean自定义了初始化方法,它就会调用已定义的初始化方法。
  • 如果Bean实现了接口BeanPostProcessor的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean就完成了初始化,那么Bean就生存在Spring IoC的容器中了,使用者就可以从中获取Bean的服务。

当服务器正常关闭,或者遇到其他关闭Spring IoC容器的事件,它就会调用对应的方法完成Bean的销毁,其步骤如下:

  • 如果Bean实现了接口DisposableBean的destroy方法,那么就会调用它。
  • 如果定义了自定义销毁方法,那么就会调用它。

注意上图中的注释文字,因为有些步骤是在一些条件下才会执行的,如果不注意这些,往往就发现明明实现了一些接口,但是该方法并没有被执行。上面的步骤结合之前的Spring相关的IoC容器接口图来看,就会发现所有的Spring IoC容器最低的要求是实现BeanFactory接口而已,而非ApplicationContext接口,如果采用了非ApplicationContext子类创建Spring IoC容器,那么即使是实现了ApplicationContextAware的setApplicationContext方法,它也不会在生命周期之中被调用。

此外,还要注意这些接口是针对什么而言的,上述生命周期的接口,大部分是针对单个Bean而言的;BeanPostProcessor接口则是针对所有Bean而言的。当一个Bean实现了上述的接口,我们只需要在Spring IoC容器中定义它就可以了,Spring IoC容器会自动识别,并且按上图的顺序执行。为了测试BeanPostProcessor接口,这里简单做个测试:

package com.hys.spring.example1.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class BeanPostProcessorImpl implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "开始实例化");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "实例化完成");
        return bean;
    }
}

这样一个BeanPostProcessor就被我们用代码实现了,它会处理Spring IoC容器所有的Bean。

为了更好地展示生命周期的内容,将之前JuiceMaker2类的代码进行修改,结果如下:

package com.hys.spring.example1.pojo;

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.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class JuiceMaker2 implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

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

    @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方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用DisposableBean接口的destroy方法");
    }
}

这个类实现了生命周期能够实现的所有方法,以便于观察其生命周期的过程,其中init方法是自定义的初始化方法,而myDestroy方法是自定义的销毁方法,为了进一步使用这两个自定义的方法,在描述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="beanPostProcessorImpl"
		class="com.hys.spring.example1.bean.BeanPostProcessorImpl" />
	<bean id="source" class="com.hys.spring.example1.pojo.Source">
		<property name="fruit" value="橙汁"></property>
		<property name="sugar" value="少糖"></property>
		<property name="size" value="大杯"></property>
	</bean>
	<bean id="juiceMaker2"
		class="com.hys.spring.example1.pojo.JuiceMaker2" init-method="init"
		destroy-method="myDestroy">
		<property name="beverageShop" value="贡茶"></property>
		<property name="source" ref="source"></property>
	</bean>
</beans>

这里定义的id为juiceMaker2的Bean,其属性init-method就是自定义初始化方法,而destroy-method为自定义销毁方法。有了这些定义后,使用如下测试代码来进行测试:

        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("com/hys/spring/example1/config/spring-cfg.xml");
        JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
        System.out.println(juiceMaker2.makeJuice());
        ctx.close();

运行结果如下:

八月 09, 2018 1:41:29 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d012ddd: startup date [Thu Aug 09 01:41:29 CST 2018]; root of context hierarchy
八月 09, 2018 1:41:29 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/hys/spring/example1/config/spring-cfg.xml]
【Source】对象source开始实例化
【Source】对象source实例化完成
【JuiceMaker2】调用BeanNameAware接口的setBeanName方法
【JuiceMaker2】调用BeanFactoryAware接口的setBeanFactory方法
【JuiceMaker2】调用ApplicationContextAware接口的setApplicationContext方法
【JuiceMaker2】对象juiceMaker2开始实例化
【JuiceMaker2】调用InitializingBean接口的afterPropertiesSet方法
【JuiceMaker2】执行自定义初始化方法
【JuiceMaker2】对象juiceMaker2实例化完成
这是一杯由贡茶饮品店,提供的大杯少糖橙汁
八月 09, 2018 1:41:29 上午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3d012ddd: startup date [Thu Aug 09 01:41:29 CST 2018]; root of context hierarchy
【JuiceMaker2】调用DisposableBean接口的destroy方法
【JuiceMaker2】执行自定义销毁方法

通过运行结果就可以发现所有的生命周期的方法都已经被执行了,从结果可以看到BeanPostProcessor针对全部Bean。这样我们就可以利用生命周期来完成一些需要自定义的初始化和销毁Bean的行为了。

参考资料:[1]杨开振 周吉文 梁华辉 谭茂华.Java EE 互联网轻量级框架整合开发:SSM框架(Spring MVC+Spring+MyBatis)和Redis实现[M].北京:电子工业出版社,2017.7

猜你喜欢

转载自blog.csdn.net/weixin_30342639/article/details/81489506
今日推荐