springmvc怎么启动?springmvc启动技巧

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工具网。

推荐阅读:

如何查看json文件?怎么创建json文件?

java如何写入excel?java写excel文件心得

程序员面试自我介绍怎么说?如何做好自我介绍?