Spring系列.Bean簡介

程式設計師自由之路發表於2020-07-01

Bean屬性配置

Spring在讀取配置檔案中bean的metadata後會構造一個個BeanDefination物件。後面Spring會根據這些BeanDefinition建立物件。在配置一個bean的時候我們可以配置下面這些元素:

元素 備註
class 設定類的全限定名
name 設定Bean的名稱,可以設定多個用逗號分隔
id/name Bean的唯一識別符號,全域性只能有一個
scope 設定Bean是單例還是原型,預設單例
constructor arguments 構造方法入參,進行依賴注入
properties 進行依賴注入
autowiring mode 自動注入模式
lazy-initialization mode 只對scope為單列的bean生效,設定為true會在getBean()時才建立bean例項
initialization method 設定Bean的初始化方法
destruction method 設定Bean的銷燬方法

除了通過配置資訊來建立Bean,Spring還允許通過介面的方式建立使用者在容器外部建立的物件。(通過DefaultListableBeanFactory類的registerSingleton方法和registerBeanDefinition方法)

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContextimplementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

雖然Spring容器還提供了registerSingleton(..)方法和registerBeanDefinition(..)來註冊單例Bean,但是不建議自己使用這個方法,因為我們可能在依賴注入之後再註冊了這個Bean。推薦使用配置BeanDefinition的方式來配置Bean。(其實這兩個方法更多是Spring框架自己使用,在配置檔案以外的一些地方再註冊一些Bean到容器中)

關於上面表格中的id屬性和name屬性這邊多說一句。

id和name屬性都可以用來當做一個bean的識別符號,他們兩個的區別是id只能給這個bean指定一個識別符號,而name屬性可以同時給這個bean指定多個識別符號(多個識別符號之間用,隔開)。下面給出一個配置的列子

<!--經過下面的配置,bean1,bean2、name1、name2、alias1、alias2、alias3其實是一個Bean-->
<bean id="bean1,bean2" name="name1,name2" class="com.csx.demo.springdemo.service.MyBean1">
</bean>
<alias name="bean1,bean2" alias="alias1"/>
<alias name="name1" alias="alias2"/>
<alias name="name2" alias="alias3"/>

Bean命名

如果沒有給Bean指定一個識別符號,Spring容器會預設給這個Bean設定一個識別符號。使用者可以通過id和name兩個屬性來設定Bean的識別符號,這些識別符號需要在整個容器範圍內唯一,同時name可以指定多個用逗號分隔。

例項化Bean

例項化Bean一般有四種方式:預設建構函式、待引數的建構函式、靜態工廠類、例項方法建立Bean。需要我們注意的是Bean的例項化和依賴注入的區別。

  1. 預設建構函式方式
    <bean id="exampleBean" class="examples.ExampleBean"/>
  1. 帶引數的建構函式
    <bean id="Hello2" class="com.csx.personal.web.services.HelloImpl2">
        <constructor-arg index="0" value="chensongxia"/>
        <constructor-arg index="1" value="zhaoru"/>
        <constructor-arg index="2" ref="beanid"/>
    </bean>
  1. 使用靜態工廠類(不推薦使用)
    <bean id="bean3" class="cn.javass.spring.chapter2.HelloApiStaticFactory" factory-method="newInstance">
         <constructor-arg index="0" value="Hello Spring!"/>
    </bean>
    <!-- 使用examples.ClientService這個類的createInstance方法建立bean -->
    <bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>

    public class ClientService {
        private static ClientService clientService = new ClientService();
        private ClientService() {}
        public static ClientService createInstance() {
        return clientService;
        }
    }
  1. 使用例項方法進行Bean建立
    <bean id="serviceLocator" class="examples.DefaultServiceLocator">
        <!-- inject any dependencies required by this locator bean -->
     </bean>
     <!-- 使用serviceLocator這個Bean的createClientServiceInstance方法建立Bean -->
     <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
     <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
     //類定義	
     public class DefaultServiceLocator {
        private static ClientService clientService = new ClientServiceImpl();
        private static AccountService accountService = new AccountServiceImpl();
        public ClientService createClientServiceInstance() {
            return clientService;
        }
        public AccountService createAccountServiceInstance() {
            return accountService;
        }
      }

如果我們要通過xml的形式配置一個靜態內部類,可以參考下面的例子

For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, the value of the class attribute on a bean definition would be com.example.SomeThing$OtherThing.

Bean的Scope

Spring預設支援的Scope

Scopes 描述
singleton 整個IOC容器中只有一個Bean
prototype 每次請求都會生成新的Bean
request 每個HTTP request都會生成一個新的Bean,只對web系列的ApplicationContext生效
session 每個session範圍內生成一個新的Bean,只對web系列的ApplicationContext生效
Application ServletContext範圍內生成一個新的Bean,只對web系列的ApplicationContext生效
websocket --

