典型的Spring MVC的开发过程为:
- 配置
web.xml
;- 配置容器配置位置:contextConfigLocation
- 配置context启动后的监听器ContextLoaderListener
- 配置前端控制器,即对某url访问的业务支持,DispatcherServlet
- 创建spring的核心启动配置
applicationContext.xml
; - 业务逻辑处理model/view/controller;
- 配置bean
WebApplicationContext 启动原理
tomcat context启动后,会自动触发ContextLoaderListener
,然后加载WebApplicationContext
。为什么?
ContextLoaderListener
继承自ServletContextListener
:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
ServletContextListener
完成对ServletContext状态的监听,响应servletContext的initialized
和destroy
事件。
public interface ServletContextListener extends EventListener {
default void contextInitialized(ServletContextEvent sce) {
}
default void contextDestroyed(ServletContextEvent sce) {
}
}
ContextLoaderListener
实现了监听ServletContext的方法,从而能获取到ServletContext的启动时间通知,被触发。
ContextLoaderListener
的实现逻辑:
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
可知WebApplicationContext的初始化逻辑位于ContextLoader.initWebApplicationContext()
:
- 如果
WebApplicationContext
不存在,那么创建一个; - 如果
WebApplicationContext
是可配置的context(ConfigurableWebApplicationContext
),那么完成配置更新(ContextLoader.configureAndRefreshWebApplicationContext
)
创建WebApplicationContext逻辑
从ServletContext中判断WebApplicationContext类型,并使用BeanUtils工具类(底层使用反射
)生成实例。
怎么确定使用的是那种类型的WebApplicationContext
?
- 通过ServletContext中的contextParam中,获取
contextClass
,此配置在web.xml
中配置,如果实现时配置了,那么使用这个配置类; - 如果没有配置
contextClass
,那么使用springMVC的默认策略(defaultStrategies
)选择contextClass。默认策略配置在ContextLoader.properties
文件中(位于spring-web工程中的org.springframework.web.context
包路径下,与ContextLoader
类位于同一包中),默认的WebApplicationContext
实现类为org.springframework.web.context.support.XmlWebApplicationContext
。
配置WebApplicationContext逻辑
核心逻辑位于ContextLoader.configureAndRefreshWebApplicationContext
,完成了:
- 配置contextID,
- 配置在web.xml中设置的
contextConfigLocation
- 初始化PropertySource
- 记载和刷新配置
refresh()
,这是spring中配置生效的核心函数
配置生效的核心逻辑位于refresh()
,由于XmlWebApplicationContext
实现了AbstractApplicationContext
,所以实际的逻辑位于AbstractApplicationContext.refresh()
。
配置生效逻辑主要包括:
- 初始化各种配置;
- 获取ApplicationContext中的BeanFactory;
- 配置BeanFactory,并在BeanFactory初始化后,在生成Bean前,做一些准备工作
postProcessBeanFactory
。 - 向BeanFactory中注册BeanPostProcessor,即Bean的初始化完成后的处理器,进一步完善初始化。
- 然后就开始各种Bean的实例化。
配置生效的核心逻辑中包括获取BeanFactory的逻辑(obtainFreshBeanFactory()
),此逻辑是Bean生命周期管理的核心!
获取BeanFactory
获取BeanFactory
的入口函数如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
其中getBeanFactory()
仅仅是从属性中获取beanFactory,而refreshBeanFactory()
完成了(AbstractRefreshableApplicationContext.refreshBeanFactory()
由于XmlWebApplicationContext的继承关系推出来):
- BeanFactory创建和自定义
BeanDefinition
的解析和加载(这块和Bean的实例化紧密关联)
@Override
protected final void refreshBeanFactory() throws BeansException {
//...省略前面的判断
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
//省略后面的设置
}
BeanFactory创建
为ApplicationContext创建一个BeanFactory(BeanFactory是ApplicationContext的属性),默认的BeanFactory类型是DefaultListableBeanFactory
。
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
BeanDefinition的解析和加载
XmlWebApplicationContext重写了loadBeanDefinitions()
方法加载BeanDefinition。
@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(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);
}
核心逻辑是:
- 生成
XmlBeanDefinitionReader
,并配置属性文件和环境配置,其中initBeanDefinitionReader(beanDefinitionReader);
是留给子类做定制化用的。 - 使用
XmlBeanDefinitionReader
来解析BeanDefinition
那么怎么使用XmlBeanDefinitionReader
解析出BeanDefinition的?
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
配置文件是什么?
获取WebApplicationContext配置文件,此配置文件为前面初始化和配置WebApplicationContext
时设置的配置文件,即configureAndRefreshWebApplicationContext()
时设置的:
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
那么根源就是web.xml
中配置的contextConfigLocation
对应的配置文件,为XML格式。
怎么加载为BeanDefinition的?
下面的函数只取出了核心部分:
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
Resource[] resources = resourceLoader.getResources(location);
int count = loadBeanDefinitions(resources);
return count;
}
通过分析,可知,resourceLoader可以帮助将配置文件的文件名,转为Spring通用的Resource抽象。
其中XML文件的BeanDefinitionReader的类型为:XmlBeanDefinitionReader
,重写了loadBeanDefinitions
方法。
它使用EncodedResource
解决xml文件的编码问题,然后读入文件,在doLoadBeanDefinitions(inputSource, encodedResource.getResource())
中完成BeanDefinition的加载。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
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();
}
}
doLoadBeanDefinitions主要完成了将XML文件专为Docuemnt类型,然后注册读出的BeanDefinition:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
registerBeanDefinitions
实现如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
preProcessXml(root);
//实际解析XML Element生成BeanDefinition的位置
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
}
可知从Document中读取Xml的rootElement,然后parseBeanDefinitions
。
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
循环遍历XML,获取XML中的元素,并解析Bean 定义。
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)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
获取Element并解析封装成BeanDefinitionHolder,并注册到ReaderContext
:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
}
总结
默认从XML配置中读入Spring BeanDefinition,读取解析的过程如下:
- 在WebApplicationContext refresh()时,初始化WebApplicationContext内置的BeanFactory;
- 创建DefaultListableBeanFactory类型的BeanFactory;
- 使用XmlBeanDefinitionReader读取Xml Resource,内部对XML文件解析,针对每个ELement,解析为BeanDefinition,然后封装成BeanDefinitionHolder;
- 将BeanDefinition注册到BeanFactory上;
--Posted from Rpc