Spring之IOC、核心容器和Bean概念詳解

    這一週忙了很多與代碼無關的事,感覺心態上還是有些急躁,週中挑幾個晚上看了一些文章,上午起來總結了一下,下午開始寫博客,因爲沒有時間擼代碼,所以就打算先把看到的概念梳理梳理,磨刀不誤砍柴工。

    首先來看一看什麼是IOC,他的全稱是Inversion of Control,即控制反轉,如果上網找絕大多數的概念是這樣說的:Spring實現了把原始的通過代碼操縱的組件和應用的調用權利交給了容器,由容器去進行組件代碼的控制和管理,反轉也就是把組件代碼的控制權由外部代碼轉移到了內部容器。

    可是光這麼說還是有一些抽象,本着知乎上先問是什麼再問是不是的原則,我們把這個問題拆解開來看,首先梳理出來參與者,一般會有三個參與者,一個是應用對象,一個是IOC容器,一個是該對象所依賴(或所使用)的另一個對象。應用對象很好理解,就是一個單純的Java對象,IOC容器簡單理解就是Spring實現IOC的一套框架程序,所依賴對象就是這個對象實現過程中需要依賴的某個外部程序。

    梳理明白了這個參與者的問題,下面解釋什麼是控制反轉,控制,即對應用對象的控制權,反轉,就意味着對象的初始化等一系列工作不再需要我們在外部完成,而是全部交給容器這個框架程序,我們只是被動地等待,所以被反轉了,那麼這麼做有什麼好處呢?事實上使用Spring配置文件來管理對象,可以大幅度減少代碼間的耦合,不必在上層類調用下層類的時候進行下層類的初始化。這裏再提到另外一個概念,就是依賴注入(DependencyInjection),其實就我的理解,依賴注入和控制反轉說的是一回事,控制反轉是結果,實現這個結果的過程(或者叫方法)是依賴注入,也就是反轉的過程是通過把應用程序注入到容器中所實現的,而這裏的依賴是指應用程序的工作要依賴容器去完成,因爲容器控制着應用程序所依賴的外部對象。

    或者還可以這麼說,依賴注入和控制反轉其實是表述的對象不同,依賴注入是從應用程序的角度來說的,應用程序需要將原始的,對象的創建過程交給容器去做,應用程序中對象的創建就依賴容器去完成,而容器創建這個對象使用的就是注入的方式,比如set注入或者是構造注入什麼的而控制反轉是從容器的角度去說的,容器將之前手動在程序裏去創建的對象反轉爲由容器自己去創建,所以叫控制反轉。


    說了這麼久的容器,那麼容器到底是什麼呢,上文說了可以把容器理解爲一個實現控制反轉的框架程序,那麼這個程序是如何實現的,又是如果工作的?

    容器,顧名思義,是承載東西的一個器皿,從程序的角度,可以大致把容器分爲兩類,一類是web容器,就是一個位於應用程序和平臺之間的接口集合,比如Tomcat等,還有一類,就是存儲和組織其他對象的對象,比如Java的Map,List類等等,我們可以叫它編程容器。容器可以管理對象的生命週期,對象與對象之間的依賴關係,我們依靠一個XML文件(通常情況下),來配置一個對象的名稱,id,產生方式,以及產生後是否作爲另一個對象的屬性等,而這個對象的初始化,設置依賴關係的過程不用寫一行代碼,Spring實現了代碼間的高度解耦。

    這是Spring的接口設計圖,這裏我們只關注兩條繼承路線,一條是從BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,這是一條BeanFactory的設計路線,他規定了IOC容器的規範,首先在BeanFactory接口中定義了類似getBean()等基礎的方法,然後在HierarchicalBeanFactory繼承了它之後,實現了getParentBeanFactory()方法,使對象具有雙親IOC容器的管理功能,然後在ConfigurableBeanFactory接口中,又實現了setParentBeanFactory功能來設置雙親IOC容器,通過一層層接口的疊加,來實現IOC的基本功能。

    另一條線是從BeanFactory到ListableBeanFactory,再到ApplicationContext,然後到我們經常使用的WebApplicationContext和ConfigurableApplicationContext等接口,ListableBeanFactory在BeanFactory的基礎上實現了類似getBeanDefinitionNames等細分功能,而ApplicationContext通過繼承ResourceLoader等方法,又實現了許多對高級容器支持的特性。

    BeanFactory源碼:

/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;

