Spring系列.Bean簡介

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
Tags: