spring是java的开源框架。由于在前公司系统框架都是自己实现,所以之前未使用过spring,通过所在新公司的项目代码看了一些spring的用法,比较好奇内部实现。虽然在前公司也用过很多技术,但由于太忙,很多时候都是知其然不知其所以然,作为技术人员,不了解内部实现,那种感觉相当难受,很多时候用过相关技术之后除了知道几个API或者是一些网上到处可以搜到的部署流程,实际上两手空空毫无收获。
来到ThoughtWorks,终于有点自己的时间,于是乎自己动手调试和阅读源码,目的是了解一下IOC和AOP的一些内部机制,顺便学习一下spring经典框架的代码架构。阅读版本为4.1.5。
以前并没有特别多的阅读源码经验,所以不知道如何读起,通过断点单步调试发现效果并不是很好,原因是继承层次较多、接口遍地,在不了解继承层次的情况下,会觉得比较乱,于是想尝试先了解下整体的继承层次,应该对于理解整体架构会有帮助。长话短说,我们一起开始看代码。
看代码由入口开始看,spring所有的信息都保存在ApplicationContext中,下图以ClassPathXmlApplicationContext为例开始走读源码。
Screen Shot 2018-08-19 at 22.37.01.png
从最上层的接口开始看,ResourceLoader接口,这个接口是提供查找资源的接口,DefaultResourceLoader和ResourcePatternResolver继承了这个类:
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提供了具体实现,该类用于获取资源:
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
ResourcePatternResolver继承后仍然为接口,没有具体实现,该接口在父接口上添加了一个接口,支持返回多个资源列表:
public interface ResourcePatternResolver extends ResourceLoader {
/**
* Pseudo URL prefix for all matching resources from the class path: "classpath*:"
* This differs from ResourceLoader's classpath URL prefix in that it
* retrieves all matching resources for a given name (e.g. "/beans.xml"),
* for example in the root of all deployed JAR files.
* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
/**
* Resolve the given location pattern into Resource objects.
* <p>Overlapping resource entries that point to the same physical
* resource should be avoided, as far as possible. The result should
* have set semantics.
* @param locationPattern the location pattern to resolve
* @return the corresponding Resource objects
* @throws IOException in case of I/O errors
*/
Resource[] getResources(String locationPattern) throws IOException;
}
MessageSource看起来是一组用来解析字符串的接口,后面看到具体实现再说。
ApplicationEventPublisher,一个通知接口,用来通知注册的listeners发生的框架事件。
public interface ApplicationEventPublisher {
/**
* Notify all listeners registered with this application of an application
* event. Events may be framework events (such as RequestHandledEvent)
* or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
void publishEvent(ApplicationEvent event);
}
EnvironmentCapable,提供返回环境信息的接口:
public interface EnvironmentCapable {
/**
* Return the {@link Environment} associated with this component.
*/
Environment getEnvironment();
}
BeanFactory,顾名思义,就是获取bean的工厂。这个接口基本算是spring中的核心接口吧,因为spring核心的IOC和AOP功能可以说是构建在BeanFactory之上的:
public interface BeanFactory {
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* 返回对象的类实例。该实例可以是单例模式或者是原型模式。
单例模式意味着所有通过此工厂获取的引用都指向同一个实例;
而原型模式返回的是实例的克隆对象。
*/
Object getBean(String name) throws BeansException;
/**
* 与getBean(String name)方法的区别在于指定了返回对象的类型
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 不提供名称,只通过类型查询,然后返回相应类型的bean
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 获取bean时提供相应参数,该参数可以用与生成bean的工厂或者bean的构造函数
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* 获取bean时,提供参数和指定bean的返回类型
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* 确认BeanFactory中是否包含bean的定义或者是否存在已经注册的bean。
* 如果给定的name为别名,那么实现需要将该别名转换成bean的真实名称;
* 如果BeanFactory有Parents(HierarchicalBeanFactory),那么需要到Parents工厂那里查询bean实例是否已经注册
*(注意这里的Parents并非继承关系,而是类似java类加载中的双亲委派机制,先在下层中查找相应类,
* 查找不到再到上层查找,他们之间构建了一个层次关系,由最底层可以向上层找,上层为祖先)
* 这个函数返回true时,并不以为可以获取bean的实例,这个因为只要bean的定义存在或者对象已经注册就可以返回true,
* 然而bean有可能是抽象类。
*/
boolean containsBean(String name);
/**
* 是否为单例模式
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 是否未原型模式
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 给定的bean name是否匹配目标类型targetType
*/
boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
/**
* 返回指定name的bean的类型
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* 返回指定bean name的别名列表。如果参数name本身为别名,
那么将会返回bean name原始名称和其他别名的列表,原始名称保
存在返回数组的第一个元素中。
*/
String[] getAliases(String name);
}
HierarchicalBeanFactory,这个接口说明BeanFactory可以存在层次模型,可以获取ParentBeanFactory,刚才�BeanFactory中的大多数方法都会在自身查找失败的时候继续查找ParentBeanFactory,比如getType等。
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* Return the parent bean factory, or null if there is none.
*/
BeanFactory getParentBeanFactory();
/**
* 忽略双亲委派模型(查找不到也不到parent bean factory中去查找),只在本地BeanFactory中查找相关的bean
* @param name the name of the bean to query
* @return whether a bean with the given name is defined in the local factory
* @see BeanFactory#containsBean
*/
boolean containsLocalBean(String name);
}
接口ListableBeanFactory,继承自BeanFactory。ListableBeanFactory的实现类需要预先加载所有的bean定义信息(例如基于xml的BeanFactory),而不是根据客户端的请求再去发现相应的bean信息。该类只关注当前Factory中bean的定义,该类会忽略通过其他方式注册的bean(所以该类不会进行双亲委托的查询且只关注自身bean定义信息,忽略双亲委托和非该Factory注册的bean,)。感觉该类是为了封闭自己的bean定义信息,保持类的纯净,看到后面知道其实际用法后再补充。这个接口的方法很简单,都是获取bean信息或者bean本身的,一些方法的不同点在于是依赖类型获取还是依靠注释获取bean的信息。
public interface ListableBeanFactory extends BeanFactory {
/**
* 检查是否包含name的bean定义
* @param beanName the name of the bean to look for
* @return if this bean factory contains a bean definition with the given name
* @see #containsBean
*/
boolean containsBeanDefinition(String beanName);
/**
* 返回bean的数量
* @return the number of beans defined in the factory
*/
int getBeanDefinitionCount();
/**
* Return the names of all beans defined in this factory.
* <p>Does not consider any hierarchy this factory may participate in,
* and ignores any singleton beans that have been registered by
* other means than bean definitions.
* @return the names of all beans defined in this factory,
* or an empty array if none defined
*/
String[] getBeanDefinitionNames();
/**
* Return the names of beans matching the given type (including subclasses),
* judging from either bean definitions or the value of {@code getObjectType}
* in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of {@code getBeanNamesForType} matches all kinds of beans,
* be it singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeanNamesForType(type, true, true)}.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all bean names
* @return the names of beans (or objects created by FactoryBeans) matching
* the given object type (including subclasses), or an empty array if none
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class)
*/
String[] getBeanNamesForType(Class<?> type);
/**
* Return the names of beans matching the given type (including subclasses),
* judging from either bean definitions or the value of {@code getObjectType}
* in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all bean names
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the names of beans (or objects created by FactoryBeans) matching
* the given object type (including subclasses), or an empty array if none
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
/**
* Return the bean instances that match the given object type (including
* subclasses), judging from either bean definitions or the value of
* {@code getObjectType} in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of getBeansOfType matches all kinds of beans, be it
* singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeansOfType(type, true, true)}.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all concrete beans
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
* @since 1.1.2
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
*/
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
/**
* Return the bean instances that match the given object type (including
* subclasses), judging from either bean definitions or the value of
* {@code getObjectType} in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all concrete beans
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
<T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
/**
* Find all names of beans whose {@code Class} has the supplied {@link Annotation}
* type, without creating any bean instances yet.
* @param annotationType the type of annotation to look for
* @return the names of all matching beans
* @since 4.0
*/
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
/**
* Find all beans whose {@code Class} has the supplied {@link Annotation} type,
* returning a Map of bean names with corresponding bean instances.
* @param annotationType the type of annotation to look for
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
* @since 3.0
*/
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
/**
* Find an {@link Annotation} of {@code annotationType} on the specified
* bean, traversing its interfaces and super classes if no annotation can be
* found on the given class itself.
* @param beanName the name of the bean to look for annotations on
* @param annotationType the annotation class to look for
* @return the annotation of the given type if found, or {@code null}
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 3.0
*/
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException;
}
ApplicationContext,这个接口继承了多个接口,这些接口在前面已经提到,所以ApplicationContext具有之前所提到接口的所有能力:获取指定资源的信息、获取bean和bean定义、获取ParentBeanFactory等。另外,它也新增了一些接口方法。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
/**
* Return the unique id of this application context.
* @return the unique id of the context, or {@code null} if none
*/
String getId();
/**
* Return a name for the deployed application that this context belongs to.
* @return a name for the deployed application, or the empty String by default
*/
String getApplicationName();
/**
* Return a friendly name for this context.
* @return a display name for this context (never {@code null})
*/
String getDisplayName();
/**
* Return the timestamp when this context was first loaded.
* @return the timestamp (ms) when this context was first loaded
*/
long getStartupDate();
/**
* Return the parent context, or {@code null} if there is no parent
* and this is the root of the context hierarchy.
* @return the parent context, or {@code null} 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 of the application context,
* applying the Spring bean lifecycle (fully or partly) to them.
* <p>Alternatively, the internal BeanFactory exposed by the
* {@link ConfigurableApplicationContext} interface offers access to the
* {@link AutowireCapableBeanFactory} interface too. The present method mainly
* serves as a convenient, specific facility on the ApplicationContext interface.
* <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
* after the application context has been closed.</b> In current Spring Framework
* versions, only refreshable application contexts behave that way; as of 4.2,
* all application context implementations will be required to comply.
* @return the AutowireCapableBeanFactory for this context
* @throws IllegalStateException if the context does not support the
* {@link AutowireCapableBeanFactory} interface, or does not hold an
* autowire-capable bean factory yet (e.g. if {@code refresh()} has
* never been called), or if the context has been closed already
* @see ConfigurableApplicationContext#refresh()
* @see ConfigurableApplicationContext#getBeanFactory()
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
ApplicationContext最后的接口方法是getAutowireCapableBeanFactory,返回了一个AutowireCapableBeanFactory类型的对象,该对象同样继承自接口BeanFactory。该BeanFactory对bean有更多的操作。
Lifecycle接口,拥有start,stop,isRunning三个接口方法,如下:
public interface Lifecycle {
/**
* Start this component.
* Should not throw an exception if the component is already running.
* <p>In the case of a container, this will propagate the start signal
* to all components that apply.
*/
void start();
/**
* Stop this component, typically in a synchronous fashion, such that
* the component is fully stopped upon return of this method. Consider
* implementing {@link SmartLifecycle} and its {@code stop(Runnable)}
* variant in cases where asynchronous stop behavior is necessary.
* <p>Should not throw an exception if the component isn't started yet.
* <p>In the case of a container, this will propagate the stop signal
* to all components that apply.
* @see SmartLifecycle#stop(Runnable)
*/
void stop();
/**
* Check whether this component is currently running.
* <p>In the case of a container, this will return {@code true}
* only if <i>all</i> components that apply are currently running.
* @return whether the component is currently running
*/
boolean isRunning();
}
ConfigurableApplicationContext接口,继承自 ApplicationContext, Lifecycle, Closeable,他拥有这三个接口所有的行为,支持启动、停止、关闭、运行期间状态查询以及ApplicationContext的行为。setId等方法很简单,一看便知。这块只记录一些重要方法。
void refresh()方法,用于加载或者刷新配置(xml文件、配置文件、关系数据库schema),这是初始化所有bean时的方法,所以要么全部bean初始化,要么全部初始化失败,不能存在部分bean初始化的情况,假设某些bean初始化失败,那么需要对已初始化的bean进行清理,保证没有悬挂的对象存在。
void setParent(ApplicationContext parent)方法,这个方法的存在说明ConfigurableApplicationContext也存在层次关系,可以拥有parent ApplicationContext。
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor)方法,顾名思义,BeanFactory的后处理器,该方法用来注册这些处理,虽然现在还未看到具体实现类,但是可以想象,这些处理器肯定具有改变BeanFactory功能的能力,值得期待。这些后处理器会在ApplicationContext配置化期间被调用。
void addApplicationListener(ApplicationListener<?> listener),添加listener,当上下文做某些动作时,通知事件发出后,这些注册的listener可以收到相应的事件。
void registerShutdownHook(),在JVM上注册一个关闭上下文的钩子,这样应该可以确保JVM关闭时,上下文可以正确被关闭,如果上下文已经关闭,那么JVM什么都不做。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
/**
* Any number of these characters are considered delimiters between
* multiple context config paths in a single String value.
*/
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
* Name of the ConversionService bean in the factory.
* If none is supplied, default conversion rules apply.
* @see org.springframework.core.convert.ConversionService
*/
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
/**
* Name of the LoadTimeWeaver bean in the factory.
* @see org.springframework.instrument.classloading.LoadTimeWeaver
*/
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
/**
* Name of the {@link Environment} bean in the factory.
*/
String ENVIRONMENT_BEAN_NAME = "environment";
/**
* Name of the System properties bean in the factory.
* @see java.lang.System#getProperties()
*/
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
/**
* Name of the System environment bean in the factory.
* @see java.lang.System#getenv()
*/
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
/**
* Set the unique id of this application context.
*/
void setId(String id);
/**
* Set the parent of this application context.
* <p>Note that the parent shouldn't be changed: It should only be set outside
* a constructor if it isn't available when an object of this class is created,
* for example in case of WebApplicationContext setup.
* @param parent the parent context
* @see org.springframework.web.context.ConfigurableWebApplicationContext
*/
void setParent(ApplicationContext parent);
/**
* Return the Environment for this application context in configurable form.
*/
@Override
ConfigurableEnvironment getEnvironment();
/**
* Set the {@code Environment} for this application context.
* @param environment the new environment
*/
void setEnvironment(ConfigurableEnvironment environment);
/**
* Add a new BeanFactoryPostProcessor that will get applied to the internal
* bean factory of this application context on refresh, before any of the
* bean definitions get evaluated. To be invoked during context configuration.
* @param beanFactoryPostProcessor the factory processor to register
*/
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
/**
* Add a new ApplicationListener that will be notified on context events
* such as context refresh and context shutdown.
* <p>Note that any ApplicationListener registered here will be applied
* on refresh if the context is not active yet, or on the fly with the
* current event multicaster in case of a context that is already active.
* @param listener the ApplicationListener to register
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.context.event.ContextClosedEvent
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* Load or refresh the persistent representation of the configuration,
* which might an XML file, properties file, or relational database schema.
* <p>As this is a startup method, it should destroy already created singletons
* if it fails, to avoid dangling resources. In other words, after invocation
* of that method, either all or no singletons at all should be instantiated.
* @throws BeansException if the bean factory could not be initialized
* @throws IllegalStateException if already initialized and multiple refresh
* attempts are not supported
*/
void refresh() throws BeansException, IllegalStateException;
/**
* 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();
/**
* Close this application context, releasing all resources and locks that the
* implementation might hold. This includes destroying all cached singleton beans.
* <p>Note: Does <i>not</i> invoke {@code close} on a parent context;
* parent contexts have their own, independent lifecycle.
* <p>This method can be called multiple times without side effects: Subsequent
* {@code close} calls on an already closed context will be ignored.
*/
@Override
void close();
/**
* Determine whether this application context is active, that is,
* whether it has been refreshed at least once and has not been closed yet.
* @return whether the context is still active
* @see #refresh()
* @see #close()
* @see #getBeanFactory()
*/
boolean isActive();
/**
* Return the internal bean factory of this application context.
* Can be used to access specific functionality of the underlying factory.
* <p>Note: Do not use this to post-process the bean factory; singletons
* will already have been instantiated before. Use a BeanFactoryPostProcessor
* to intercept the BeanFactory setup process before beans get touched.
* <p>Generally, this internal factory will only be accessible while the context
* is active, that is, inbetween {@link #refresh()} and {@link #close()}.
* The {@link #isActive()} flag can be used to check whether the context
* is in an appropriate state.
* @return the underlying bean factory
* @throws IllegalStateException if the context does not hold an internal
* bean factory (usually if {@link #refresh()} hasn't been called yet or
* if {@link #close()} has already been called)
* @see #isActive()
* @see #refresh()
* @see #close()
* @see #addBeanFactoryPostProcessor
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
AbstractApplicationContext,看了这个抽象类,仿佛终于看到了一丝胜利的曙光了。
第一,这个类是抽象类,说明离真正的实现不远了;
第二,这个类基本上实现了多数的接口,一定程度说明这个类就是spring context的核心类,提前浏览了下它后面的几个子类,里面只是一些抽象方法的子类实现,所以,初步判断掌握此类就可以掌握spring context初始化过程。
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean
AbstractApplicationContext是个抽象类,继承自DefaultResourceLoader,ConfigurableApplicationContext,DisposableBean,不得不佩服java不支持多继承的缺陷,逼迫开发人员使用DefaultResourceLoader作为直接父类也是没谁了,首先说明AbstractApplicationContext是一个资源类;同时,继承了两个接口ConfigurableApplicationContext, DisposableBean,DisposableBean中只有一个destroy方法,但是想起来之前继承过Closeable接口,里面是个close方法,很奇怪destroy方法和closeable方法之间有啥区别?查看AbstractApplicationContext中的实现后发现destroy方法中啥也没干,直接调用了close方法。。。ConfigurableApplicationContext接口前面已经提过。
@Override
public void destroy() {
close();
}
/**
* Close this application context, destroying all beans in its bean factory.
* <p>Delegates to {@code doClose()} for the actual closing procedure.
* Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
* @see #doClose()
* @see #registerShutdownHook()
*/
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
我们再一起看下AbstractApplicationContext中的实现,从之前的继承层次来看,AbstractApplicationContext继承了LifecycleProcessor,ResourcePatternResolver,MessageSource,在AbstractApplicationContext定义了三个对象:
/** ResourcePatternResolver used by this context */
private ResourcePatternResolver resourcePatternResolver;
/** LifecycleProcessor for managing the lifecycle of beans within this context */
private LifecycleProcessor lifecycleProcessor;
/** MessageSource we delegate our implementation of this interface to */
private MessageSource messageSource;
很显然,AbstractApplicationContext没有自己去直接实现相关接口,而是委托给了这几个接口的实现类来进行具体的操作(其实整个AbstractApplicationContext都是这样设计思路,后面会看其他接口的实现策略),这样设计可以避免AbstractApplicationContext太过臃肿,同时这几个接口的实现类还可以被多个类复用,这也算是组合优于继承的典型应用吧。既然有这三个实现类,那么必定会有他们的初始化过程。
下面是initMessageSource的初始化过程。可以看到会先获取一个BeanFactory,判断该BeanFactory中是否包含该bean,如果包含该bean,则从BeanFactory中取出该实例;否则使用默认的DelegatingMessageSource作为具体实现,再将该实例注册到BeanFactory中。同时,从第一个if分支可以看出,HierarchicalMessageSource也存在层次。
/**
* Initialize the MessageSource.
* Use parent's if none defined in this context.
*/
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}
initLifecycleProcessor的初始化过程。跟initMessageSource类似,beanFactory中可以查找到该bean则从beanFactory中取,否则使用默认的DefaultLifecycleProcessor。
/**
* Initialize the LifecycleProcessor.
* Uses DefaultLifecycleProcessor if none defined in the context.
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
}
else {
DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
defaultProcessor.setBeanFactory(beanFactory);
this.lifecycleProcessor = defaultProcessor;
beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LifecycleProcessor with name '" +
LIFECYCLE_PROCESSOR_BEAN_NAME +
"': using default [" + this.lifecycleProcessor + "]");
}
}
}
resourcePatternResolver在构造函数中初始化,直接使用getResourcePatternResolver方法返回一个默认的PathMatchingResourcePatternResolver对象
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
ApplicationContext继承的接口中支持发布事件,通知具体的监听器做出相应的动作。ApplicationEventMulticaster负责监听器的注册以及通知工作,具体为负责发布事件到监听器的是publishEvent方法。
@Override
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}
initApplicationEventMulticaster对负责事件广播的ApplicationEventMulticaste对象进行初始化。仍然是从BeanFactory优先获取,其他使用默认的SimpleApplicationEventMulticaster进行事件广播。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
LifeCycle接口的具体实现LifecycleProcessor。start之后,通过广播发布上下文启动事件ContextStartedEvent,触发相应监听器做出相应动作;stop之后也是类似。isRunning仅返回processring的状态。
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
@Override
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
@Override
public boolean isRunning() {
return getLifecycleProcessor().isRunning();
}
小知识点来了,JVM在关闭之前会调用注册的hook,注册方法为Runtime.getRuntime().addShutdownHook(JVM自身提供),这个hook(实际为一个线程)可以保证JVM关闭之前被调用。这个方法在我们实现其他组件时也非常好用。
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
@Override
public void run() {
doClose();
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
getBeanFactory是该抽象类中的关键方法,因为该BeanFactory代理了AbstractApplicationContext中所有涉及bean操作的方法,该方法需要在子类中进行实现。
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
最重要的方法登场:public void refresh(),该方法负责所有bean信息的加载,bean的初始化等工作,名副其实的大佬。由于其涉及的工作比较多,留到后面分析。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
到这里,整体AbtractApplicationContext所涉及的工作就比较清楚了,它包括了上下文的启动停止,bean的加载、创建、查询,监听器的注册,广播事件的发布等。当子类继承了该抽象类,实现了具体抽象类后,我们便可以使用spring上下文做真正的工作了。
AbstractRefreshableApplicationContext继承自AbstractApplicationContext。实现的方法主要是对BeanFactory的操作(创建、刷新、销毁等)。
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext
AbstractRefreshableApplicationContext通过实现refreshBeanFactory()方法,支持多次调用重建BeanFactory,如下:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
createBeanFactory方法负责创建BeanFactory,类型为DefaultListableBeanFactory。
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
新增抽象方法loadBeanDefinitions,由子类负责实现。
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
AbstractRefreshableConfigApplicationContext是AbstractRefreshableApplicationContext的子类,仍然为抽象类,继承了两个接口BeanNameAware, InitializingBean,InitializingBean只有一个方法:void afterPropertiesSet(),此方法会在某些属性全部设置后触发,偷看了下子类实现,该实现调用了下refresh这个关键方法。BeanNameAware接口也只有一个方法,setBeanName(String),看了下方法实现,实际上就是设置ApplicationContext的名称。AbstractRefreshableConfigApplicationContext抽象类中主要增加了configLocations属性,并支持该属性的设置和获取,该抽象类是xml上下文的基类。
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
private String[] configLocations;
...
}
到这里,已经快抵达终点了,加把劲,继续分析。抽象类AbstractXmlApplicationContext直接继承自父类AbstractRefreshableConfigApplicationContext。
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext
这个类已经非常清楚了,因为从名字上就可以看出来它到底负责什么功能,它主要方法都负责架加载bean的信息,通过XmlBeanDefinitionReader读取xml配置信息后对beanFactory进行填充:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
ClassPathXmlApplicationContext,用户使用的实现类,支持解析xml配置的spring上下文,继承自AbstractXmlApplicationContext。该类比较简单,使用时只需要把xml配置名称传入即可。整个类基本是各种构造函数,没有什么要说的。
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext
至此,ClassPathXmlApplicationContext整体层次结构已经比较清晰。最后再总结下,ClassPathXmlApplicationContext:它是一个BeanFactory,所以它具有所有获取bean状态,获取bean定义,获取bean对象的一系列方法;它是一个资源加载器,它可以查找相关资源文件,加载相应的xml配置为bean定义信息;它具有生命周期,可以启动、停止、获取其运行状态;它具有层次,可以获取自己的parent;它具有一些工具类(MessageSource,LifeCycle等),虽然它本身继承了相关接口,但是它自己并不实现这些接口,而是通过组合方式直接使用相关代理类执行继承的接口操作,增强了程序的扩展性,也让这些代理类在其他地方可以复用,减少了需要实现的接口方法。本文主要剖析了ClassPathXmlApplicationContext的整体继承模型以及列出了本人觉得可能是重点功能的函数方法。类似refresh这种重点函数并没有阅读,是因为这些内部涉及更多的类之间的组合关系,工作量较大,留待后续学习。
网友评论