【Java EE】Spring IoC的概念

Spring IoC的概念

Spring框架是Java世界最爲成功的框架,它最爲核心的理念是IoC(控制反轉)和AOP(面向切面編程)。

Spring的概述

在Spring中,它會認爲一切Java類都是資源,而資源都是Bean,容納這些Bean的是Spring所提供的IoC容器,所以Spring是一種基於Bean的編程。

Spring提供了以下策略:

  1. 對於POJO的潛力開發,提供輕量級和低侵入的編程,可以通過配置(XML、註解等)來擴展POJO的功能,通過依賴注入的理念去擴展功能,建議通過接口編程,強調OOD的開發模式理念,降低系統耦合度,提高系統可讀性和可擴展性。
  2. 提供切面編程,尤其是把企業的核心應用——數據庫應用,通過切面消除了以前複雜的try…catch…finally…代碼結構,使得開發人員能夠把精力更加集中於業務開發而不是技術本身,也避免了try…catch…finally…語句的濫用。
  3. 爲了整合各個框架和技術的應用,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步:

  1. Resource定位,這步是Spring IoC容器根據開發者的配置,進行資源定位,在Spring的開發中,通過XML或者註解都是十分常見的方式,定位的內容是由開發者所提供的。
  2. BeanDefinition的載入,這個時候只是將Resource定位到的信息,保存到Bean定義中(BeanDefinition)中,此時並不會創建Bean的實例。
  3. 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的完整生命週期經歷了各種方法調用,這些方法可以劃分爲以下幾類:

  1. Bean自身的方法:這個包括了Bean本身調用的方法和通過配置文件中的init-method和destroy-method指定的方法
  2. Bean級生命週期接口方法:這個包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這些接口的方法
  3. 容器級生命週期接口方法:這個包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個接口實現,一般稱它們的實現類爲“後處理器”。
  4. 工廠後處理器接口方法:這個包括了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的銷燬,其步驟如下:
    1. 如果Bean實現了接口DisposableBean的destory方法,那麼就會調用它。
    1. 如果定義了自定義銷燬方法,那麼就會調用它。

注意:所有的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對象。這裏要注意。

  1. 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對象。

  1. 演示工廠後處理器接口方法,如下:
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");
    }

}
  1. 配置文件如下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>
  1. 測試一下:
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的鉤子方法。
結果如下:

現在開始初始化容器
305, 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
305, 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屬性指定的初始化方法

參考資料:

  1. https://www.cnblogs.com/zrtqsk/p/3735273.html
  2. https://zhuanlan.zhihu.com/p/52537298
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章