SpringMVC是当下使用量比较大的web端框架,为了增加自己对SpringMVC的深入理解,故浅度剖析框架源码。如博客中存在任何问题,欢迎您指正。
一 ,概述
DispatcherServlet是SpringMVC框架的入口,而servlet有其自己的生命周期。初始化对应的方法是init,因此我们从该方法入手进行SpringMVC初始化的源码阅读。
二 ,核心类继承关系图
DispatcherServlet的类继承结构图如上所示,关于servlet的核心方法:init/service/destory 可以直接在父类中找到。而关于Aware接口的部分,主要是抽象设置ApplicationContext和Environment的方法,可以在如上的FrameworkServlet和HttpServletBean中看到具体实现。
三 ,源码分析
说明:因为框架中有很多地方的代码就自己目前的水平没有办法持续深究,所以这边展示的是对主要方法的理解,当然为了节约篇幅也会省略其他的无关代码。本篇博客主要描述SpringMVC加载bean的过程,代码跳转的过程可以通过黑体标注查看。如有问题,还望指正。
org.springframework.web.servlet.HttpServletBean--> init()
@Override
public final void init() throws ServletException {
......
// Set bean properties from init parameters.Get Config location and other param.
...
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
...
//The place of main load xml bean and init servlet opration.
initServletBean();
......
}
org.springframework.web.servlet.FrameworkServlet--> initServletBean()
@Override
protected final void initServletBean() throws ServletException {
......
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
......
}
org.springframework.web.servlet.FrameworkServlet--> initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
//create WebApplicationContext object.
......
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
//Config and Refresh object of creator,including spring bean load.
configureAndRefreshWebApplicationContext(cwac);
}
}
}
......
return wac;
}
下一篇博客会看一下这个Listener所带来的初始化作用,本次着重spring bean的初始化
org.springframework.web.servlet.FrameworkServlet--> configureAndRefreshWebApplicationContext()
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
......
//Add a listener for context refresh,it will invoke method named onRefresh(),which define in current class.
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
......
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//This method can show us how to load object which managed by spring.
wac.refresh();
}
org.springframework.context.support.AbstractApplicationContext--> refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// Real Bean Load Process is in following method.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
......
}
}
org.springframework.context.support.AbstractApplicationContext--> obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
......
return beanFactory;
}
org.springframework.context.support.AbstractRefreshableApplicationContext--> refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
......
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//Through bean factory to load bean definiton,So we can check bean factory filed like following.
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
......
}
org.springframework.context.support.AbstractXmlApplicationContext--> loadBeanDefinitions(beanFactory)
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
......
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
org.springframework.context.support.AbstractXmlApplicationContext--> loadBeanDefinitions(reader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
......
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader--> loadBeanDefinitions()
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
//keep skip to subclass.
counter += loadBeanDefinitions(resource);
}
return counter;
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> loadBeanDefinitions()
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//Load bean from xmll file which config in web.xml file.
return loadBeanDefinitions(new EncodedResource(resource));
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> loadBeanDefinitions(encodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
......
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
......
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
......
//Use w3c document object to parse xml file
Document doc = doLoadDocument(inputSource, resource);
//bean load from document object
return registerBeanDefinitions(doc, resource);
......
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> doLoadDocument()
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//Parse different xml format file by different valifation mode.
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) { //Inner method of load Doument.
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//This method will show us how to get validation mode of xml,including dtd/xsd,main method is through DOCTYPE,If you want //know ,you can get it by your own skip method.
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
//start bean load.......
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
......Skip to final main method......
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader -->parseBeanDefinitions()
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//loop the xml object from root to children,and parse element.
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);//this is customer element parse,it will be seen next time.
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader--> parseDefaultElement()
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//There has four different element parse,So we can see all bean load logic in here.
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);
}
}
未完待续......
四 ,疑惑点
1. Spring在解析XML文件之前会进行验证XML的格式是不是正确,代码是在getEntityResolver()中获取不同的EntityResolver 如:DelegatingEntityResolver中的getEntityResolver方法中,但是没有理清楚这个入口,后面会继续学习解释。
2.关于Spring对象的高级解析细节,如对象依赖,自定义标签等的深入理解,有待学习。