Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。可以说Spring MVC是Spring系列框架中使用频率最高的部分,那springmvc怎么启动?下面来我们就来给大家讲解一下。
Spring MVC的开发过程为:
配置web.xml;
1. 配置容器配置位置:contextConfigLocation
2. 配置context启动后的监听器ContextLoaderListener
3. 配置前端控制器,即对某url访问的业务支持,DispatcherServlet
2. 创建spring的核心启动配置applicationContext.xml;
3. 业务逻辑处理model/view/controller;
4. 配置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():
1. 如果WebApplicationContext不存在,那么创建一个;
2. 如果WebApplicationContext是可配置的context(ConfigurableWebApplicationContext),那么完成配置更新(ContextLoader.configureAndRefreshWebApplicationContext)
创建WebApplicationContext逻辑
从ServletContext中判断WebApplicationContext类型,并使用BeanUtils工具类(底层使用反射)生成实例。
怎么确定使用的是那种类型的WebApplicationContext?
1. 通过ServletContext中的contextParam中,获取contextClass,此配置在web.xml中配置,如果实现时配置了,那么使用这个配置类;
2. 如果没有配置contextClass,那么使用springMVC的默认策略(defaultStrategies)选择contextClass。默认策略配置在ContextLoader.properties文件中(位于spring-web工程中的org.springframework.web.context包路径下,与ContextLoader类位于同一包中),默认的WebApplicationContext实现类为org.springframework.web.context.support.XmlWebApplicationContext。
配置WebApplicationContext逻辑
核心逻辑位于ContextLoader.configureAndRefreshWebApplicationContext,完成了:
1. 配置contextID,
2. 配置在web.xml中设置的contextConfigLocation
3. 初始化PropertySource
4. 记载和刷新配置refresh(),这是spring中配置生效的核心函数
配置生效的核心逻辑位于refresh(),由于XmlWebApplicationContext实现了AbstractApplicationContext,所以实际的逻辑位于AbstractApplicationContext.refresh()。
配置生效逻辑主要包括:
1. 初始化各种配置;
2. 获取ApplicationContext中的BeanFactory;
3. 配置BeanFactory,并在BeanFactory初始化后,在生成Bean前,做一些准备工作postProcessBeanFactory。
4. 向BeanFactory中注册BeanPostProcessor,即Bean的初始化完成后的处理器,进一步完善初始化。
5. 然后就开始各种Bean的实例化。
配置生效的核心逻辑中包括获取BeanFactory的逻辑(obtainFreshBeanFactory()),此逻辑是Bean生命周期管理的核心!
获取BeanFactory
获取BeanFactory的入口函数如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); }
其中getBeanFactory()仅仅是从属性中获取beanFactory,而refreshBeanFactory()完成了(AbstractRefreshableApplicationContext.refreshBeanFactory()由于XmlWebApplicationContext的继承关系推出来):
1. BeanFactory创建和自定义
2. 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); }
核心逻辑是:
1. 生成XmlBeanDefinitionReader,并配置属性文件和环境配置,其中initBeanDefinitionReader(beanDefinitionReader);是留给子类做定制化用的。
2. 使用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()); } }
作 为 Spring 框 架 一 部 分,SpringMVC 功能分工明确,容易理解,上手快,使用也非常简单,因此熟悉SpringMVC框架很有必要!最后大家如果想要了解更多java架构师知识,敬请关注奇Q工具网。
推荐阅读: