spring注册DispatcherServlet的过程

embedded/2024/11/15 5:39:31/

先看下servlet注入到tomcat服务器的过程,
DispatcherServletAutoConfiguration$DispatcherServletConfiguration类中声明了Bean方法,会创建DispatcherServlet类型的Bean,然后作为dispatcherServletRegistration方法的参数注入到DispatcherServletRegistrationBean内部。

java">		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());return dispatcherServlet;}@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);return registration;}

创建web服务器时this.webServer = factory.getWebServer(getSelfInitializer());,会返回指向selfInitialize()方法的引用赋值给函数式接口ServletContextInitializer.

java">	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {return this::selfInitialize;}private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}}

TomcatServletWebServerFactory#prepareContext()中,使用TomcatStarter类封装刚才注入的ServletContextInitializer接口,添加到Context容器的private Map<ServletContainerInitializer,Set<Class<?>>> initializers = new LinkedHashMap<>();中。

java">	protected void prepareContext(Host host, ServletContextInitializer[] initializers) {......context.addLifecycleListener(new StaticResourceConfigurer(context));ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);host.addChild(context);configureContext(context, initializersToUse);postProcessContext(context);}protected void configureContext(Context context, ServletContextInitializer[] initializers) {TomcatStarter starter = new TomcatStarter(initializers);if (context instanceof TomcatEmbeddedContext) {TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;embeddedContext.setStarter(starter);embeddedContext.setFailCtxIfServletStartFails(true);}context.addServletContainerInitializer(starter, NO_CLASSES);
......}

初始化完成后就会进入tomcat服务器的启动流程,此时会遍历内部的initializers,调用每个ServletContainerInitializer接口的onStartup()方法。

java">protected synchronized void startInternal() throws LifecycleException {......for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :initializers.entrySet()) {try {entry.getKey().onStartup(entry.getValue(),getServletContext());} catch (ServletException e) {log.error(sm.getString("standardContext.sciFail"), e);ok = false;break;}}......
}

此时会遍历TomcatStarter内部的initializers。

java">	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {try {for (ServletContextInitializer initializer : this.initializers) {initializer.onStartup(servletContext);}}......}

TomcatStarter内部的private final ServletContextInitializer[] initializers会包含selfInitialize方法的lamb引用,即会调用selfInitialize方法。

java">	private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}}protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {return new ServletContextInitializerBeans(getBeanFactory());}public ServletContextInitializerBeans(ListableBeanFactory beanFactory,Class<? extends ServletContextInitializer>... initializerTypes) {this.initializers = new LinkedMultiValueMap<>();//默认为 ServletContextInitializer 类型this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes): Collections.singletonList(ServletContextInitializer.class);addServletContextInitializerBeans(beanFactory);addAdaptableBeans(beanFactory);List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream().flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)).collect(Collectors.toList());this.sortedList = Collections.unmodifiableList(sortedInitializers);logMappings(this.initializers);}// 取出ServletContextInitializer类型bean//添加到private final MultiValueMap<Class<?>, ServletContextInitializer> initializers;private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,initializerType)) {addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);}}}	

由于ServletContextInitializerBeans实现了Iterable迭代器接口,可以对此对象进行迭代,而我们之前创建的
DispatcherServletRegistrationBean实现了ServletContextInitializer接口,因此会调用父类RegistrationBeanonStartup方法,最终在ApplicationContext#addServlet()方法中生成Wrapper对象,添加到StandardContext容器中。

