概述
BeanFatory
作为Spring的IOC容器主要提供了注册Bean和管理依赖这两个功能,它的接口包括如下函数:
而
ApplicationContext
在间接继承了BeanFactory
的基础上,还多继承了四个接口,提供了更多的功能,如事件发布,资源载入,消息等。BeanFactory and ApplicationContext
实现
BeanFactory
接口的实现为了实现注册Bean的功能,需要使用BeanDefinitionRegistry
这个接口,这三者的关系如图:
可以看到,
BeanFactory
的实现同时实现了这两个接口,而它的内部,正是使用BeanDefinition
来管理Bean:
DefaultListableBeanFactory.png
无论是通过代码还是配置文件或者注解,本质上都是通过
BeanDefinitionReader
先构造BeanDefinition
,然后将BeanDefinition
注册到BeanDefinitionRegistry
,通常也就是BeanFactory
的实现。
IoC容器的两个阶段
容器启动阶段
- 加载配置
- 分析配置信息
- 装配到BeanDefinition
- PostProcess
BeanFactoryPostProcessor是容器的扩展机制,可在实例化阶段之前对BeanDefinition中的信息进行修改
如PropertyPlaceHolderConfigurer,把它声明为Bean,ApplicationContext会注册并应用它,将bean配置中的占位符用属性文件中的值替换
Bean实例化阶段
- 实例化对象
- 装配依赖
- 生命周期回调
- 对象的其他处理
- 注册回调接口
- 实例化
BeanFactory根据BeanDefinition来进行实例化,使用策略模式有反射和cglib两种方式来实例化bean,返回一个包裹了bean的BeanWrapper,用于第二步设置属性 - 设置属性
BeanWrapperImpl同时也是继承了PropertyEditorRegistry,使用PropertyEditor来转换类型,设置属性 - 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;
}
常用场景:1.处理Aware接口 2.生成当前对象的代理(AOP)
ApplicationContextAwareProcessor处理ApplicationContext相关的各个Aware接口。
用BeanPostProcessor处理Marker接口时(如Aware接口),其实这个接口相当于为这个Bean添加了新的属性,接口一般包括的函数只有设置属性和获取属性,获取属性由Bean的Client调用,设置属性的函数则由BeanPostProcessor调用来进行设置,所以这种Marker接口一般是在BeanPostProcessor的Before函数中处理,紧邻着Bean实例化过程中的设置属性(因为这也是设置属性的一种,只不过是所有Bean统一设置)。
相对的,使用BeanPostProcessor来生成proxy时会在after函数中处理,这时候bean的初始化已经完成,可以被代理了。
-
InitializingBean和init-method
Bean的生命周期接口和生命周期方法 -
注册销毁回调
以上的设置都完成之后,如果Bean实现了DisposableBean接口或者声明了destroy-method属性,那么这时候容器会为这个Bean注册一个用于销毁的回调,当关闭容器的时候,主动调用一个函数,来执行bean的销毁工作。
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的自动识别
- bean实例的自动初始化
- 统一的资源加载策略
- 国际化的信息支持
- 容器内事件发布
资源加载
问题:
- java.net.URL局限于http,ftp,file等协议,而资源可以以二进制、字节流、文件等形式存在于文件系统、classpath、URL定位等地方
- java.net.URL对资源的查找和定位没有清晰的界限,返回的形式多种多样,没有统一的抽象
解决:
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:开头->ClassPathResource
(2) a.通过Url来定位资源,构造UrlResource;b.构造ClassPathResource
FileSystemResourceLoader
修改了(2)b构造FileSystemResource
ResourcePatternResolver
ResourceLoader的扩展,添加了getResources
它的实现会委派一个ResouceLoader来加载资源
ApplicationContext
继承了ResourcePatternResolver,它的实现一般会继承一个AbstractApplicationContext,这个类会继承DefaultReosurceLoader,而且有一个ResourcePatternResolver的对象,通过自己构造。详见Spring揭秘p91。
网友评论