springmvc流程分析,读取web.xml配置被扫描bean

本文详细解析了SpringMVC在Tomcat启动时的初始化过程,包括DispatcherServlet的构造与初始化,以及通过web.xml配置参数创建并刷新WebApplicationContext。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet-->GenericServlet
父级递进,前三个是springmvc,后两个是标准sevlet-api
其实只有一个DispatcherServlet,但是有的方法是继承的,需要到上级去看具体实现,但是具体实现调用的方法,又可能是子类的方法,所以调用的方法,基本是从子类向上找,继承方法,或覆盖重写方法,使用idea的structure可以看到类的方法列表,上面按钮可以显示所有继承的方法,很方便

以下都是tomcat启动时springmvc初始化过程,方便查看执行流程

tomcat启动

根据web.xml, tomcat的org.apache.catalina.loader.ParallelWebappClassLoader加载jar包与类,以后类都是由这个加载器加载

org.springframework.web.servlet.DispatcherServlet 无参构造,引发父类构造FrameworkServlet,HttpServletBean,就是实例化对象,没有特别处理

开始标准servlet生命周期(Servlet.init(ServletConfig)),具体是GenericServlet.initinit(ServletConfig),

调用HttpServletBean.init(),先是设置web.xml中的initparam,然后是FrameworkServlet.initServletBean();

this.webApplicationContext = initWebApplicationContext();开始初始化applicationContext

wac = createWebApplicationContext(rootContext);开始创建,默认是XmlWebApplicationContext

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { 继续创建

configureAndRefreshWebApplicationContext(wac);开始读取xml配置刷新refresh()

wac.refresh();刷新是容器建立的开始,AbstractApplicationContext.refresh(),XmlWebApplicationContext的上级类

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();刷新bean工程就开始读取xml配置bean

refreshBeanFactory();

AbstractRefreshableApplicationContext.refreshBeanFactory()

loadBeanDefinitions(beanFactory);加载bean

AbstractXmlApplicationContext.loadBeanDefinitions(beanFactory)读取xmlbean

loadBeanDefinitions(beanDefinitionReader);

reader.loadBeanDefinitions(configResources);读取

AbstractBeanDefinitionReader.loadBeanDefinitions(Resource... resources)

loadBeanDefinitions(resource);

XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)

return doLoadBeanDefinitions(inputSource, encodedResource.getResource());进入这个方法

return registerBeanDefinitions(doc, resource);//进入

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//xml读取并扫描bean  

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

doRegisterBeanDefinitions(root);

parseBeanDefinitions(root, this.delegate);

delegate.parseCustomElement(ele);

eanDefinitionParserDelegate.parseCustomElement(Element ele, BeanDefinition containingBd)


return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

return findParserForElement(element, parserContext).parse(element, parserContext);//下面是这个的前部findParserForElement(element, parserContext)

NamespaceHandlerSupport.findParserForElement(Element element, ParserContext parserContext)

BeanDefinitionParser parser = this.parsers.get(localName);//这是上面方法内,源码中上一行就是取到xml的元素,比如component-scan,进入get方法

ComponentScanBeanDefinitionParser.parse(Element element, ParserContext parserContext)//这是上面的后部.parse(element, parserContext)

Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);//扫描配置的ase-package

ClassPathBeanDefinitionScanner.doScan(String... basePackages) 扫描

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//就是找到有注解的类的class

ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage) 遍历查找被注解的类的class,Candidate是待选的意思
首先会生成一个匹配式,比如你web.xml配置的base-package是com.a 生成的匹配式就是classpath*:com/a/**/*.class, 主要是因为他会调用一些工具类

Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);这是接下来的一句,resourcePatternResolver就是XmlWebApplicationContext对象,XmlWebApplicationContext.getResources(String)就是AbstractApplicationContext.getResources

return this.resourcePatternResolver.getResources(locationPattern);resourcePatternResolver是PathMatchingResourcePatternResolver.getResources(String locationPattern),locationPattern就是生成的匹配式classpath*:com/a/**/*.class

return findPathMatchingResources(locationPattern);

Resource[] rootDirResources = getResources(rootDirPath);rootDirPath被处理为classpath*:com/a/

继续PathMatchingResourcePatternResolver.getResources(String locationPattern)

return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));

Set<Resource> result = doFindAllClassPathResources(path);doFindAllClassPathResources里是调用classloader.getResources(),会遍历所有classpath去查找合适的,可以自己试试


,classloader就是ParallelWebappClassLoader,是调用XmlWebApplicationContext.getClassLoader,具体是上级DefaultResourceLoader初始化无参构造时,设置的,使用工具类
public DefaultResourceLoader() {
        this.classLoader = ClassUtils.getDefaultClassLoader();
    }
ClassUtils.getDefaultClassLoader

public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        }
        catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = ClassUtils.class.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
        return cl;
    }
就是先从ThreadLocal获取,然后是ClassUtils类自己被哪个加载器加载,还没有就只能使用bootstrap系统加载器
### Spring MVC 应用中热更新或重新加载 web.xml 配置 在传统的 Java Web 应用程序中,`web.xml` 是部署描述符文件,在应用程序启动时由容器读取并解析。对于 `web.xml` 的更改通常需要重启整个应用服务器才能生效。 然而,在现代开发环境中,频繁修改配置而不希望每次都重启服务的需求日益增加。针对这一需求,有几种方法可以实现对 `web.xml` 或其等效功能的动态重载: #### 使用 JRebel 工具 JRebel 提供了一种无需重启即可检测到类、资源以及某些情况下配置文件变化的能力。安装该插件后,当开发者改变任何受支持类型的文件时,它能够自动同步这些改动至正在运行的应用实例上[^1]。 #### 利用 Servlet 3.0 特性与编程式注册组件替代静态 XML 定义 随着Servlet API的发展到了版本3.0及以上,引入了通过Java代码来定义和初始化Web应用程序的方式,这使得我们可以完全摆脱对传统`web.xml`依赖的可能性。具体来说就是可以通过继承`AbstractAnnotationConfigDispatcherServletInitializer`来自定义项目的入口点设置,从而达到不使用甚至移除物理上的`web.xml`的目的。这样做的好处之一是可以更容易地管理配置项,并且允许更灵活地处理它们——比如基于环境变量或其他外部条件决定哪些bean应该被创建出来。 ```java public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{ RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{ WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[]{ "/" }; } } ``` 在这种模式下,“刷新”配置实际上变成了调整相应的Java Config类的内容;由于IDE提供了良好的重构工具链支持,因此相比于编辑XML要更加高效便捷。而且一旦实现了这种无xml方式构建Spring上下文结构之后,就可以考虑采用诸如Spring Cloud Config Server之类的解决方案来进行集中化管理和实时推送新的设定给各个微服务节点。 需要注意的是,直接意义上的“重新加载web.xml”的操作并不常见也不推荐,因为这意味着破坏了原有的一次性声明原则(即所有的初始状态都应该是在第一次部署期间建立好的)。更好的实践是利用上述提到的技术手段逐步过渡到更为现代化的服务治理框架内[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值