java">    private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,Servlet servlet, Map<String,String> initParams) throws IllegalStateException {if (servletName == null || servletName.equals("")) {throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", servletName));}if (!context.getState().equals(LifecycleState.STARTING_PREP)) {//TODO Spec breaking enhancement to ignore this restrictionthrow new IllegalStateException(sm.getString("applicationContext.addServlet.ise",getContextPath()));}Wrapper wrapper = (Wrapper) context.findChild(servletName);// Assume a 'complete' ServletRegistration is one that has a class and// a nameif (wrapper == null) {wrapper = context.createWrapper();wrapper.setName(servletName);context.addChild(wrapper);} else {if (wrapper.getName() != null &&wrapper.getServletClass() != null) {if (wrapper.isOverridable()) {wrapper.setOverridable(false);} else {return null;}}}ServletSecurity annotation = null;if (servlet == null) {wrapper.setServletClass(servletClass);Class<?> clazz = Introspection.loadClass(context, servletClass);if (clazz != null) {annotation = clazz.getAnnotation(ServletSecurity.class);}} else {wrapper.setServletClass(servlet.getClass().getName());wrapper.setServlet(servlet);if (context.wasCreatedDynamicServlet(servlet)) {annotation = servlet.getClass().getAnnotation(ServletSecurity.class);}}if (initParams != null) {for (Map.Entry<String, String> initParam: initParams.entrySet()) {wrapper.addInitParameter(initParam.getKey(), initParam.getValue());}}ServletRegistration.Dynamic registration =new ApplicationServletRegistration(wrapper, context);if (annotation != null) {registration.setServletSecurity(new ServletSecurityElement(annotation));}return registration;}

到tomcat服务器调用子容器的startInternal方法阶段会注册到Context容器,

java">    private void registerContext(Context context) {String contextPath = context.getPath();if ("/".equals(contextPath)) {contextPath = "";}Host host = (Host)context.getParent();WebResourceRoot resources = context.getResources();String[] welcomeFiles = context.findWelcomeFiles();List<WrapperMappingInfo> wrappers = new ArrayList<>();for (Container container : context.findChildren()) {prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);if(log.isDebugEnabled()) {log.debug(sm.getString("mapperListener.registerWrapper",container.getName(), contextPath, service));}}mapper.addContextVersion(host.getName(), host, contextPath,context.getWebappVersion(), context, welcomeFiles, resources,wrappers);if(log.isDebugEnabled()) {log.debug(sm.getString("mapperListener.registerContext",contextPath, service));}}

最终在Mapper#addWrapper()方法中赋值给public MappedWrapper defaultWrapper,而处理请求时默认会使用defaultWrapper处理请求,就是说spring内置的DispatcherServlet是默认的servlet,所以请求默认都会被DispatcherServlet分发到内部的mvc的Controller中。


http://www.ppmy.cn/embedded/94861.html

相关文章

react-antive 項目報錯 [CXX1429] error when building with cmake using

react-antive 項目報錯 [CXX1429] error when building with cmake using修复 错误现场分析原因解决方案举一反三技巧引用参考&#xff08;感谢作者提供思路&#xff09; 错误现场 [CXX1429] error when building with cmake using /Users/sebastiangarcia/Desktop/work/flm/…

算法板子:容斥原理——求出 1∼n 中能被质数 p1,p2,…,pm 中的至少一个数整除的整数有多少个

1. 题目要点 1. 设&#xff1a;求1~10中能被质数2和3中至少一个数整除的数有多少个。1~10中能被质数2整除的数的集合记为S1{2,4,6,8,10}&#xff0c;能被质数3整除的数的集合记为S2{3,6,9}&#xff0c;能同时被质数2和3整数的数的集合为S1∩S2{6} 2. 这道题的目的是求S1∪S2∪S…

Python 报错:ModuleNotFoundError: No module named ‘Crypto‘

Crypto报错解决方案 Python 报错&#xff1a;ModuleNotFoundError: No module named Crypto前言问题解决方案 Python 报错&#xff1a;ModuleNotFoundError: No module named ‘Crypto’ 前言 Crypto是一个加密模块&#xff0c;它包含了多种加密算法&#xff0c;如 AES、DES、…

AI大模型赋能游戏:更智能、更个性化的NPC

参考论文&#xff1a;https://arxiv.org/abs/2403.10249 在传统游戏中&#xff0c;NPC&#xff08;非玩家角色&#xff09;的行为往往是预先设定好的&#xff0c;缺乏灵活性和变化性。然而&#xff0c;基于大模型的NPC可以利用其强大的推理和学习能力&#xff0c;实时生成对话…

在HTML中固定表格表头的简单方法

在HTML中&#xff0c;表格元素自身无法提供滚动以及固定表头的配置。借助第三方工具&#xff08;如jQuery的表头固定插件&#xff09;或者结合JavaScrip&#xff0c;是可以实现表格的表头固定的&#xff0c;除此之外&#xff0c;本文还想讨论一种更简单的方式来实现。 从思路上…

macOS 关闭系统更新以及相关提示

在 macOS 上&#xff0c;关闭系统更新以及相关提示可以通过以下步骤实现&#xff1a; 1. 通过系统偏好设置关闭自动更新 打开 系统偏好设置。点击 软件更新。取消勾选 “自动检查更新”&#xff0c;以及 “下载新的可用更新” 等选项。这样可以关闭自动检查更新和自动下载更新…

odoo17 翻译一个小bug

odoo17 翻译一个小bug 用户界面的没译过来 标红处&#xff0c;但在zh_CN.po中明显已经翻译过来了&#xff0c;采取暴力点的&#xff0c;直接把base下的base.pot删除&#xff0c;再更新一下&#xff0c;可以正常显示了

【数据结构】判断单链表中结点是否关于中心对称算法

设计判断单链表中结点是否关于中心对称算法 思路&#xff1a; 初始化栈&#xff1a; sqstack stack; stack.top -1; 初始化一个空栈&#xff0c;栈顶初始值为 -1。 第一次遍历链表&#xff1a; 使用指针 p 遍历链表&#xff0c;将每个节点的值 p->data 压入栈中。 每次压栈…