singleton型別的bean定義,在一個容器中只存在一個例項,所有對該型別bean的依賴都引用這一單一例項。

scope為prototype的bean,容器在接受到該型別的物件的請求的時候(呼叫getBean方法),會每次都重新 生成一個新的物件給請求方,雖然這種型別的物件的例項化以及屬性設定等工作都是由容器負責的,但是隻要準備完畢,並且物件例項返回給請求方之後,容器就不 在擁有當前物件的引用,請求方需要自己負責當前物件後繼生命週期的管理工作,包括該物件的銷燬。也就是說,容器每次返回請求方該物件的一個新的例項之後, 就由這個物件“自生自滅”了。

單例Bean依賴原型Bean

當一個單例的Bean依賴一個原型Bean時,由於單例只初始化一次,所以拿到的原型Bean也只是我們第一次初始化時拿到的Bean,並不能達到我們想要的效果。此時我們可以使用Spring提供的look-up方式的注入來解決這個問題。

Request, Session, Application, and WebSocket Scopes

這三種Scope只對Web系列的ApplicationContext的生效。可以視同@RequestScope、@SessionScope和@ApplicationScope使之生效。

自定義Scope

Spring還支援自定義scope,需要時可以學些下這個特性。

Bean的擴充套件點(注意和容器擴充套件點的區別)

Spring提供了一系列介面讓使用者來自定義Bean的屬性(注意和容器擴充套件點那一個章節的區別),主要的介面有:

  • Lifecycle Callbacks;
  • ApplicationContextAware and BeanNameAware;
  • Other Aware Interfaces;

生命週期回撥

所謂生命週期互動就是指在容器建立Bean和銷燬Bean之前做某些操作。在Spring中我們可以通過讓Bean實現InitializingBean和DisposableBean介面,使用@PostConstruct和@PreDestroy以及通過如下的配置實現。

    <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> 
    <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

如果我們給一個Bean同時配置了上面多種的初始化和銷燬機制,那麼他們的執行順序如下:

初始化順序

  1. Methods annotated with @PostConstruct (這個方法在建構函式之後執行,是最佳實踐,可以和Spring API解耦)
  2. afterPropertiesSet() as defined by the InitializingBean callback interface(不建議使用,會耦合Spring API)
  3. A custom configured init() method(普通的xml配置檔案中配置的初始化方法)
    析構順序
  4. Methods annotated with @PreDestroy(建議使用)
  5. destroy() as defined by the DisposableBean callback interface (不建議使用)
  6. A custom configured destroy() method (普通的xml檔案中配置的銷燬方法)

Spring完整的初始化順序

  1. 容器啟動,例項化所有實現了BeanFactoyPostProcessor介面的類;這步會在任何普通Bean例項化之前載入;
  2. 例項化剩下的單例Bean,對這些Bean進行依賴注入;
  3. 如果Bean有實現BeanNameAware的介面那麼對這些Bean進行呼叫;
  4. 如果Bean有實現BeanFactoryAware介面的那麼對這些Bean進行呼叫;
  5. 如果Bean有實現ApplicationContextAware介面的那麼對這些Bean進行呼叫;
  6. 如果配置有實現BeanPostProcessor的Bean,那麼呼叫它的postProcessBeforeInitialization方法;
  7. 呼叫@PostConstruct註解的方法;
  8. 如果Bean有實現InitializingBean介面那麼對這些Bean進行呼叫;
  9. 如果Bean配置有init屬性,那麼呼叫它屬性中設定的方法;
  10. 如果配置有實現BeanPostProcessor的Bean,那麼呼叫它的postProcessAfterInitialization方法;
  11. Bean正常的使用;
  12. 呼叫@PreDestroy標註的方法;
  13. 呼叫DisposableBean介面的destory方法;
  14. 呼叫Bean定義是指定的destroy-method方法;

LifeCycle和SmartLifeCycle介面

Spring提供了LifeCycle介面,實現了這個介面的Bean在Spring容器呼叫start()和stop()方法的時候能收到Spring的回撥資訊,分別呼叫這個Bean的start()和stop()方法。

Aware介面

Spring提供了很多aware介面讓Bean來實現,提示IOC容器,這個Bean需要得到容器的某些元件或元素。

  • ApplicationContextAware
  • ApplicationEventPublisherAEwvaenrte
  • BeanClassLoaderAware
  • BeanFactoryAware
  • BeanNameAware
  • BootstrapContextAware:Typically available only in JCA aware ApplicationContext instances.
  • LoadTimeWeaverAware
  • MessageSourceAware
  • NotificationPublisherAwareSpring:Spring JMX notification publisher.
  • ResourceLoaderAware
  • ServletConfigAware
  • ServletContextAware

相關文章