[TOC]
主要內(nèi)容整理自Spring官方reference
Ioc容器
一個對象定義它的依賴(也就是它所協(xié)作的對象)歹撒,只能通過構造器參數(shù)(依賴作為構造器參數(shù)傳入)、工廠方法的參數(shù)情组,或者在此對象實例被構造或從工廠方法返回后在對象實例上設置屬性(set方法參數(shù))來建立依賴關系。Ioc容器在創(chuàng)建Bean時將這些依賴注入進去箩祥,也就是不需要顯式通過構造器傳參院崇,setter等方式傳入一個對象,容器幫我們把這件事完成了袍祖,這個過程的依賴設置從對象自身而言是被動的底瓣,是容器將依賴項“注入”到對象中,所以這翻轉(zhuǎn)了依賴建立的過程蕉陋,所以才成為依賴反轉(zhuǎn)濒持。
Spring容器是Spring的核心键耕,也是依賴注入的基礎。容器容納了應用所使用的bean對象柑营,并為這些對象根據(jù)依賴關系裝配屬性屈雄。容器的概念相當于Spring環(huán)境,置入容器中的對象官套,才會被spring框架所管理和操作酒奶,依賴注入也需要基于容器這個環(huán)境才能讓Spring知道注入什么以及注入到何處甚至如何注入。容器是SPring管轄的地方奶赔,一系列對象想要被Spring管理(或者說要使用Spring提供的特性)惋嚎,需要先“主動”地注冊到容器中,然后才能借助Spring的力量“被動”的注入依賴關系站刑。
對象主動注冊到容器的方式另伍,就涉及到配置,即配置對象由spring管理绞旅,配置對象之間的依賴關系摆尝,甚至還有配置對象被創(chuàng)建的形式和被銷毀的方法。但是只是配置因悲,交給Spring容器堕汞,此后對象的聲明周期由容器來管理,而非自行編寫的代碼來管理晃琳。
將應用對象和依賴關系配置到容器讯检,就需要使用容器提供的具體工具。借助工具才能將對象放入容器和讓容器了解對象之間的依賴卫旱。容器提供了兩個實現(xiàn)人灼,一個是基本的BeanFactory,另一個是基于BeanFactory的ApplicationContext,即應用上下文顾翼。
ApplicationContext和BeanFactory
- BeanFactory是基礎類型的IoC容器挡毅,提供完整的IoC服務支持。如果沒有特別指定暴构,默認使用的是延遲加載的策略:即為只有當客戶端對象需要訪問容器中的某個受管對象的時候跪呈,容器才會對該受管的對象進行初始化以及依賴注入操作。所以相對來說取逾,容器的初期啟動較快耗绿,所需要的資源也少。
- ApplicationContext是在BeanFactory基礎上構建的砾隅,即ApplicationContext實際上是BeanFactory的子類误阻,是相對比較高級的容器實現(xiàn),除了擁有BeanFactory的所有支持,還提供了其他高級的特性究反。ApplicationContext所管理的對象寻定,在該類型的容器啟動之后,默認全部初始化并綁定完成(也可以配置lazy-init來延遲加載)精耐,所以相對于BeanFactory狼速,它需要更多的系統(tǒng)資源,且初期的啟動也會慢一些卦停。
簡而言之向胡,BeanFactory提供了配置框架和基本功能,ApplicationContext添加了更多的企業(yè)特定功能如AOP功能惊完、Web應用僵芹、從屬性文件解析文本信息以及發(fā)布應用事件給感興趣的事件監(jiān)聽者等。ApplicationContext是BeanFactory的一個完整的超集小槐。因為ApplicationContext具備BeanFactory的所有功能拇派,且還有更多BeanFactory不具備的高級功能,一般除非在嵌入式系統(tǒng)中(資源緊張)會使用原始的BeanFactory凿跳,一般都是使用ApplicationContext的實現(xiàn)類件豌。
關于BeanFactory:
BeanFactory只是一個接口,且它只定義了如何訪問容器內(nèi)管理的bean的方法拄显,各個BeanFactory的實現(xiàn)類負責具體Bean的注冊以及管理工作苟径。
DefaultListableBeanFactory就是一個比較通用的BeanFactory的實現(xiàn)類案站,它除了間接實現(xiàn)了BeanFactory接口躬审,還實現(xiàn)了BeanDefinitionRegistry接口,BeanDefinitionRegistry接口才是在BeanFactory的實現(xiàn)中擔當Bean注冊管理的角色蟆盐,它的接口定義抽象了Bean的注冊邏輯承边。
BeanDefinitionRegistry接口依賴于BeanDefinition接口,這三個接口在IoC 的實現(xiàn)中的角色是這樣的:
BeanDefinition接口定義了每一個受管對象石挂,BeanDefinitionRegistry負責把每一個受管對象注冊到容器中博助,BeanFactory接口負責提供訪問這些受管對象的方法。
BeanDefinition:
每一個受管對象痹愚,在容器中都會有一個對應的BeanDefinition實例富岳,該實例負責保存對象的所有必要信息,包括對象的class類型拯腮,是否抽象等窖式,當客戶端想BeanFactory請求相應對象時,BeanFactory會通過這些信息為客戶端返回一個完備可用的對象實例动壤。
ApplicationContext接口有以下幾種常見實現(xiàn)來加載bean的定義到容器中萝喘。
常用的應用上下文:
- AnnotationConfigApplicationContext:從一個或多個Java的配置類中加載Spring應用上下文定義。
- AnnotationConfigWebApplicationContext:從一個或多個Java的配置類中加載Spring Web應用上下文定義。
- ClassPathXMLApplicationContext: 從classpath的一個或多個xml文件來加載上下文定義阁簸。
- FileSystemXMLApplicationContext: 從文件系統(tǒng)下的一個或多個xml文件加載上下文定義爬早。
- XmlWebApplicationContext: 從web應用下的一個或多個xml文件加載上下文定義。
從以上的上下文實現(xiàn)類來看启妹,可以反向得出Spring容器的配置方式可以有兩種:Java類配置和xml文件配置筛严。實際上Java類來配置可以分為編碼配置和注解來配置(注解也可以使用xml文件來開啟),實際上不同的配置方式對于容器來說是通用的翅溺,并不是獨立的配置脑漫,而且不同方式直接可以通過import匯總到一個地方。
關閉容器的方法
在非web環(huán)境下咙崎,可以通過以下方式關閉一個容器:
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
容器擴展點
BeanPostProcessor定制Bean
If you want to implement some custom logic after the Spring container finishes instantiating, configuring, and initializing a bean, you can plug in one or more BeanPostProcessor implementations.
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
An ApplicationContext automatically detects any beans that are defined in the configuration metadata which implement the BeanPostProcessor interface. The ApplicationContext registers these beans as post-processors so that they can be called later upon bean creation. Bean post-processors can be deployed in the container just like any other beans.
雖然BeanPostProcessor注冊的推薦方法是通過ApplicationContext自動檢測 (@Component) 优幸,但也可以通過使用addBeanPostProcessor
方法以編程方式將它們注冊到一個ConfigurableBeanFactory
中。當需要在注冊之前評估條件邏輯褪猛,或者甚至在層次結構的上下文中復制bean post處理器時网杆,這可能是有用的。但是請注意伊滋,以編程方式添加的BeanPostProcessor
不尊重有序接口碳却。這里的注冊順序決定了執(zhí)行的順序。還要注意笑旺,以編程方式注冊的BeanPostProcessor
總是在通過自動檢測注冊的處理器之前進行處理昼浦,而不考慮任何顯式排序。
BeanPostProcessor的實現(xiàn)是作用于所有Bean上的筒主。實現(xiàn)BeanPostProcessor接口的類是特殊的关噪,并被容器區(qū)別對待。它們直接引用的所有BeanPostProcessor和bean都在啟動時實例化乌妙,作為ApplicationContext的特殊啟動階段的一部分使兔。接下來,以排序的方式注冊所有的BeanPostProcessor并應用到容器中的所有進一步的bean藤韵。因為AOP自動代理是作為一個bean - postprocessor本身實現(xiàn)的虐沥,所以它們直接引用的bean和bean都不適合自動代理,因此也沒有將方面編織到它們中泽艘。
BeanFactoryPostProcessor自定義配置元數(shù)據(jù)
Spring IoC容器允許BeanFactoryPostProcessor讀取配置元數(shù)據(jù)欲险,并在容器實例化除BeanFactoryPostProcessor以外的任何bean之前可能對其進行更改。
可以配置多個BeanFactoryPostProcessor匹涮,順序有Ordered接口控制天试。
如果要更改實際的bean實例(即,從配置元數(shù)據(jù)創(chuàng)建的對象)焕盟,則需要使用BeanPostProcessor(如上面BeanPostProcessor定制bean中所述)秋秤。 雖然技術上可以在BeanFactoryPostProcessor中使用bean實例(例如宏粤,使用BeanFactory.getBean()),但這樣做會導致過早的bean實例化灼卢,從而違反標準的容器生命周期绍哎。 這可能會導致負面影響,例如繞過bean后處理鞋真。
此外崇堰,BeanFactoryPostProcessors的范圍是每個容器。 這僅在您使用容器層次結構時才相關涩咖。 如果在一個容器中定義BeanFactoryPostProcessor海诲,它將僅應用于該容器中的bean定義。 BeanFactoryPostProcessors不會在另一個容器中對一個容器中的Bean定義進行后處理檩互,即使兩個容器都是同一層次結構的一部分特幔。
bean工廠后處理器在ApplicationContext中聲明時自動執(zhí)行,以便將更改應用于定義容器的配置元數(shù)據(jù)闸昨。 Spring包含許多預定義的bean工廠后處理器蚯斯,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。 例如饵较,也可以使用自定義BeanFactoryPostProcessor來注冊自定義屬性編輯器拍嵌。
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
}
- 示例,占位符的解析
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
或者
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
FactoryBean自定義實例化邏輯
Implement the org.springframework.beans.factory.FactoryBean
interface for objects that are themselves factories.
FactoryBean接口是Spring IoC容器實例化邏輯的可插拔點循诉。 如果你有一個復雜的初始化代碼横辆,用Java表示,而不是(可能)冗長的XML茄猫,你可以創(chuàng)建自己的FactoryBean狈蚤,在該類中編寫復雜的初始化,然后將自定義FactoryBean插入容器募疮。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
當您需要向容器詢問實際的FactoryBean實例本身而不是它生成的bean時炫惩,在調(diào)用ApplicationContext的getBean()方法時僻弹,使用&符號(&)作為bean的id前綴阿浓。 因此,對于id為myBean的給定FactoryBean蹋绽,在容器上調(diào)用getBean(“myBean”)將返回FactoryBean的產(chǎn)品; 而芭毙,調(diào)用getBean(“&myBean”)會返回FactoryBean實例本身。
Bean
Bean實際上就是對象卸耘,被Spring管理的對象統(tǒng)稱為Bean退敦。一個Spring IOC容器管理一個或多個Bean。
這些Bean的創(chuàng)建依賴于所提供的配置元數(shù)據(jù)(如xml蚣抗、Java類配置侈百、注解配置)。在容器內(nèi)部,bean的定義包括他們的依賴關系钝域,對象的作用域等等相關的bean的定義讽坏,都被抽象為BeanDefinition
對象。包括:
- 包限定的類名
- bean的行為配置項(scope例证、生命周期回調(diào)等等)
- 依賴對象的引用
- 其他設置到新建對象的配置
這些元數(shù)據(jù)轉(zhuǎn)換成一系列標記每個bean的屬性:
class路呜、name、scope织咧、constructor arguments胀葱、properties、autowiring mode笙蒙、lazy-initialization mode抵屿、initialization method、destruction method
除了bean定義中包含關于如何創(chuàng)建特定bean的信息之外捅位,ApplicationContext實現(xiàn)還允許用戶對在容器外創(chuàng)建的現(xiàn)有對象進行注冊晌该。這是通過通過方法getBeanFactory()訪問ApplicationContext的BeanFactory來完成的,它返回BeanFactory實現(xiàn)DefaultListableBeanFactory绿渣。DefaultListableBeanFactory通過方法registerSingleton()和registerBeanDefinition()來支持這種注冊朝群。
Bean的命名
Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean. A bean usually has only one identifier, but if it requires more than one, the extra ones can be considered aliases。
在基于xml的配置中中符,可以指定一個Id或者name來標識一個bean姜胖,id不能重復。如果要向bean引入其他別名淀散,還可以在name屬性中指定它們撩轰,用逗號(女器,)、分號(;)分隔。
不強制為bean提供名稱或id戈毒。如果沒有顯式提供名稱或id,容器將為該bean生成唯一的名稱麸拄。但是星著,如果您希望按名稱引用該bean,通過使用ref元素或服務定位符樣式查找则剃,您必須提供一個名稱耘柱。使用內(nèi)部bean和autowire可以不顯式提供名稱。
- Bean Naming Conventions(慣例)
使用類名的首字母小寫形式棍现。另外使用組件自動掃描功能且沒有注明bean的name時调煎,Spring會依據(jù)此Convention為bean生成name。
實例化beans
實例化的方式是可以配置的己肮,有以下幾種:
- 默認情況下士袄,是調(diào)用無參構造器(所以要注意提供無參構造器)來實例化bean的悲关。
- factory-method模式,指定bean所在類的內(nèi)部靜態(tài)工廠方法來實例化娄柳,在xml中指定factory-method即可:
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
- 使用其他bean的工廠方法
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
Bean的生命周期
在傳統(tǒng)的java應用中坚洽,bean的生命周期很簡單,使用Java關鍵字new進行實例化西土,然后bean就可以使用了讶舰。一旦bean不再被使用,就會被垃圾回收機制自動回收需了。
在Spring容器中的bean的生命周期跳昼,是托付給Spring進行管理的,包括bean對象的實例化到對象死亡肋乍,都由容器來控制鹅颊。
在Spring容器的管理下,一個bean在準備就緒之前墓造,bean工廠會執(zhí)行若干步驟堪伍,如下歸納:
- Spring對bean進行實例化;
- Spring將值和bean的引用注入到bean對應的屬性中觅闽;
- 如果bean實現(xiàn)了
BeanNameAware
接口帝雇,Spring將bean的ID傳遞給setBeanName()
方法; - 如果bean實現(xiàn)了
BeanFactoryAware
接口,Spring將調(diào)用setBeanFactory()
方法蛉拙,將Beanfactory容器實例注入尸闸; - 如果bean實現(xiàn)了
ApplicationContextAware
接口,Spring將調(diào)用setApplicationContext()
方法孕锄,將bean所在的應用上下文的引用傳進來吮廉; - 如果bean實現(xiàn)了
BeanPostProcessor
接口,Spring將調(diào)用它們的postProcessBeforeInitialization()
方法畸肆; - 如果bean實現(xiàn)了
InitializingBean
接口宦芦,Spring將調(diào)用它們的afterPropertiesSet()
方法。類似地轴脐,如果bean使用init-method
聲明了初始化方法调卑,該方法也會調(diào)用; - 如果bean實現(xiàn)了
BeanPostProcessor
接口豁辉,Spring將調(diào)用它們的postProcessAfterInitialization()
方法令野; - 此時舀患,bean已經(jīng)準備就緒徽级,可以被應用程序使用了,它們將一直停留在應用上下文中聊浅,直到應用上下文被銷毀餐抢;
- 如果bean實現(xiàn)了
DisposableBean
接口现使,Spring將調(diào)用它的destory()
方法,同樣的旷痕,如果bean使用destory-method
聲明了銷毀方法碳锈,該方法也會被調(diào)用;
所以可以通過實現(xiàn)上面提到的接口來介入Bean的生命周期欺抗,提供各種回調(diào)函數(shù)來附加功能售碳。
其他一些aware接口:
Name | Injected Dependency |
---|---|
ApplicationContextAware |
Declaring ApplicationContext
|
ApplicationEventPublisherAware |
Event publisher of the enclosing ApplicationContext
|
BeanClassLoaderAware |
Class loader used to load the bean classes. |
BeanFactoryAware |
Declaring BeanFactory
|
BeanNameAware |
Name of the declaring bean |
BootstrapContextAware |
Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContext s |
LoadTimeWeaverAware |
Defined weaver for processing class definition at load time |
MessageSourceAware |
Configured strategy for resolving messages (with support for parametrization and internationalization) |
NotificationPublisherAware |
Spring JMX notification publisher |
PortletConfigAware |
Current PortletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext
|
PortletContextAware |
Current PortletContext the container runs in. Valid only in a web-aware Spring ApplicationContext
|
ResourceLoaderAware |
Configured loader for low-level access to resources |
ServletConfigAware |
Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext
|
ServletContextAware |
Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext
|
Bean的scope
Scope | Description |
---|---|
singleton | (Default)每個容器中的每個bean都是單例的 |
prototype | 對每個對bean的請求創(chuàng)建一個bean. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. |
session | Scopes a single bean definition to the lifecycle of an HTTP Session. |
globalSession | Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a Portlet context. |
application | Scopes a single bean definition to the lifecycle of a ServletContext. |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket. |
The request
, session
, globalSession
, application
, and websocket
scopes are only available if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you use these scopes with regular Spring IoC containers such as the ClassPathXmlApplicationContext, an IllegalStateException will be thrown complaining about an unknown bean scope.
-
singleton
-
prototype
配置scope
- xml
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
- annotation
@RequestScope
@Component
public class LoginAction {
// ...
}
Scoped beans as dependencies
如果您想將一個HTTP請求作用域bean注入一個更長的作用域的另一個bean,您可以選擇將一個AOP代理注入到作用域bean的位置绞呈。也就是說贸人,您需要注入一個代理對象,它公開與作用域?qū)ο笙嗤墓步涌诘枭且部梢詮南嚓P的范圍(比如HTTP請求)中檢索真正的目標對象艺智,并將方法調(diào)用委托給真正的對象。
例如:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
一個singleton scope的userManager依賴一個session scope的userPreferences圾亏,userManager之后實例化一次十拣,其依賴項也只會注入一次,而userPerference是每次seession創(chuàng)建一個新的志鹃,但是userManager使用的userPerference一直只能是最初注入的那個夭问,顯然這個是違反需求的。
因此就需要容器能創(chuàng)建這樣一個對象曹铃,該對象公開與UserPreferences類(理想情況下是UserPreferences實例)完全相同的公共接口甲喝,該對象可以從scope機制(HTTP請求、會話等)獲取真正的UserPreferences對象铛只。 也就是每次要使用短生命周期的bean依賴時埠胖,能借助這個表明(代理)bean獲取真正的bean。
容器將這個代理對象注入到userManager bean中淳玩,后者不知道這個UserPreferences引用是一個代理直撤。在本例中,當UserManager實例調(diào)用依賴注入的UserPreferences對象上的方法時蜕着,它實際上是調(diào)用代理上的方法谋竖。然后代理從HTTP會話(在本例中)獲取真正的UserPreferences對象,并將方法調(diào)用委托給檢索到的真正的UserPreferences對象承匣。
這個代理的生成蓖乘,需要借助AOP的功能,配置上只需要加一個<aop:scoped-proxy/>
(xml):
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
依賴注入
注入的數(shù)據(jù)類型
- bean(ref)
- 字面量(value)
注入的形式
- 構造器DI(constructor-arg)
<beans>
<bean id="foo" class="x.y.Foo">
<!--按參數(shù)順序-->
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<!--指定參數(shù)類型-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
<!--指定參數(shù)位置index-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
<!--指定參數(shù)名字-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
</beans>
The ApplicationContext supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another.
- setter DI(property)
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class.
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
Circular dependencies
如果出現(xiàn)A依賴B韧骗,B又依賴 A的環(huán)形依賴嘉抒,容器會檢測到并會拋出BeanCurrentlyInCreationException。
自動化裝配
即基于注解掃描的自動化方案袍暴。
- Autowiring modes
Mode | Explanation |
---|---|
no | (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
byName | Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property. |
byType | Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. |
constructor | Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
- autowiring的優(yōu)點
- 自動裝配可以顯著減少指定屬性或構造函數(shù)參數(shù)的需要些侍。
- 自動化更新依賴關系隶症,無需維護xml文件
- autowiring的缺點
- 在
property
和constructor-arg
設置中顯式的依賴總是覆蓋自動連接。您不能自動連接所謂的簡單屬性岗宣,如原語蚂会、字符串和類(以及此類簡單屬性的數(shù)組)。 - 依賴關系的表達沒有顯示配置那么明確耗式,盡管Spring謹慎地避免猜測可能會產(chǎn)生意想不到的結果胁住,但Spring管理對象之間的關系不再明確地記錄下來。
- 可能從Spring容器生成文檔的工具可能無法獲得連接信息刊咳。
- 容器內(nèi)的多個bean定義可能與setter方法或構造器參數(shù)指定的類型相匹配措嵌,以便自動連接。需要明確唯一性芦缰,否則會拋出異常企巢。
Method injection
在大多數(shù)應用程序場景中,容器中的大多數(shù)bean都是單例的让蕾。當單例bean需要與另一個單例bean協(xié)作時浪规,或者非單例bean需要與另一個非單例bean協(xié)作時,通常通過將一個bean定義為另一個bean的屬性來處理依賴關系探孝。當bean的生命周期不同時笋婿,就會出現(xiàn)問題。假設單例bean A需要使用非單例(原型)bean B顿颅,可能是在A上的每個方法調(diào)用上缸濒,容器只創(chuàng)建一次單例bean A,因此只有一次機會設置屬性粱腻。容器不能在每次需要bean B時為bean A提供一個新的實例庇配。
解決辦法是放棄一些控制反轉(zhuǎn)(還有Bean Scope中協(xié)調(diào)不同生命周期的注入使用AOP代理的方式)。您可以通過實現(xiàn)applicationcontextAware接口來讓bean知道容器绍些,并且通過在每次bean A需要的時候捞慌,讓一個getBean(“B”)調(diào)用容器請求(一個典型的新)bean實例。下面是這種方法的一個例子:
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
參考資料
[1] Spring實戰(zhàn)
[2] Spring Framework Reference Documentation