生活随笔
收集整理的這篇文章主要介紹了
手写简版spring --7--初始化方法和销毁方法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、目標
當我們的類創建的 Bean 對象,交給 Spring 容器管理以后,這個類對象就可以被賦予更多的使用能力。就像我們在上一章節已經給類對象添加了修改注冊Bean定義未實例化前的屬性信息修改和實例化過程中的前置和后置處理,這些額外能力的實現,都可以讓我們對現有工程中的類對象做相應的擴展處理。那么除此之外我們還希望可以在 Bean 初始化過程,執行一些操作。比如幫我們做一些數據的加載執行,鏈接注冊中心暴漏RPC接口以及在Web程序關閉時執行鏈接斷開,內存銷毀等操作。如果說沒有Spring我們也可以通過構造函數、靜態方法以及手動調用的方式實現,但這樣的處理方式終究不如把諸如此類的操作都交給 Spring 容器來管理更加合適。 因此你會看到到 spring.xml 中有如下操作:
需要滿足用戶可以在 xml 中配置初始化和銷毀的方法,也可以通過實現類的方式處理,比如我們在使用 Spring 時用到的 InitializingBean, DisposableBean 兩個接口。其實還可以有一種是注解的方式處理初始化操作,不過目前還沒有實現到注解的邏輯,后續再完善此類功能。
二、設計
可能面對像 Spring 這樣龐大的框架,對外暴露的接口定義使用或者xml配置,完成的一系列擴展性操作,都讓 Spring 框架看上去很神秘。其實對于這樣在 Bean 容器初始化過程中額外添加的處理操作,無非就是預先執行了一個定義好的接口方法或者是反射調用類中xml中配置的方法,最終你只要按照接口定義實現,就會有 Spring 容器在處理的過程中進行調用而已。整體設計結構如下圖:
在 spring.xml 配置中添加 init-method 、 destroy-method 兩個注解,在配置文件加載的過程中,把注解配置一并定義到 BeanDefinition 的屬性當中。這樣在 initializeBean初始化操作的工程中,就可以通過反射的方式來調用配置在 Bean 定義屬性當中的方法信息了。另外如果是接口實現的方式,那么直接可以通過Bean對象調用對應接口定義的方法即可,((InitializingBean) bean).afterPropertiesSet()afterPropertiesSet(),兩種方式達到的效果是一樣的。 除了在初始化做的操作外, destroy-method 和 DisposableBean 接口的定義,都會在 Bean 對象初始化完成階段,執行注冊銷毀方法的信息到 DefaultSingletonBeanRegistry 類中的 disposableBeans 屬性里,這是為了后續統 一進行操作。這里還有一段適配器的使用,因為反射調用和接口直接調用,是兩種 方式。所以需要使用適配器進行包裝,下文代碼講解中參考 DisposableBeanAdapter 的具體實現 關于銷毀方法需要在虛擬機執行關閉之前進行操作,所以這里需要用到一個注冊鉤子的操作,如:Runtime.getRuntime().addShutdownHook(new Thread(()-->System.out.println("close"))); 這段代碼你可以執行測試 ,另外你可以使用手動調用 ApplicationContext.close 方法關閉容器。
三、實現
工程結構
small
- spring
- step
- 07
└── src├── main│ └── java│ └── cn
. bugstack
. springframework│ ├── beans│ │ ├── factory│ │ │ ├── config│ │ │ │ ├──
AutowireCapableBeanFactory . java│ │ │ │ ├──
BeanDefinition . java│ │ │ │ ├──
BeanFactoryPostProcessor . java│ │ │ │ ├──
BeanPostProcessor . java│ │ │ │ ├──
BeanReference . java│ │ │ │ ├──
ConfigurableBeanFactory . java│ │ │ │ └──
SingletonBeanRegistry . java│ │ │ ├── support│ │ │ │ ├──
AbstractAutowireCapableBeanFactory . java│ │ │ │ ├──
AbstractBeanDefinitionReader . java│ │ │ │ ├──
AbstractBeanFactory . java│ │ │ │ ├──
BeanDefinitionReader . java│ │ │ │ ├──
BeanDefinitionRegistry . java│ │ │ │ ├──
CglibSubclassingInstantiationStrategy . java│ │ │ │ ├──
DefaultListableBeanFactory . java│ │ │ │ ├──
DefaultSingletonBeanRegistry . java│ │ │ │ ├──
DisposableBeanAdapter . java│ │ │ │ ├──
InstantiationStrategy . java│ │ │ │ └──
SimpleInstantiationStrategy . java │ │ │ ├── support│ │ │ │ └──
XmlBeanDefinitionReader . java│ │ │ ├──
BeanFactory . java│ │ │ ├──
ConfigurableListableBeanFactory . java│ │ │ ├──
DisposableBean . java│ │ │ ├──
HierarchicalBeanFactory . java│ │ │ ├──
InitializingBean . java│ │ │ └──
ListableBeanFactory . java│ │ ├──
BeansException . java│ │ ├──
PropertyValue . java│ │ └──
PropertyValues . java │ ├── context│ │ ├── support│ │ │ ├──
AbstractApplicationContext . java │ │ │ ├──
AbstractRefreshableApplicationContext . java │ │ │ ├──
AbstractXmlApplicationContext . java │ │ │ └──
ClassPathXmlApplicationContext . java │ │ ├──
ApplicationContext . java │ │ └──
ConfigurableApplicationContext . java│ ├── core
. io│ │ ├──
ClassPathResource . java │ │ ├──
DefaultResourceLoader . java │ │ ├──
FileSystemResource . java │ │ ├──
Resource . java │ │ ├──
ResourceLoader . java │ │ └──
UrlResource . java│ └── utils│ └──
ClassUtils . java└── test└── java└── cn
. bugstack
. springframework
. test├── bean│ ├──
UserDao . java│ └──
UserService . java└──
ApiTest . java
Spring 應用上下文和對Bean對象擴展機制的類關系
以上整個類圖結構描述出來的就是本次新增 Bean 實例化過程中的初始化方法和銷毀方法。 因為我們一共實現了兩種方式的初始化和銷毀方法, xml 配置和定義接口,所以這里既有 InitializingBean 、 DisposableBean 也有需要 XmlBeanDefinitionReader 加載 spring.xml 配置信息到 BeanDefinition 中。 另外接口 ConfigurableBeanFactory 定義了 destroySingletons 銷毀方法,并由AbstractBeanFactory 繼承的父類 DefaultSingletonBeanRegistry 實現ConfigurableBeanFactory 接口定義的 destroySingletons 方法。 這種方式的設計可能是程序員是沒有用過的,都是用的誰實現接口誰完成實現類,而不是把實現接口的操作又交給繼承的父類處理。所以這塊還是蠻有意思的,是一種不錯的隔離分層的操作又交給繼承的父類處理。 最后就是關于向虛擬機注冊鉤子,保證在虛擬機關閉之前,執行銷毀操作。最后就是關于向虛擬機注冊鉤子,保證在虛擬機關閉之前,執行銷毀操作。Runtime.getRuntime().addShutdownHook(new Thread(() Runtime.getRuntime().addShutdownHook(new Thread(() --> > System.out.println("closeSystem.out.println("close!!")));")));
定義初始化和銷毀方法的接口
public interface InitializingBean { void afterPropertiesSet ( ) throws Exception ;
}
public interface DisposableBean { void destroy ( ) throws Exception ;
}
InitializingBean 、 DisposableBean ,兩個接口方法還是比較常用的,在一些需要結合 Spring 實現的組件中,經常會使用這兩個方法來做一些參數的初始化和銷毀操作。比如接口暴露、數據庫數據讀取、配置文件加載等等。
Bean 屬性定義新增初始化和銷毀
public class BeanDefinition { private Class beanClass
; private PropertyValues propertyValues
; private String initMethodName
; private String destroyMethodName
; public BeanDefinition ( Class beanClass
) { this . beanClass
= beanClass
; this . propertyValues
= new PropertyValues ( ) ; } public BeanDefinition ( Class beanClass
, PropertyValues propertyValues
) { this . beanClass
= beanClass
; this . propertyValues
= propertyValues
!= null ? propertyValues
: new PropertyValues ( ) ; } public Class getBeanClass ( ) { return beanClass
; } public void setBeanClass ( Class beanClass
) { this . beanClass
= beanClass
; } public PropertyValues getPropertyValues ( ) { return propertyValues
; } public void setPropertyValues ( PropertyValues propertyValues
) { this . propertyValues
= propertyValues
; } public String getInitMethodName ( ) { return initMethodName
; } public void setInitMethodName ( String initMethodName
) { this . initMethodName
= initMethodName
; } public String getDestroyMethodName ( ) { return destroyMethodName
; } public void setDestroyMethodName ( String destroyMethodName
) { this . destroyMethodName
= destroyMethodName
; }
}
在 BeanDefinition 新增加了兩個屬性: initMethodName 、 destroyMethodName這兩個屬性是為了在 spring.xml 配置的 Bean 對象中,可以配置 init-method="initDataMethod" destroy-method="destroyDataMethod" 操作,最終實現接口的效果是一樣的。 只不過一個是接口方法的直接調用,另外是一個在配置文件中讀取到方法反射調用
執行 Bean 對象的初始化方法
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy
= new CglibSubclassingInstantiationStrategy ( ) ; @Override protected Object createBean ( String beanName
, BeanDefinition beanDefinition
, Object [ ] args
) throws BeansException { Object bean
= null ; try { bean
= createBeanInstance ( beanDefinition
, beanName
, args
) ; applyPropertyValues ( beanName
, bean
, beanDefinition
) ; bean
= initializeBean ( beanName
, bean
, beanDefinition
) ; } catch ( Exception e
) { throw new BeansException ( "Instantiation of bean failed" , e
) ; } addSingleton ( beanName
, bean
) ; return bean
; } private Object initializeBean ( String beanName
, Object bean
, BeanDefinition beanDefinition
) { Object wrappedBean
= applyBeanPostProcessorsBeforeInitialization ( bean
, beanName
) ; try { invokeInitMethods ( beanName
, wrappedBean
, beanDefinition
) ; } catch ( Exception e
) { throw new BeansException ( "Invocation of init method of bean[" + beanName
+ "] failed" , e
) ; } wrappedBean
= applyBeanPostProcessorsAfterInitialization ( bean
, beanName
) ; return wrappedBean
; } private void invokeInitMethods ( String beanName
, Object bean
, BeanDefinition beanDefinition
) throws Exception { if ( bean
instanceof InitializingBean ) { ( ( InitializingBean ) bean
) . afterPropertiesSet ( ) ; } String initMethodName
= beanDefinition
. getInitMethodName ( ) ; if ( StrUtil . isNotEmpty ( initMethodName
) ) { Method initMethod
= beanDefinition
. getBeanClass ( ) . getMethod ( initMethodName
) ; if ( null == initMethod
) { throw new BeansException ( "Could not find an init method named '" + initMethodName
+ "' on bean with name '" + beanName
+ "'" ) ; } initMethod
. invoke ( bean
) ; } }
}
抽象類 AbstractAutowireCapableBeanFactory 中的 createBean 是用來創建 Bean 對象的方法,在這個方法中我們之前已經擴展了 BeanFactoryPostProcessor、BeanPostProcessor 操作,這里我們繼續完善執行 Bean 對象的初始化方法的處理動作。 在方法 invokeInitMethods 中,主要分為兩塊來執行實現了 InitializingBean 接口的操作,處理 afterPropertiesSet 方法。另外一個是判斷配置信息 init-method 是否存在,執行反射調用 initMethod.invoke(bean)。這兩種方式都可以在 Bean 對象初始化過程中進行處理加載 Bean 對象中的初始化操作,讓使用者可以額外新增加自己想要的動作。
定義銷毀方法適配器(接口和配置)
public class DisposableBeanAdapter implements DisposableBean { private final Object bean
; private final String beanName
; private String destroyMethodName
; public DisposableBeanAdapter ( Object bean
, String beanName
, BeanDefinition beanDefinition
) { this . bean
= bean
; this . beanName
= beanName
; this . destroyMethodName
= beanDefinition
. getDestroyMethodName ( ) ; } @Override public void destroy ( ) throws Exception { if ( bean
instanceof DisposableBean ) { ( ( DisposableBean ) bean
) . destroy ( ) ; } if ( StrUtil . isNotEmpty ( destroyMethodName
) && ! ( bean
instanceof DisposableBean && "destroy" . equals ( this . destroyMethodName
) ) ) { Method destroyMethod
= bean
. getClass ( ) . getMethod ( destroyMethodName
) ; if ( null == destroyMethod
) { throw new BeansException ( "Couldn't find a destroy method named '" + destroyMethodName
+ "' on bean with name '" + beanName
+ "'" ) ; } destroyMethod
. invoke ( bean
) ; } }
}
可能你會想這里怎么有一個適配器的類呢,因為銷毀方法有兩種甚至多種方式,目前有實現接口 DisposableBean、配置信息 destroy-method,兩種方式。而這兩種方式的銷毀動作是由 AbstractApplicationContext 在注冊虛擬機鉤子后看,虛擬機關閉前執行的操作動作。 那么在銷毀執行時不太希望還得關注都銷毀那些類型的方法,它的使用上更希望是有一個統一的接口進行銷毀,所以這里就新增了適配類,做統一處理。
創建Bean時注冊銷毀方法對象
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy
= new CglibSubclassingInstantiationStrategy ( ) ; @Override protected Object createBean ( String beanName
, BeanDefinition beanDefinition
, Object [ ] args
) throws BeansException { Object bean
= null ; try { bean
= createBeanInstance ( beanDefinition
, beanName
, args
) ; applyPropertyValues ( beanName
, bean
, beanDefinition
) ; bean
= initializeBean ( beanName
, bean
, beanDefinition
) ; } catch ( Exception e
) { throw new BeansException ( "Instantiation of bean failed" , e
) ; } registerDisposableBeanIfNecessary ( beanName
, bean
, beanDefinition
) ; addSingleton ( beanName
, bean
) ; return bean
; } protected void registerDisposableBeanIfNecessary ( String beanName
, Object bean
, BeanDefinition beanDefinition
) { if ( bean
instanceof DisposableBean || StrUtil . isNotEmpty ( beanDefinition
. getDestroyMethodName ( ) ) ) { registerDisposableBean ( beanName
, new DisposableBeanAdapter ( bean
, beanName
, beanDefinition
) ) ; } }
}
在創建 Bean 對象的實例的時候,需要把銷毀方法保存起來,方便后續執行銷毀動作進行調用。 那么這個銷毀方法的具體方法信息,會被注冊到 DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans 屬性中去,因為這個接口的方法最終可以被類 AbstractApplicationContext 的 close 方法通過 getBeanFactory().destroySingletons() 調用。 在注冊銷毀方法的時候,會根據是接口類型和配置類型統一交給 DisposableBeanAdapter 銷毀適配器類來做統一處理。實現了某個接口的類可以被 instanceof 判斷或者強轉后調用接口方法
虛擬機關閉鉤子注冊調用銷毀方法
public interface ConfigurableApplicationContext extends ApplicationContext { void refresh ( ) throws BeansException ; void registerShutdownHook ( ) ; void close ( ) ;
}
首先我們需要在 ConfigurableApplicationContext 接口中定義注冊虛擬機鉤子的方法 registerShutdownHook 和手動執行關閉的方法 close。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { @Override public void registerShutdownHook ( ) { Runtime . getRuntime ( ) . addShutdownHook ( new Thread ( this :: close ) ) ; } @Override public void close ( ) { getBeanFactory ( ) . destroySingletons ( ) ; }
}
這里主要體現了關于注冊鉤子和關閉的方法實現,上文提到過的 Runtime.getRuntime().addShutdownHook,可以嘗試驗證。在一些中間件和監控系統的設計中也可以用得到,比如監測服務器宕機,執行備機啟動操作。
四、測試
事先準備
public class UserDao { private static Map < String , String > hashMap
= new HashMap < > ( ) ; public void initDataMethod ( ) { System . out
. println ( "執行:init-method" ) ; hashMap
. put ( "10001" , "小傅哥" ) ; hashMap
. put ( "10002" , "八杯水" ) ; hashMap
. put ( "10003" , "阿毛" ) ; } public void destroyDataMethod ( ) { System . out
. println ( "執行:destroy-method" ) ; hashMap
. clear ( ) ; } public String queryUserName ( String uId
) { return hashMap
. get ( uId
) ; } }
public class UserService implements InitializingBean , DisposableBean { private String uId
; private String company
; private String location
; private UserDao userDao
; @Override public void destroy ( ) throws Exception { System . out
. println ( "執行:UserService.destroy" ) ; } @Override public void afterPropertiesSet ( ) throws Exception { System . out
. println ( "執行:UserService.afterPropertiesSet" ) ; }
}
UserDao,修改了之前使用 static 靜態塊初始化數據的方式,改為提供 initDataMethod 和 destroyDataMethod 兩個更優雅的操作方式進行處理。 UserService,以實現接口 InitializingBean, DisposableBean 的兩個方法destroy()、afterPropertiesSet(),處理相應的初始化和銷毀方法的動作。afterPropertiesSet,方法名字很好,在屬性設置后執行
配置文件 基礎配置,無BeanFactoryPostProcessor、BeanPostProcessor,實現類
<?xml version="1.0" encoding="UTF-8"?>
< beans> < bean id = " userDao" class = " cn.bugstack.springframework.test.bean.UserDao" init-method = " initDataMethod" destroy-method = " destroyDataMethod" /> < bean id = " userService" class = " cn.bugstack.springframework.test.bean.UserService" > < property name = " uId" value = " 10001" /> < property name = " company" value = " 騰訊" /> < property name = " location" value = " 深圳" /> < property name = " userDao" ref = " userDao" /> </ bean> </ beans>
配置文件中主要是新增了,init-method=“initDataMethod” destroy-method=“destroyDataMethod”,這樣兩個配置。從源碼的學習中可以知道,這兩個配置是為了加入到BeanDefinition 定義類之后寫入到類 DefaultListableBeanFactory 中的beanDefinitionMap 屬性中去。
單元測試
@Test
public void test_xml ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. registerShutdownHook ( ) ; UserService userService
= applicationContext
. getBean ( "userService" , UserService . class ) ; String result
= userService
. queryUserInfo ( ) ; System . out
. println ( "測試結果:" + result
) ;
}
測試方法中新增加了一個,注冊鉤子的動作。applicationContext.registerShutdownHook();
六、總結
本文主要完成了關于初始和銷毀在使用接口定義 implements InitializingBean, DisposableBean和在spring.xml中配置 init-method="initDataMethod",destroy-method="destroyDataMethod" 的兩種具體在AbstractAutowireCapableBeanFactory 完成初始方法和 AbstractApplicationContext 處理銷毀動作的具體實現過程。 通過本文的實現內容,可以看到目前這個 Spring 框架對 Bean 的操作越來越完善了,可擴展性也不斷的增強。你既可以在Bean注冊完成實例化前進行 BeanFactoryPostProcessor 操作,也可以在Bean實例化過程中執行前置和后置操作,現在又可以執行Bean的初始化方法和銷毀方法。所以一個簡單的Bean對象,已經被賦予了各種擴展能力。 在學習和動手實踐 Spring 框架學習的過程中,特別要注意的是它對接口和抽象類的把握和使用,尤其遇到類似,A繼承B實現C時,C的接口方法由A繼承的父類B實現,這樣的操作都蠻有意思的。也是可以復用到通常的業務系統開發中進行處理一些復雜邏輯的功能分層,做到程序的可擴展、易維護等特性。
與50位技術專家面對面 20年技術見證,附贈技術全景圖
總結
以上是生活随笔 為你收集整理的手写简版spring --7--初始化方法和销毁方法 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。