/**
 * The root interface for accessing a Spring bean container.
 * This is the basic client view of a bean container; further interfaces
 * such as <code>ListableBeanFactory</code> and <code>ConfigurableBeanFactory</code>
 * are available for specific purposes.
 *
 * <p>This interface is implemented by objects that hold a number of bean definitions,
 * each uniquely identified by a String name. Depending on the bean definition,
 * the factory will return either an independent instance of a contained object
 * (the Prototype design pattern), or a single shared instance (a superior
 * alternative to the Singleton design pattern, in which the instance is a
 * singleton in the scope of the factory). Which type of instance will be returned
 * depends on the bean factory configuration: the API is the same. The Singleton
 * approach is more useful and more common in practice.
 *
 * <p>The point of this approach is that the BeanFactory is a central registry
 * of application components, and centralizes configuration of application
 * components (no more do individual objects need to read properties files,
 * for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and
 * Development" for a discussion of the benefits of this approach.
 *
 * <p>Note that it is generally better to rely on Dependency Injection
 * ("push" configuration) to configure application objects through setters
 * or constructors, rather than use any form of "pull" configuration like a
 * BeanFactory lookup. Spring's Dependency Injection functionality is
 * implemented using BeanFactory and its subinterfaces.
 *
 * <p>Normally a BeanFactory will load bean definitions stored in a configuration
 * source (such as an XML document), and use the org.springframework.beans package
 * to configure the beans. However, an implementation could simply return Java
 * objects it creates as necessary directly in Java code. There are no constraints
 * on how the definitions could be stored: LDAP, RDBMS, XML, properties file etc.
 * Implementations are encouraged to support references amongst beans, to either
 * Singletons or Prototypes.
 *
 * <p>In contrast to the methods in ListableBeanFactory, all of the methods in this
 * interface will also check parent factories if this is a HierarchicalBeanFactory.
 * If a bean is not found in this factory instance, the immediate parent is asked.
 * Beans in this factory instance are supposed to override beans of the same name
 * in any parent factory.
 *
 * <p>Bean factory implementations should support the standard bean lifecycle interfaces
 * as far as possible. The full set of initialization methods and their standard order is:<br>
 * 1. BeanNameAware's <code>setBeanName</code><br>
 * 2. BeanClassLoaderAware's <code>setBeanClassLoader</code><br>
 * 3. BeanFactoryAware's <code>setBeanFactory</code><br>
 * 4. ResourceLoaderAware's <code>setResourceLoader</code>
 * (only applicable when running in an application context)<br>
 * 5. ApplicationEventPublisherAware's <code>setApplicationEventPublisher</code>
 * (only applicable when running in an application context)<br>
 * 6. MessageSourceAware's <code>setMessageSource</code>
 * (only applicable when running in an application context)<br>
 * 7. ApplicationContextAware's <code>setApplicationContext</code>
 * (only applicable when running in an application context)<br>
 * 8. ServletContextAware's <code>setServletContext</code>
 * (only applicable when running in a web application context)<br>
 * 9. <code>postProcessBeforeInitialization</code> methods of BeanPostProcessors<br>
 * 10. InitializingBean's <code>afterPropertiesSet</code><br>
 * 11. a custom init-method definition<br>
 * 12. <code>postProcessAfterInitialization</code> methods of BeanPostProcessors
 *
 * <p>On shutdown of a bean factory, the following lifecycle methods apply:<br>
 * 1. DisposableBean's <code>destroy</code><br>
 * 2. a custom destroy-method definition
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 13 April 2001
 * @see ListableBeanFactory
 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
 * @see BeanNameAware#setBeanName
 * @see BeanClassLoaderAware#setBeanClassLoader
 * @see BeanFactoryAware#setBeanFactory
 * @see org.springframework.context.ResourceLoaderAware#setResourceLoader
 * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
 * @see org.springframework.context.MessageSourceAware#setMessageSource
 * @see org.springframework.context.ApplicationContextAware#setApplicationContext
 * @see org.springframework.web.context.ServletContextAware#setServletContext
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
 * @see InitializingBean#afterPropertiesSet
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
 * @see DisposableBean#destroy
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
 */
public interface BeanFactory {

	/**
	 * Used to dereference a FactoryBean and distinguish it from beans
	 * <i>created</i> by the FactoryBean. For example, if the bean named
	 * <code>myEjb</code> is a FactoryBean, getting <code>&myEjb</code> will
	 * return the factory, not the instance returned by the factory.
	 */
	String FACTORY_BEAN_PREFIX = "&";


