概述
BeanFatory
作為Spring的IOC容器主要提供了注冊(cè)Bean和管理依賴(lài)這兩個(gè)功能缀蹄,它的接口包括如下函數(shù):
而
ApplicationContext
在間接繼承了BeanFactory
的基礎(chǔ)上榆骚,還多繼承了四個(gè)接口,提供了更多的功能框冀,如事件發(fā)布阅爽,資源載入,消息等。實(shí)現(xiàn)
BeanFactory
接口的實(shí)現(xiàn)為了實(shí)現(xiàn)注冊(cè)Bean的功能钥飞,需要使用BeanDefinitionRegistry
這個(gè)接口,這三者的關(guān)系如圖:
可以看到衫嵌,
BeanFactory
的實(shí)現(xiàn)同時(shí)實(shí)現(xiàn)了這兩個(gè)接口读宙,而它的內(nèi)部,正是使用BeanDefinition
來(lái)管理Bean:
無(wú)論是通過(guò)代碼還是配置文件或者注解楔绞,本質(zhì)上都是通過(guò)
BeanDefinitionReader
先構(gòu)造BeanDefinition
结闸,然后將BeanDefinition
注冊(cè)到BeanDefinitionRegistry
粱哼,通常也就是BeanFactory
的實(shí)現(xiàn)透硝。
IoC容器的兩個(gè)階段
容器啟動(dòng)階段
- 加載配置
- 分析配置信息
- 裝配到BeanDefinition
- PostProcess
BeanFactoryPostProcessor是容器的擴(kuò)展機(jī)制,可在實(shí)例化階段之前對(duì)BeanDefinition中的信息進(jìn)行修改
如PropertyPlaceHolderConfigurer抠忘,把它聲明為Bean蔫耽,ApplicationContext會(huì)注冊(cè)并應(yīng)用它结耀,將bean配置中的占位符用屬性文件中的值替換
Bean實(shí)例化階段
- 實(shí)例化對(duì)象
- 裝配依賴(lài)
- 生命周期回調(diào)
- 對(duì)象的其他處理
- 注冊(cè)回調(diào)接口
- 實(shí)例化
BeanFactory根據(jù)BeanDefinition來(lái)進(jìn)行實(shí)例化,使用策略模式有反射和cglib兩種方式來(lái)實(shí)例化bean匙铡,返回一個(gè)包裹了bean的BeanWrapper图甜,用于第二步設(shè)置屬性 - 設(shè)置屬性
BeanWrapperImpl同時(shí)也是繼承了PropertyEditorRegistry,使用PropertyEditor來(lái)轉(zhuǎn)換類(lèi)型慰枕,設(shè)置屬性 - Aware接口
BeanFactory: BeanNameAware BeanClassLoaderAware BeanFactoryAware
ApplicationContext: ResourceLoaderAware ApplicationEventPublisherAware MessageSourceAware ApplicationContextAware(都是ApplicationContext) - BeanPostProcessor
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.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
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.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
常用場(chǎng)景:1.處理Aware接口 2.生成當(dāng)前對(duì)象的代理(AOP)
ApplicationContextAwareProcessor處理ApplicationContext相關(guān)的各個(gè)Aware接口具则。
用BeanPostProcessor處理Marker接口時(shí)(如Aware接口),其實(shí)這個(gè)接口相當(dāng)于為這個(gè)Bean添加了新的屬性具帮,接口一般包括的函數(shù)只有設(shè)置屬性和獲取屬性博肋,獲取屬性由Bean的Client調(diào)用,設(shè)置屬性的函數(shù)則由BeanPostProcessor調(diào)用來(lái)進(jìn)行設(shè)置蜂厅,所以這種Marker接口一般是在BeanPostProcessor的Before函數(shù)中處理匪凡,緊鄰著B(niǎo)ean實(shí)例化過(guò)程中的設(shè)置屬性(因?yàn)檫@也是設(shè)置屬性的一種,只不過(guò)是所有Bean統(tǒng)一設(shè)置)掘猿。
相對(duì)的病游,使用BeanPostProcessor來(lái)生成proxy時(shí)會(huì)在after函數(shù)中處理,這時(shí)候bean的初始化已經(jīng)完成稠通,可以被代理了衬衬。
InitializingBean和init-method
Bean的生命周期接口和生命周期方法注冊(cè)銷(xiāo)毀回調(diào)
以上的設(shè)置都完成之后,如果Bean實(shí)現(xiàn)了DisposableBean接口或者聲明了destroy-method屬性改橘,那么這時(shí)候容器會(huì)為這個(gè)Bean注冊(cè)一個(gè)用于銷(xiāo)毀的回調(diào)滋尉,當(dāng)關(guān)閉容器的時(shí)候,主動(dòng)調(diào)用一個(gè)函數(shù)飞主,來(lái)執(zhí)行bean的銷(xiāo)毀工作狮惜。
ConfigurableBeanFactory
/**
* Destroy all singleton beans in this factory, including inner beans that have
* been registered as disposable. To be called on shutdown of a factory.
* <p>Any exception that arises during destruction should be caught
* and logged instead of propagated to the caller of this method.
*/
void destroySingletons();
ConfigurableApplicationContext
/**
* Register a shutdown hook with the JVM runtime, closing this context
* on JVM shutdown unless it has already been closed at that time.
* <p>This method can be called multiple times. Only one shutdown hook
* (at max) will be registered for each context instance.
* @see java.lang.Runtime#addShutdownHook
* @see #close()
*/
void registerShutdownHook();
- 使用
ApplicationContext
- BeanFactoryPostProcessor高诺,BeabPostProcessor等特殊bean的自動(dòng)識(shí)別
- bean實(shí)例的自動(dòng)初始化
- 統(tǒng)一的資源加載策略
- 國(guó)際化的信息支持
- 容器內(nèi)事件發(fā)布
資源加載
問(wèn)題:
- java.net.URL局限于http,ftp,file等協(xié)議,而資源可以以二進(jìn)制碾篡、字節(jié)流虱而、文件等形式存在于文件系統(tǒng)、classpath开泽、URL定位等地方
- java.net.URL對(duì)資源的查找和定位沒(méi)有清晰的界限牡拇,返回的形式多種多樣,沒(méi)有統(tǒng)一的抽象
解決:
Resource與ResourceLoader
ClassPathResource class|classLoader#getResourceAsStream
FileSystemResource
UrlResource UrlConnection#getInputStream
ByteArrayResource
InputStreamResource
public interface Resource extends InputStreamSource {
/**
* Return whether this resource actually exists in physical form.
* <p>This method performs a definitive existence check, whereas the
* existence of a {@code Resource} handle only guarantees a
* valid descriptor handle.
*/
boolean exists();
/**
* Return whether the contents of this resource can be read,
* e.g. via {@link #getInputStream()} or {@link #getFile()}.
* <p>Will be {@code true} for typical resource descriptors;
* note that actual content reading may still fail when attempted.
* However, a value of {@code false} is a definitive indication
* that the resource content cannot be read.
* @see #getInputStream()
*/
boolean isReadable();
/**
* Return whether this resource represents a handle with an open
* stream. If true, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be {@code false} for typical resource descriptors.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
File getFile() throws IOException;
/**
* Determine the content length for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long contentLength() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Determine a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
* <p>Returns {@code null} if this type of resource does not
* have a filename.
*/
String getFilename();
/**
* Return a description for this resource,
* to be used for error output when working with the resource.
* <p>Implementations are also encouraged to return this value
* from their {@code toString} method.
* @see Object#toString()
*/
String getDescription();
}
public interface InputStreamSource {
/**
* Return an {@link InputStream}.
* <p>It is expected that each call creates a <i>fresh</i> stream.
* <p>This requirement is particularly important when you consider an API such
* as JavaMail, which needs to be able to read the stream multiple times when
* creating mail attachments. For such a use case, it is <i>required</i>
* that each {@code getInputStream()} call returns a fresh stream.
* @return the input stream for the underlying resource (must not be {@code null})
* @throws IOException if the stream could not be opened
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
*/
InputStream getInputStream() throws IOException;
}
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* Return a Resource handle for the specified resource.
* The handle should always be a reusable resource descriptor,
* allowing for multiple {@link Resource#getInputStream()} calls.
* <p><ul>
* <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
* <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
* <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
* (This will be implementation-specific, typically provided by an
* ApplicationContext implementation.)
* </ul>
* <p>Note that a Resource handle does not imply an existing resource;
* you need to invoke {@link Resource#exists} to check for existence.
* @param location the resource location
* @return a corresponding Resource handle
* @see #CLASSPATH_URL_PREFIX
* @see org.springframework.core.io.Resource#exists
* @see org.springframework.core.io.Resource#getInputStream
*/
Resource getResource(String location);
/**
* Expose the ClassLoader used by this ResourceLoader.
* <p>Clients which need to access the ClassLoader directly can do so
* in a uniform manner with the ResourceLoader, rather than relying
* on the thread context ClassLoader.
* @return the ClassLoader (only {@code null} if even the system
* ClassLoader isn't accessible)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
*/
ClassLoader getClassLoader();
}
DefaultResourceLoader#getResource
(1) 檢查路徑是否以classpath:開(kāi)頭->ClassPathResource
(2) a.通過(guò)Url來(lái)定位資源眼姐,構(gòu)造UrlResource诅迷;b.構(gòu)造ClassPathResource
FileSystemResourceLoader
修改了(2)b構(gòu)造FileSystemResource
ResourcePatternResolver
ResourceLoader的擴(kuò)展,添加了getResources
它的實(shí)現(xiàn)會(huì)委派一個(gè)ResouceLoader來(lái)加載資源
ApplicationContext
繼承了ResourcePatternResolver众旗,它的實(shí)現(xiàn)一般會(huì)繼承一個(gè)AbstractApplicationContext,這個(gè)類(lèi)會(huì)繼承DefaultReosurceLoader趟畏,而且有一個(gè)ResourcePatternResolver的對(duì)象贡歧,通過(guò)自己構(gòu)造。詳見(jiàn)Spring揭秘p91赋秀。