IOC概述
1、理解:
(1)控制反轉(zhuǎn)。將生成對象的控制權(quán)交IOC容器,由容器生成依賴的對象。調(diào)用類只依賴接口,而不依賴具體的實現(xiàn)類,減少了耦合。在運行的時候,才由容器將具體的實例注入到調(diào)用類的對象中。
(2)依賴注入,就是向Ioc容器索要bean的過程。getBean是依賴注入的起點。依賴注入的過程是用戶第一次向Ioc容器索要Bean時觸發(fā)的。
(3)生成bean的兩種方式
a、通過反射調(diào)用構(gòu)造函數(shù)
b、通過CGLib
2、優(yōu)點:
(1)依賴關(guān)系的管理被反轉(zhuǎn)并交給容器,使復(fù)雜的依賴關(guān)系管理從應(yīng)用中解放出來。
(2)代碼解耦
3、啟動過程(依賴注入的實現(xiàn)過程):
a、Resource尋找資源(XML文件形式的beanDefinition)
b、將XML文件載入內(nèi)存中,解析成org.springframework.beans.factory.config.BeanDefinition對象
c、將org.springframework.beans.factory.config.BeanDefinition對象注冊到HashMap容器中
d、客戶想Ioc容器索要bean,觸發(fā)依賴注入
基礎(chǔ)使用
一、首先講解依賴注入的3種方式:
1、set方式注入:
假設(shè)有一個SpringAction,類中需要實例化一個SpringDao對象,那么就可以定義一個private的SpringDao成員變量,然后創(chuàng)建SpringDao的set方法(這是ioc的注入入口):
package com.bless.springdemo.action;
public class SpringAction {
//注入對象springDao
private SpringDao springDao;
//一定要寫被注入對象的set方法
public void setSpringDao(SpringDao springDao) {
this.springDao = springDao;
}
public void ok(){
springDao.ok();
}
}
隨后編寫spring的xml文件,<bean>中的name屬性是class屬性的一個別名,class屬性指類的全名,因為在SpringAction中有一個公共屬性Springdao,所以要在<bean>標(biāo)簽中創(chuàng)建一個<property>標(biāo)簽指定SpringDao。<property>標(biāo)簽中的name就是SpringAction類中的SpringDao屬性名,ref指下面<bean name="springDao"...>,這樣其實是spring將SpringDaoImpl對象實例化并且調(diào)用SpringAction的setSpringDao方法將SpringDao注入。
<!--配置bean,配置后該類由spring管理-->
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<!--(1)依賴注入,配置當(dāng)前類中相應(yīng)的屬性-->
<property name="springDao" ref="springDao"></property>
</bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
2、構(gòu)造器注入:
這種方式的注入是指帶有參數(shù)的構(gòu)造函數(shù)注入,看下面的例子,我創(chuàng)建了兩個成員變量SpringDao和User,但是并未設(shè)置對象的set方法,所以就不能支持Set注入方式,這里的注入方式是在SpringAction的構(gòu)造函數(shù)中注入,也就是說在創(chuàng)建SpringAction對象時要將SpringDao和User兩個參數(shù)值傳進(jìn)來:
public class SpringAction {
//注入對象springDao
private SpringDao springDao;
private User user;
public SpringAction(SpringDao springDao,User user){
this.springDao = springDao;
this.user = user;
System.out.println("構(gòu)造方法調(diào)用springDao和user");
}
public void save(){
user.setName("卡卡");
springDao.save(user);
}
}
在XML文件中同樣不用<property>的形式,而是使用<constructor-arg>標(biāo)簽,ref屬性同樣指向其它<bean>標(biāo)簽的name屬性:
<!--配置bean,配置后該類由spring管理-->
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<!--(2)創(chuàng)建構(gòu)造器注入,如果主類有帶參的構(gòu)造方法則需添加此配置-->
<constructor-arg ref="springDao"></constructor-arg>
<constructor-arg ref="user"></constructor-arg>
</bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
<bean name="user" class="com.bless.springdemo.vo.User"></bean>
解決構(gòu)造方法參數(shù)的不確定性,你可能會遇到構(gòu)造方法傳入的兩參數(shù)都是同類型的,為了分清哪個該賦對應(yīng)值,則需要進(jìn)行一些小處理:
下面是設(shè)置index,就是參數(shù)位置:
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<constructor-arg index="0" ref="springDao"></constructor-arg>
<constructor-arg index="1" ref="user"></constructor-arg>
</bean>
另一種是設(shè)置參數(shù)類型:
<constructor-arg type="java.lang.String" ref=""/>
3、接口注入:
ClassA依賴于InterfaceB,在運行期, InterfaceB 實例將由容器提供。
public class ClassA {
private InterfaceB clzB;
public Object doSomething(InterfaceB b) {
clzB = b;
return clzB.doIt();
}
}
……
}
二、Bean標(biāo)簽
1、scope屬性:
(1)singleton:單例模式,即該bean對應(yīng)的類只有一個實例;在spring 中是scope(作用范圍)參數(shù)的默認(rèn)值 ;
(2)prototype:表示每次從容器中取出bean時,都會生成一個新實例;相當(dāng)于new出來一個對象;
(3)request:基于web,表示每次接受一個HTTP請求時,都會生成一個新實例;
(4)session:表示在每一個session中只有一個該對象.
(5)global session
global session作用域類似于標(biāo)準(zhǔn)的HTTP Session作用域,不過它僅僅在基于portlet的web應(yīng)用中才有意義。Portlet規(guī)范定義了全局Session的概念,它被所有構(gòu)成某個portlet web應(yīng)用的各種不同的portlet所共享。在global session作用域中定義的bean被限定于全局portlet Session的生命周期范圍內(nèi)。如果你在web中使用global session作用域來標(biāo)識bean,那么web會自動當(dāng)成session類型來使用。
配置實例:
和request配置實例的前提一樣,配置好web啟動文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>
(6)自定義bean裝配作用域:
在spring2.0中作用域是可以任意擴展的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singleton和prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope來定義,自定義自己的作用域只要實現(xiàn)該接口即可,下面給個實例:
我們建立一個線程的scope,該scope在表示一個線程中有效,代碼如下:
publicclass MyScope implements Scope {
privatefinal ThreadLocal threadScope = new ThreadLocal() {
protected Object initialValue() {
returnnew HashMap();
}
};
public Object get(String name, ObjectFactory objectFactory) {
Map scope = (Map) threadScope.get();
Object object = scope.get(name);
if(object==null) {
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
public Object remove(String name) {
Map scope = (Map) threadScope.get();
return scope.remove(name);
}
publicvoid registerDestructionCallback(String name, Runnable callback) {
}
public String getConversationId() {
// TODO Auto-generated method stub
returnnull;
}
}
源碼解析
一、IOC容器:
1、對于Spring的使用者而言,IOC容器實際上是什么呢?我們可以說BeanFactory就是我們看到的IoC容器,當(dāng)然了Spring為我們準(zhǔn)備了許多種IoC容器來使用,比如說ApplicationContext。這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義信息來建立我們需要的IoC容器。
在Spring中,最基本的IOC容器接口是BeanFactory - 這個接口為具體的IOC容器的實現(xiàn)作了最基本的功能規(guī)定 - 不管怎么著,作為IOC容器,這些接口你必須要滿足應(yīng)用程序的最基本要求,查看BeanFactory的源碼:
public interface BeanFactory {
//這里是對FactoryBean的轉(zhuǎn)義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,
//如果需要得到工廠本身,需要轉(zhuǎn)義
String FACTORY_BEAN_PREFIX = "&";
//這里根據(jù)bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。
Object getBean(String name) throws BeansException;
//這里根據(jù)bean的名字和Class類型來得到bean實例,和上面的方法不同在于它會拋出異常:如果根據(jù)名字取得的bean實例的Class類型和需要的不同的話。
Object getBean(String name, Class requiredType) throws BeansException;
//這里提供對bean的檢索,看看是否在IOC容器有這個名字的bean
boolean containsBean(String name);
//這里根據(jù)bean名字得到bean實例,并同時判斷這個bean是不是單件
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//這里對得到bean實例的Class類型
Class getType(String name) throws NoSuchBeanDefinitionException;
//這里得到bean的別名,如果根據(jù)別名檢索,那么其原名也會被檢索出來
String[] getAliases(String name);
}
2、容器加載流程解析:
這里我們以ClassPathXmlApplicationContext的初始化為例
(1)首先從容器構(gòu)造函數(shù)入口:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//這里是IoC容器的初始化過程,其初始化過程的大致步驟由AbstractApplicationContext來定義
refresh();
}
}
(2)再看AbstractApplicationContext中refresh函數(shù)定義,這個方法包含了整個BeanFactory初始化的過程。這里使用到模板模式。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準(zhǔn)備這個上下文來刷新
prepareRefresh();
// 告訴子類刷新其beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準(zhǔn)備將要在上下文中使用的bean工廠
prepareBeanFactory(beanFactory);
try {
// 允許在上下文子類中對bean工廠進(jìn)行后處理
postProcessBeanFactory(beanFactory);
// 調(diào)用 factory processors注冊為上下文中的bean
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊 攔截bean創(chuàng)建的bean處理器
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 初始化此上下文的時間多播器
initApplicationEventMulticaster();
// 在特殊上下文子類中初始化其特殊的bean
onRefresh();
// 檢查監(jiān)聽器bean,并且注冊它們
registerListeners();
// 初始化所有剩下的(非懶加載)單例
finishBeanFactoryInitialization(beanFactory);
// 發(fā)布相應(yīng)的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 銷毀已經(jīng)創(chuàng)建的單例,以避免資源泄漏
destroyBeans();
// 重置active標(biāo)志位
cancelRefresh(ex);
// 拋出異常給調(diào)用者
throw ex;
}
finally {
// 在Spring的核心中重置常見的自檢緩存,因為我們可能不再需要單例對象的元數(shù)據(jù)了
resetCommonCaches();
}
}
}
(4)進(jìn)入obtainFreshBeanFactory()函數(shù),發(fā)現(xiàn)調(diào)用refreshBeanFactory(),而refreshBeanFactory()里面調(diào)用了loadBeanDefinitions()函數(shù),該函數(shù)描述了加載bean定義的過程,最終會調(diào)用”具體的解析和注冊過程“。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 為給定的BeanFactory創(chuàng)建一個新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用此上下文的資源加載環(huán)境,去配置bean定義閱讀器。
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允許子類提供reader的自定義初始化,然后執(zhí)行實際加載bean定義。
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
// 調(diào)用XmlBeanDefinitionReader來載入bean定義信息。
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
// XmlBeanDefinitionReader.java
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//這里是具體的解析和注冊過程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//通過解析得到DOM,然后完成bean在IOC容器中的注冊
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
....
我們看到先把定義文件解析為DOM對象,然后進(jìn)行具體的注冊過程:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 具體的注冊過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
(5)在BeanDefinitionDocumentReader中完成bean定義文件的解析和IOC容器bean的初始化。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root)
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
// 對配置文件(xml文件)進(jìn)行解析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//這里是解析過程的調(diào)用,對缺省的元素進(jìn)行分析比如bean元素
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//這里對我們最熟悉的bean元素進(jìn)行處理
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//委托給BeanDefinitionParserDelegate來完成對bean元素的處理,這個類包含了具體的bean解析的過程。
// 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要載體,也是IOC容器的管理對象。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 這里是向IOC容器注冊,實際上是放到IOC容器的一個map里
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 這里向IOC容器發(fā)送事件,表示解析和注冊完成
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
(6)我們看到在parseBeanDefinition中對具體bean元素的解析式交給BeanDefinitionParserDelegate來完成的,下面我們看看解析完的bean是怎樣在IOC容器中注冊的:
在BeanDefinitionReaderUtils調(diào)用的是:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//這是調(diào)用IOC來注冊的bean的過程,需要得到BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 別名也是可以通過IOC容器和bean聯(lián)系起來的進(jìn)行注冊
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
(7)我們看看XmlBeanFactory中的注冊實現(xiàn):
//---------------------------------------------------------------------
// 這里是IOC容器對BeanDefinitionRegistry接口的實現(xiàn)
//---------------------------------------------------------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
.....//這里省略了對BeanDefinition的驗證過程
//先看看在容器里是不是已經(jīng)有了同名的bean,如果有拋出異常。
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
...........
}
else {
//把bean的名字加到IOC容器中去
this.beanDefinitionNames.add(beanName);
}
//這里把bean的名字和Bean定義聯(lián)系起來放到一個HashMap中去,IOC容器通過這個Map來維護(hù)容器里的Bean定義信息。
this.beanDefinitionMap.put(beanName, beanDefinition);
removeSingleton(beanName);
}
這樣就完成了Bean定義在IOC容器中的注冊,就可被IOC容器進(jìn)行管理和使用了。
總結(jié)IOC容器初始化流程:
1、初始化的入口在容器實現(xiàn)中的refresh()調(diào)用來完成
2、將bean定義信息載入IOC容器。使用的方法是loadBeanDefinition,其中的大致過程如下:
(1)通過ResourceLoader來完成資源文件位置的定位,DefaultResourceLoader是默認(rèn)的實現(xiàn),同時上下文本身就給出了ResourceLoader的實現(xiàn),可以從類路徑,文件系統(tǒng), URL等方式來定為資源位置。如果是XmlBeanFactory作為IOC容器,那么需要為它指定bean定義的資源,也就是說bean定義文件時通過抽象成Resource來被IOC容器處理的
(2)容器通過BeanDefinitionReader來完成定義信息的解析和Bean信息的注冊,往往使用的是XmlBeanDefinitionReader來解析bean的xml定義文件 - 實際的處理過程是委托給BeanDefinitionParserDelegate來完成的,從而得到bean的定義信息,這些信息在Spring中使用BeanDefinition對象來表示 - 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition這些相關(guān)的方法 - 他們都是為處理BeanDefinitin服務(wù)的,IoC容器解析得到BeanDefinition以后,需要把它在IOC容器中注冊,這由IOC實現(xiàn) BeanDefinitionRegistry接口來實現(xiàn)。注冊過程就是在IOC容器內(nèi)部維護(hù)的一個HashMap來保存得到的 BeanDefinition的過程。這個HashMap是IoC容器持有bean信息的場所,以后對bean的操作都是圍繞這個HashMap來實現(xiàn)的。
(3)然后我們就可以通過BeanFactory和ApplicationContext來享受到Spring IOC的服務(wù)了.
Beanfactory 和Factory bean
1、BeanFactory 指的是IOC容器的編程抽象,比如ApplicationContext, XmlBeanFactory等,Bean工廠, 保存了所有的Bean并管理它們的生命周期和依賴關(guān)系。
2、Factory bean 是一個可以在IOC容器中被管理的一個bean,是對各種處理過程和資源使用的抽象,Factory bean在需要時產(chǎn)生另一個對象,而不返回FactoryBean本省,我們可以把它看成是一個抽象工廠,對它的調(diào)用返回的是工廠生產(chǎn)的產(chǎn)品。所有的 Factory bean都實現(xiàn)特殊的org.springframework.beans.factory.FactoryBean接口,當(dāng)使用容器中factory bean的時候,該容器不會返回factory bean本身,而是返回其生成的對象。Spring包括了大部分的通用資源和服務(wù)訪問抽象的Factory bean的實現(xiàn),其中包括:
對JNDI查詢的處理,對代理對象的處理,對事務(wù)性代理的處理,對RMI代理的處理等,這些我們都可以看成是具體的工廠,看成是SPRING為我們建立好的工廠。也就是說Spring通過使用抽象工廠模式為我們準(zhǔn)備了一系列工廠來生產(chǎn)一些特定的對象,免除我們手工重復(fù)的工作,我們要使用時只需要在IOC容器里配置好就能很方便的使用了。
IOC高級特性
一、lazy-init延遲加載
1、執(zhí)行原理:
lazy-init屬性:為true的話,在Ioc容器初始化過程中,會對BeanDefinitionMap中所有的Bean進(jìn)行依賴注入,這樣在初始化過程結(jié)束后,容器執(zhí)行g(shù)etBean得到的就是已經(jīng)準(zhǔn)備好的Bean,不需要進(jìn)行依賴注入。
2、優(yōu)點:當(dāng)應(yīng)用第一次向容器索取所需的Bean時,容器不再需要對Bean進(jìn)行初始化,直接從已經(jīng)完成實例化的Bean中取出需要的bean,這樣就提高了第一次獲取Bean的性能。
二、BeanPostProcessor后置處理器:
1、BeanPostProcessor后置處理器是Spring IoC容器經(jīng)常使用到的一個特性,這個Bean后置處理器是一個監(jiān)聽器,可以監(jiān)聽容器觸發(fā)的Bean聲明周期事件。后置處理器想容器注冊以后,容器中管理的Bean就具備了接收IoC容器事件回調(diào)的能力。
2、BeanPostProcessor的使用非常簡單,只需要提供一個實現(xiàn)接口BeanPostProcessor的實現(xiàn)類,然后在Bean的配置文件中設(shè)置即可。
3、API解釋:
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
*/
//實例化、依賴注入完畢,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
*/
//實例化、依賴注入、初始化完畢時執(zhí)行
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
三、自動裝配:
1、概念:無須在Spring配置文件中描述javaBean之間的依賴關(guān)系(如配置<property>、<constructor-arg>)。IOC容器會自動建立java Bean之間的關(guān)聯(lián)關(guān)系(通過autowire)。
2、在Spring中,支持 5 自動裝配模式。
(1)no – 缺省情況下,自動配置是通過“ref”屬性手動設(shè)定
(2)byName – 根據(jù)屬性名稱自動裝配。如果一個bean的名稱和其他bean屬性的名稱是一樣的,將會自裝配它。
(3)byType – 按數(shù)據(jù)類型自動裝配。如果一個bean的數(shù)據(jù)類型是用其它bean屬性的數(shù)據(jù)類型,兼容并自動裝配它。
(4)constructor – 在構(gòu)造函數(shù)參數(shù)的byType方式。
參考:
http://www.iteye.com/topic/86339
http://blessht.iteye.com/blog/1162131
https://blog.csdn.net/mastermind/article/details/1932787
https://blog.csdn.net/sugar_rainbow/article/details/76757383