	/**
	 * Return an instance, which may be shared or independent, of the given bean name.
	 * This method allows a Spring BeanFactory to be used as a replacement for the
	 * Singleton or Prototype design pattern.
	 * <p>Callers may retain references to returned objects in the case of Singleton beans.
	 * <p>Translates aliases back to the corresponding canonical bean name.
	 * Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the name of the bean to return
	 * @return the instance of the bean
	 * @throws NoSuchBeanDefinitionException if there is no bean definition
	 * with the specified name
	 * @throws BeansException if the bean could not be obtained
	 */
	Object getBean(String name) throws BeansException;

	/**
	 * Return an instance (possibly shared or independent) of the given bean name.
	 * <p>Behaves the same as getBean(String), but provides a measure of type safety by
	 * throwing a Spring BeansException if the bean is not of the required type.
	 * This means that ClassCastException can't be thrown on casting the result correctly,
	 * as can happen with <code>getBean(String)</code>.
	 * @param name the name of the bean to return
	 * @param requiredType type the bean must match. Can be an interface or superclass
	 * of the actual class, or <code>null</code> for any match. For example, if the value
	 * is <code>Object.class</code>, this method will succeed whatever the class of the
	 * returned instance.
	 * @return an instance of the bean (never <code>null</code>)
	 * @throws BeanNotOfRequiredTypeException if the bean is not of the required type
	 * @throws NoSuchBeanDefinitionException if there's no such bean definition
	 * @throws BeansException if the bean could not be created
	 */
	Object getBean(String name, Class requiredType) throws BeansException;

	/**
	 * Does this bean factory contain a bean definition with the given name?
	 * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the name of the bean to query
	 * @return whether a bean with the given name is defined
	 */
	boolean containsBean(String name);

	/**
	 * Is this bean a singleton? That is, will <code>getBean</code> always return the same object?
	 * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the name of the bean to query
	 * @return is this bean a singleton
	 * @throws NoSuchBeanDefinitionException if there is no bean with the given name
	 * @see #getBean
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	/**
	 * Determine the type of the bean with the given name.
	 * More specifically, checks the type of object that <code>getBean</code> would return.
	 * For a FactoryBean, returns the type of object that the FactoryBean creates.
	 * @param name the name of the bean to query
	 * @return the type of the bean, or <code>null</code> if not determinable
	 * @throws NoSuchBeanDefinitionException if there is no bean with the given name
	 * @since 1.1.2
	 * @see #getBean
	 * @see FactoryBean#getObjectType()
	 */
	Class getType(String name) throws NoSuchBeanDefinitionException;

	/**
	 * Return the aliases for the given bean name, if defined.
	 * <p>If the given name is an alias, the corresponding original bean name
	 * and other aliases (if any) will be returned, with the original bean name
	 * being the first element in the array.
	 * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the bean name to check for aliases
	 * @return the aliases, or an empty array if none
	 */
	String[] getAliases(String name);

}

ApplicationContext源碼:

/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.io.support.ResourcePatternResolver;

/** 
 * Central interface to provide configuration for an application.
 * This is read-only while the application is running, but may be
 * reloaded if the implementation supports this.
 *
 * <p>An ApplicationContext provides:
 * <ul>
 * <li>Bean factory methods, inherited from ListableBeanFactory.
 * This avoids the need for applications to use singletons.
 * <li>The ability to resolve messages, supporting internationalization.
 * Inherited from the MessageSource interface.
 * <li>The ability to load file resources in a generic fashion.
 * Inherited from the ResourceLoader interface.
 * <li>The ability to publish events. Implementations must provide a means
 * of registering event listeners.
 * <li>Inheritance from a parent context. Definitions in a descendant context
 * will always take priority. This means, for example, that a single parent
 * context can be used by an entire web application, while each servlet has
 * its own child context that is independent of that of any other servlet.
 * </ul>
 *
 * <p>In addition to standard bean factory lifecycle capabilities,
 * ApplicationContext implementations need to detect ApplicationContextAware
 * beans and invoke the setApplicationContext method accordingly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see ApplicationContextAware#setApplicationContext
 * @see ConfigurableApplicationContext
 */
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the parent context, or <code>null</code> if there is no parent,
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or <code>null</code> if there is no parent
	 */
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * <p>This is not typically used by application code, except for the purpose
	 * of initializing bean instances that live outside the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * ConfigurableApplicationContext interface offers access to the
	 * AutowireCapableBeanFactory interface too. The present method mainly
	 * serves as convenient, specific facility on the ApplicationContext
	 * interface itself.
	 * @throws IllegalStateException if the context does not support
	 * the AutowireCapableBeanFactory interface or does not hold an autowire-capable
	 * bean factory yet (usually if <code>refresh()</code> has never been called)
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context
	*/
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

}

    總結一下:BeanFactory是Spring中比較原始的Factory,他無法支持Spring的許多插件,比如AOP功能,Web應用等,而ApplicationContext由BeanFactory接口繼承而來,因此提供更多功能,是以一種更面向框架的方式工作以及對上下文進行分層和實現繼承。

    最後說一個概念,就是Spring裏的Bean,Spring的一切都是圍繞Bean來工作的,創建Bean,配置Bean,最後銷燬Bean,下面是一片博文中對Bean的配置和理解,摘錄如下,一個最基本的Bean的配置:

<!-- 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"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
  <!-- 1. 定義一個Bean,id是這個Bean的唯一標識,class指出這個Bean的來源,singleton指定這個Bean是否是單例模式,depends-on指定這個Bean初始化前,強制初始化date -->
  <bean id="HelloWorld" class="com.bjpowernode.spring.manager.HelloWorld" singleton="true" depends-on="date">
      <!-- 2. 配置Bean的屬性 -->
      <property name="msg">
          <value>Hello World</value>
      </property>
     <!-- 3. 指定Bean的一個依賴 -->
      <property name="date">
          <ref bean="date"/>
      </property>
 <!-- 4. 定義上面Bean的結束 -->
  </bean>  
 <bean id="date" class="java.util.Date"/>
</beans>

從內容和註釋我們可以總結出以下幾點:
    1. Bean的標識(id和name) 
        在Spring中可以用id或name屬性來指定Bean的id,並且至少指定一個id。id和name的區別:id屬性是Bean的唯一標識,不可重複標記,並且它在XML DTD中作爲一個XML元素的ID屬性被標記。如果開發中需要給Bean增加別名,可以通過name屬性指定一個或多個id,多個id用(,)或(;)分隔。
    2. Bean的類(class)
    	在Spring的配置文檔中,class屬性指明瞭Bean的來源,即Bean的實際路徑。
    3. Singleton的使用
    	在Spring中,Bean可以定義爲兩種部署模式:singleton或non-singleton(prototype)。Spring默認爲singleton模式。
    	如果一個Bean被定義爲singleton模式:只有一個共享的實例存在,所有對這個Bean的請求都會返回這個唯一的實例。
    	如果一個Bean被定義爲non-singleton(prototype)模式,那麼對這個Bean的每次請求都會創建一個新的bean實例。
    4. 使用依賴depends-on
    	Bean的depends-on屬性可以用來在初始化使用這個Bean之前,強制執行一個或多個Bean的初始化。詳見上面代碼中的第1點。
    【生命週期】
    	一個Bean從定義到銷燬都有一個生命週期。在Spring中,Bean的生命週期包括Bean的定義、初始化、使用和銷燬4個階段。下面分別進行介紹:
    1. Bean的定義
    	在Spring中,通常是通過配置文檔的方式定義Bean。如上面的代碼所示。
    	在一個大的應用中,會有很多的Bean需要定義,這樣配置文檔就會很大,而不好維護。所以,我們可以把相關的Bean放在一個配置文檔中,出現多個配置文檔。
    2. Bean的初始化
    	第一種方式,通過在配置文檔中指定init-method屬性完成。    
	實現思路:在類中增加一個初始化方法init(),用來完成初始化工作,並去掉構造函數。修改配置文檔,指定Bean的初始化方法爲init(),即init-method="init",並去掉通過setter注入方法。
    	第二種方式,實現org.springframework.beans.factory.InitializingBean接口。
	實現思路:讓類實現InitializingBean接口,增加afterPropertiesSet()完成初始化工作,然後修改配置文檔。
    	以上兩種方式都是Bean的初始化方式,但第一種方式沒有把代碼耦合於Spring。
    3. Bean的使用
    	在Spring中,Bean的使用有3種方式:BeanWrapper、BeanFactory和ApplicationContext。
    	通過前面的學習,我們對後面兩種都很熟悉了。在此也不具體講解了。
    4. Bean的銷燬
    	在Spring中,Bean的銷燬有以下兩種方式:
    	第一種,在配置文檔中通過制定destroy-method屬性完成。
    	第二種,實現org.springframework.beans.factory.DisposableBean接口。
    	和初始化方式相同,不再寫出具體的實現思路。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章