web.xml?SpringBoot打包可执行Jar运行SpringMVC的方法是什么


这篇文章主要讲解了“web.xmlSpringBoot打包可执行Jar运行SpringMVC的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“web.xmlSpringBoot打包可执行Jar运行SpringMVC的方法是什么”吧!

部署到webapps目录启动

本文使用的Spring版本为Spring6,SpringBoot版本为3,JDK为17,可能会和之前有细微不同,但整体流程差不太大。

如果部署应用到tomcat webapps目录下面启动,则需要在项目中配置web.xml文件

web.xml文件

配置Spring应用上下文

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/application-context.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

context-param

context-param标签是用于在Web应用程序的上下文范围内设置初始化参数。这些参数可以在整个Web应用程序中使用,并且可以通过ServletContext对象的getInitParameter()方法获取。

ContextLoaderListener

ContextLoaderListener实现了ServletContextListener接口,这个接口是tomcat留给应用程序初始化上下文环境的接口,用于在Web应用程序启动时加载ApplicationContext。

ServletContextListener有两个默认方法

//在所有的servlet和filter初始化之前被调用defaultpublicvoidcontextInitialized(ServletContextEventsce){}//在所有的servlet和filter销毁之后被调用defaultpublicvoidcontextDestroyed(ServletContextEventsce){}

ContextLoaderListener还继承了ContextLoader类,所有的context操作都在此类进行。

ContextLoaderListener实现contextInitialized方法,然后调用父类ContextLoader的initWebApplicationContext方法,把ServletContext传进去。

@OverridepublicvoidcontextInitialized(ServletContextEventevent){&nbsp;initWebApplicationContext(event.getServletContext());}

初始化Spring Context。

initWebApplicationContext方法关键代码

...if(this.context==null){&nbsp;&nbsp;//创建ApplicationContextthis.context=createWebApplicationContext(servletContext);}...//刷新ApplicationContextconfigureAndRefreshWebApplicationContext(cwac,servletContext);...//将当前ApplicationContext添加到ServletContext的属性中,后面有用再说//StringROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName()+".ROOT";servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);...

创建ApplicationContext

在createWebApplicationContext方法中,先调用determineContextClass方法确定使用哪个ApplicationContext,找到之后,实例化。

determineContextClass这个方法,主要是确定使用的ApplicationContext,首先从web.xml中加载,如果用户有定义,直接使用用户自定义的。

StringcontextClassName=servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

web.xml中配置如下,

<context-param><param-name>contextClass</param-name><param-value&gt.xxx.XxxContext</param-value></context-param>

如果没有配置,则使用Spring默认的XmlWebApplicationContext类。

这个类在ContextLoader同路径包下面的ContextLoader.properties文件中定义。

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

配置和刷新ApplicationContext

configureAndRefreshWebApplicationContext关键代码

protectedvoidconfigureAndRefreshWebApplicationContext(ConfigurableWebApplicationContextwac,ServletContextsc){//...//获取web.xml中配置的contextConfigLocation参数StringconfigLocationParam=sc.getInitParameter(CONFIG_LOCATION_PARAM);if(configLocationParam!=null){wac.setConfigLocation(configLocationParam);}//...//刷新上下文wac.refresh();}

至此Tomcat已经启动Spring环境了,后续就是Spring的初始化流程,这里不再叙述。

初始化DispatcherServlet

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern></servlet-mapping>

此处的contextConfigLocation属于DispatcherServlet的父类FrameworkServlet,主要用来加载SpringMVC相关的配置,示例如下:

<beansxmlns="springframework.org/schema/beans"xmlns:context="springframework.org/schema/context"xmlns:mvc="springframework.org/schema/mvc"xmlns:xsi="w3.org/2001/XMLSchema-instance"xsi:schemaLocation="springframework.org/schema/beansspringframework.org/schema/beans/spring-beans-4.3.xsdspringframework.org/schema/contextspringframework.org/schema/context/spring-context-4.3.xsdspringframework.org/schema/mvcspringframework.org/schema/mvc/spring-mvc-4.3.xsd"><!--扫描控制器和其他组件--><contextponent-scanbase-package=&quot.example.controller"/><!--配置视图解析器--><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/views/"/><propertyname="suffix"value=".jsp"/></bean><!--启用SpringMVC注解支持--><mvc:annotation-driven/></beans>

DispatcherServlet类图

可以看到DispatcherServlet实现了Servlet接口,Servlet接口中有init方法,SpringMVC的配置就是在初始化的时候被加载的。

关键代码在HttpServletBean.init()和FrameworkServlet.initServletBean()方法中。

HttpServletBean.init()

publicfinalvoidinit()throwsServletException{//Setbeanpropertiesfrominitparameters.PropertyValuespvs=newServletConfigPropertyValues(getServletConfig(),this.requiredProperties);if(!pvs.isEmpty()){try{BeanWrapperbw=PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoaderresourceLoader=newServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class,newResourceEditor(resourceLoader,getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs,true);}catch(BeansExceptionex){if(logger.isErrorEnabled()){logger.error("Failedtosetbeanpropertiesonservlet'"+getServletName()+"'",ex);}throwex;}}//Letsubclassesdowhateverinitializationtheylike.initServletBean();}

FrameworkServlet.initServletBean()

protectedfinalvoidinitServletBean()throwsServletException{...//在这里初始化ApplicationContextthis.webApplicationContext=initWebApplicationContext();//初始化servletinitFrameworkServlet();}

FrameworkServlet.initWebApplicationContext()

protectedWebApplicationContextinitWebApplicationContext(){//此处获取根容器,就是Spring初始化的XmlWebApplicationContext,//在上面把它添加到了ServletContext的属性中,标记根容器,这里把它获取出来//StringROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName()+".ROOT";//servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);WebApplicationContextrootContext=WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContextwac=null;//此时webApplicationContext还是null,因为DispatchServlet是被tomcat创建的,需要无参构造器//构造器中没有设置webApplicationContext的代码,所以此时webApplicationContext还是null//注意:在SpringBoot使用嵌入式Tomcat时,这个webApplicationContext不为null,因为FrameworkServlet还//实现了ApplicationContextAware接口,所以当SpringBoot的上下文准备好之后,会回调setApplicationContext方法//注入ApplicationContext,后面在细说if(this.webApplicationContext!=null){//Acontextinstancewasinjectedatconstructiontime->useitwac=this.webApplicationContext;if(wacinstanceofConfigurableWebApplicationContextcwac&&!cwac.isActive()){//Thecontexthasnotyetbeenrefreshed->provideservicessuchas//settingtheparentcontext,settingtheapplicationcontextid,etcif(cwac.getParent()==null){//Thecontextinstancewasinjectedwithoutanexplicitparent->set//therootapplicationcontext(ifany;maybenull)astheparentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}if(wac==null){//Nocontextinstancewasinjectedatconstructiontime->seeifone//hasbeenregisteredintheservletcontext.Ifoneexists,itisassumed//thattheparentcontext(ifany)hasalreadybeensetandthatthe//userhasperformedanyinitializationsuchassettingthecontextid//此处主要是获取web.xml配置的WebApplicationContext//可以通过设置参数contextAttribute来设置加载SpringMVC的ApplicationContext//比如下面这样。除非项目中有多个WebApplicationContext,需要使用其他WebApplicationContext才会用到//一般都是null//<context-param>//<param-name>contextAttribute</param-name>//<param-value>myWebApplicationContext</param-value>//</context-param>wac=findWebApplicationContext();}if(wac==null){//现在进入到创建SpringMVC的ApplicationContext流程//也就是加载contextConfigLocation定义的xml文件//Nocontextinstanceisdefinedforthisservlet->createalocalonewac=createWebApplicationContext(rootContext);}if(!this.refreshEventReceived){//EitherthecontextisnotaConfigurableApplicationContextwithrefresh//supportorthecontextinjectedatconstructiontimehadalreadybeen//refreshed->triggerinitialonRefreshmanuallyhere.synchronized(this.onRefreshMonitor){//初始化策略对象//比如:HandlerMapping,HandlerAdapter,ViewResolver等等onRefresh(wac);}}if(this.publishContext){//Publishthecontextasaservletcontextattribute.StringattrName=getServletContextAttributeName();getServletContext().setAttribute(attrName,wac);}returnwac;}
protectedWebApplicationContextcreateWebApplicationContext(@NullableApplicationContextparent){//SpringMVC所使用的contextClass,可以在<servlet>标签下设置//<init-param>//<param-name>contextClass</param-name>//<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>//</init-param>//默认为XmlWebApplicationContextClass<?>contextClass=getContextClass();if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)){thrownewApplicationContextException("Fatalinitializationerrorinservletwithname'"+getServletName()+"':customWebApplicationContextclass["+contextClass.getName()+"]isnotoftypeConfigurableWebApplicationContext");}//实例化ApplicationContextConfigurableWebApplicationContextwac=(ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);//设置环境参数wac.setEnvironment(getEnvironment());//设置父容器为Spring的ApplicationContextwac.setParent(parent);//获取SpringMVC的contextConfigLocation文件StringconfigLocation=getContextConfigLocation();if(configLocation!=null){wac.setConfigLocation(configLocation);}//配置并刷新ApplicationContextconfigureAndRefreshWebApplicationContext(wac);returnwac;}

DispatchServlet初始化完成

为什么需要父子容器

父子容器的作用主要是划分框架边界和实现bean的复用。

  • 在J2EE三层架构中,在service层我们一般使用Spring框架,而在web层则有多种选择,如Spring MVC、Struts等。为了让web层能够使用service层的bean,我们需要将service层的容器作为web层容器的父容器,这样就可以实现框架的整合。

  • 父子容器的作用在于,当我们尝试从子容器(Servlet WebApplicationContext)中获取一个bean时,如果找不到,则会委派给父容器(Root WebApplicationContext)进行查找。这样可以避免在多个子容器中重复定义相同的bean,提高了代码的复用性和可维护性。

接收请求

请求先进入doService,然后调用doDispatch进行处理。

doDispatch关键代码

...//首先根据当前请求HttpServletRequest,遍历所有的HandlerMapping执行handle方法,返回可用的HandlerExecutionChain对象。mappedHandler=getHandler(processedRequest);//然后根据handler获取支持的适配器HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());//执行HandlerInterceptor.preHandle,在controller的方法被调用前执行if(!mappedHandler.applyPreHandle(processedRequest,response)){return;}//执行controller方法mv=ha.handle(processedRequest,response,mappedHandler.getHandler());//执行HandlerInterceptor.postHandle,在controller的方法被调用后执行mappedHandler.applyPostHandle(processedRequest,response,mv);//渲染结果到视图processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);
  • HandlerMapping是request与handler object之间的映射,它能根据request找到对应的handler。handler object可以是任意类型,比如@Controller注解的类,或者实现了Controller接口的类,或者实现了HttpRequestHandler接口的类等。

  • HandlerExecutionChain是handler执行链,它包装了handler object和一组HandlerInterceptor。HandlerInterceptor是拦截器,它可以在handler执行前后进行一些额外的操作,比如权限检查,日志记录等。

  • HandlerAdapter是handler的适配器,它能处理不同类型的handler object,并调用其对应的方法,返回ModelAndView对象。HandlerAdapter可以根据handler object的类型,进行参数绑定,返回值处理等操作。

HandlerInterceptor使用

  • 定义一个拦截器类,实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类,重写preHandle,postHandle和aftepletion三个方法。

  • 在preHandle方法中,可以获取请求和响应对象,进行预处理,比如检查请求头中的token,或者判断请求的url是否有权限访问等。如果返回true,则继续执行后续的拦截器或者处理器;如果返回false,则中断请求,不再执行后续的拦截器或者处理器。

  • 在postHandle方法中,可以获取请求和响应对象,以及处理器返回的ModelAndView对象,进行后处理,比如修改模型数据或者视图信息等。这个方法只有在preHandle返回true且处理器成功执行后才会调用。

  • 在aftepletion方法中,可以获取请求和响应对象,以及处理器抛出的异常对象(如果有的话),进行清理资源或者异常处理等。这个方法只有在preHandle返回true后才会调用,无论处理器是否成功执行。

  • 在SpringMVC的配置文件中,注册拦截器类,并指定拦截的url模式。可以注册多个拦截器,并指定顺序。拦截器会按照顺序执行preHandle方法,然后按照逆序执行postHandle和aftepletion方法。

HandlerInterceptor和Filter的区别

  • HandlerInterceptor是基于Java反射机制的,而Filter是基于函数回调的。HandlerInterceptor可以利用Spring的AOP技术,实现更灵活的拦截逻辑,而Filter只能在请求前后进行简单的处理。

  • HandlerInterceptor不依赖于Servlet容器,而Filter依赖于Servlet容器。HandlerInterceptor是SpringMVC框架提供的,可以在任何情况下使用,而Filter是Servlet规范的一部分,只能在Web应用中使用。

  • HandlerInterceptor的执行由SpringMVC框架控制,而Filter的执行由Servlet容器控制。HandlerInterceptor可以通过IoC容器来管理,可以注入其他的Bean,而Filter则需要在web.xml中配置,或者使用@WebFilter注解,并且需要@ServleponentScan扫描。

  • HandlerInterceptor只能拦截DispatcherServlet处理的请求,而Filter可以拦截任何请求。HandlerInterceptor只能对Controller方法进行拦截,而Filter可以对静态资源、JSP页面等进行拦截。

  • HandlerInterceptor有三个方法:preHandle,postHandle和aftepletion,分别在请求处理前后和视图渲染前后执行,而Filter只有一个方法:doFilter,在请求处理前后执行。

处理controller返回结果

对于被controller方法,使用的适配器是RequestMappingHandlerAdapter,在handlerAdapter.handle方法执行时,会去执行对应的controller方法,处理controller方法返回的结果。

invocableMethod.invokeAndHandle(webRequest,mavContainer);

ServletInvocableHandlerMethod.invokeAndHandle

//执行controller方法ObjectreturnValue=invokeForRequest(webRequest,mavContainer,providedArgs);...//处理返回数据,会判断是不是有@ResponseBody注解,如果有,会使用RequestResponseBodyMethodProcessor来处理返回值//然后会解析请求头等等,判断应该返回什么类型的数据,然后使用对应的HttpMessageConverter写入输出流this.returnValueHandlers.handleReturnValue(returnValue,getReturnValueType(returnValue),mavContainer,webRequest);

SpringBoot Jar启动

SpringBoot使用嵌入式Servlet容器启动应用,有Tomcat,Jetty,Undertow。

选择Servlet容器

SpringBoot默认使用Tomcat,可以在配置文件中看出。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

web模块自动引入了tomcat

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></dependency>

如果不使用Tomcat可以排除,引入其他服务器。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--剔除Tomcat--><exclusions><exclusion><artifactId>spring-boot-starter-tomcat</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions></dependency><!--使用jetty--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>

如果没有排除Tomcat,直接引入其他服务器,比如下面。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--没有排除Tomcat--></dependency><!--引入jetty--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>

如果项目中同时引入了Tomcat和其他服务器的依赖,那么SpringBoot会按照以下顺序来选择启动的服务器。

Tomcat > Jetty > Undertow

也就是说,如果有Tomcat,就优先使用Tomcat,如果没有Tomcat,就看有没有Jetty,如果有Jetty,就使用Jetty,以此类推。这个顺序是在SpringBoot的ServletWebServerFactoryConfiguration类中定义的。

//只展示必要代码classServletWebServerFactoryConfiguration{//当Servlet、Tomcat、UpgradeProtocol类在类路径存在时//并且ServletWebServerFactory类存在,则会创建tomcatServletWebServerFactorybean。@ConditionalOnClass({Servlet.class,Tomcat.class,UpgradeProtocol.class})@ConditionalOnMissingBean(value=ServletWebServerFactory.class,search=SearchStrategy.CURRENT)staticclassEmbeddedTomcat{@BeanTomcatServletWebServerFactorytomcatServletWebServerFactory(...代码省略}}//当Servlet、Server、WebAppContext类在类路径存在时//并且ServletWebServerFactory类型的Bean不存在时,则会创建JettyServletWebServerFactorybean。//ServletWebServerFactory是TomcatServletWebServerFactory、JettyServletWebServerFactory、//UndertowServletWebServerFactory的父类//所以如果Tomcat被引入,上面的tomcatServletWebServerFactory就会被创建,这里的条件就不满足,不会被创建。@ConditionalOnClass({Servlet.class,Server.class,Loader.class,WebAppContext.class})@ConditionalOnMissingBean(value=ServletWebServerFactory.class,search=SearchStrategy.CURRENT)staticclassEmbeddedJetty{@BeanJettyServletWebServerFactoryJettyServletWebServerFactory(...代码省略}}//分析同上@ConditionalOnClass({Servlet.class,Undertow.class,SslClientAuthMode.class})@ConditionalOnMissingBean(value=ServletWebServerFactory.class,search=SearchStrategy.CURRENT)staticclassEmbeddedUndertow{@BeanUndertowServletWebServerFactoryundertowServletWebServerFactory(...代码省略}}

下面继续以Tomcat为例

Tomcat配置、启动

Tomcat是在Spring容器启动的时候启动的

SpringApplication.run方法

首先创建一个ConfigurableApplicationContext对象,并调用其refresh()方法,这个对象一般是AnnotationConfigServletWebServerApplicationContext。

context=createApplicationContext();->refreshContext(context);->refresh(context);->applicationContext.refresh();

refresh()方法会调用其父类ServletWebServerApplicationContext的refresh()方法,在父类的refresh()中再次调用父类AbstractApplicationContext的refresh()方法,主要在onRefresh阶段,会进行服务器的配置。

...refresh()代码简略//这里会初始化Tomcat配置onRefresh();//这里会启动TomcatfinishRefresh();...

回到ServletWebServerApplicationContext类的onRefresh()方法,会调用createWebServer()方法,创建web服务器。

protectedvoidonRefresh(){super.onRefresh();try{//创建服务器createWebServer();}catch(Throwableex){thrownewApplicationContextException("Unabletostartwebserver",ex);}}
privatevoidcreateWebServer(){...代码简略//获取工厂类,这里获取的就是在配置类中生效的那一个,这里为TomcatServletWebServerFactoryServletWebServerFactoryfactory=getWebServerFactory();createWebServer.tag("factory",factory.getClass().toString());//获取服务器this.webServer=factory.getWebServer(getSelfInitializer());}

TomcatServletWebServerFactory.getWebServer

publicWebServergetWebServer(ServletContextInitializer...initializers){if(this.disableMBeanRegistry){Registry.disableRegistry();}Tomcattomcat=newTomcat();FilebaseDir=(this.baseDirectory!=null)?this.baseDirectory:createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());for(LifecycleListenerlistener:this.serverLifecycleListeners){tomcat.getServer().addLifecycleListener(listener);}//设置Connector,对应与TomcatServer.xml中的<Connector></Connector>Connectorconnector=newConnector(this.protocol);connector.setThrowOnFailure(true);//对应于Server.xml中//<Servicename="Catalina">//<Connectorport="8080"protocol="HTTP/1.1"//connectionTimeout="20000"//redirectPort="8443"relaxedQueryChars="[|]"/>//</Service>tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for(ConnectoradditionalConnector:this.additionalTomcatConnectors){tomcat.getService().addConnector(additionalConnector);}//准备好Context组件prepareContext(tomcat.getHost(),initializers);returngetTomcatWebServer(tomcat);}
//创建Tomcat服务器protectedTomcatWebServergetTomcatWebServer(Tomcattomcat){returnnewTomcatWebServer(tomcat,getPort()>=0,getShutdown());}

至此,Tomcat配置已经初始化完成,准备启动。

在finishRefresh()方法中,会启动Tomcat

getLifecycleProcessor().onRefresh();>DefaultLifecycleProcessor.startBeans(true);>LifecycleGroup::start>doStart(this.lifecycleBeans,member.name,this.autoStartupOnly);>bean.start();>WebServerStartStopLifecycle.start>TomcatWebServer.start();
privatevoidstartBeans(booleanautoStartupOnly){Map<String,Lifecycle>lifecycleBeans=getLifecycleBeans();Map<Integer,LifecycleGroup>phases=newTreeMap<>();lifecycleBeans.forEach((beanName,bean)->{if(!autoStartupOnly||(beaninstanceofSmartLifecyclesmartLifecycle&&smartLifecycle.isAutoStartup())){intphase=getPhase(bean);phasesputeIfAbsent(phase,p->newLifecycleGroup(phase,this.timeoutPerShutdownPhase,lifecycleBeans,autoStartupOnly)).add(beanName,bean);}});if(!phases.isEmpty()){phases.values().forEach(LifecycleGroup::start);}}
publicvoidstart(){this.webServer.start();this.running=true;this.applicationContext.publishEvent(newServletWebServerInitializedEvent(this.webServer,this.applicationContext));}

DispatchServlet配置

ServletContextInitializer

在prepareContext方法中,有一个方法configureContext

configureContext(context,initializersToUse);

configureContext方法,在这里面创建了一个TomcatStarter对象,这个类实现了ServletContainerInitializer接口,所以在容器启动过程中会被调用。

TomcatStarterstarter=newTomcatStarter(initializers);context.addServletContainerInitializer(starter,NO_CLASSES);

initializers是Spring自己定义的初始化接口ServletContextInitializer,传入TomcatStarter之后,在onStartup方法中循环调用onStartup方法。

publicvoidonStartup(Set<Class<?>>classes,ServletContextservletContext)throwsServletException{try{for(ServletContextInitializerinitializer:this.initializers){initializer.onStartup(servletContext);}}...}

需要注意的是,这里的initializers有些传过来的时候是一个函数式接口,在上面的factory.getWebServer(getSelfInitializer());这里传进来的,就是一个函数式接口

privateorg.springframework.boot.web.servlet.ServletContextInitializergetSelfInitializer(){returnthis::selfInitialize;}

实际调用在下面这个方法

privatevoidselfInitialize(ServletContextservletContext)throwsServletException{prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),servletContext);for(ServletContextInitializerbeans:getServletContextInitializerBeans()){beans.onStartup(servletContext);}}

这里遍历所有的ServletContextInitializer,然后调用它的onStartup方法。

其中有一个DispatcherServletRegistrationBean,这个类实现了ServletContextInitializer接口,主要是用来添加DispatchServlet。

DispatcherServletAutoConfiguration配置类中有DispatcherServlet,DispatcherServletRegistrationBean两个Bean。

protectedstaticclassDispatcherServletRegistrationConfiguration{@Bean(name=DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value=DispatcherServlet.class,name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletRegistrationBeandispatcherServletRegistration(DispatcherServletdispatcherServlet,WebMvcPropertieswebMvcProperties,ObjectProvider<MultipartConfigElement>multipartConfig){//创建DispatcherServletRegistrationBean,并把dispatcherServlet传进去DispatcherServletRegistrationBeanregistration=newDispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);returnregistration;}}protectedstaticclassDispatcherServletConfiguration{@Bean(name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletdispatcherServlet(WebMvcPropertieswebMvcProperties){//创建DispatcherServletDispatcherServletdispatcherServlet=newDispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());returndispatcherServlet;}}

ServletContextInitializer.onStartup方法由子类RegistrationBean实现

publicfinalvoidonStartup(ServletContextservletContext)throwsServletException{Stringdescription=getDescription();if(!isEnabled()){logger.info(StringUtils.capitalize(description)+"wasnotregistered(disabled)");return;}//register是一个抽象方法,由子类DynamicRegistrationBean实现register(description,servletContext);}protectedabstractvoidregister(Stringdescription,ServletContextservletContext);

DynamicRegistrationBean.register

protectedfinalvoidregister(Stringdescription,ServletContextservletContext){//addRegistration是一个抽象方法,由子类ServletRegistrationBean实现Dregistration=addRegistration(description,servletContext);if(registration==null){logger.info(StringUtils.capitalize(description)+"wasnotregistered(possiblyalreadyregistered?)");return;}//Servlet被添加到Context后,这里对Servlet进行配置,如拦截路径configure(registration);}protectedabstractDaddRegistration(Stringdescription,ServletContextservletContext);

ServletRegistrationBean.addRegistration,作用类似下面

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet>
protectedServletRegistration.DynamicaddRegistration(Stringdescription,ServletContextservletContext){Stringname=getServletName();//添加Servlet到Context中,这里的servlet就是DispatchServlet。returnservletContext.addServlet(name,this.servlet);}

ServletRegistrationBean.configure,作用类似下面

<servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
protectedvoidconfigure(ServletRegistration.Dynamicregistration){super.configure(registration);String[]urlMapping=StringUtils.toStringArray(this.urlMappings);if(urlMapping.length==0&&this.alwaysMapUrl){//DEFAULT_MAPPINGS默是“/”urlMapping=DEFAULT_MAPPINGS;}if(!ObjectUtils.isEmpty(urlMapping)){//设置mappingregistration.addMapping(urlMapping);}registration.setLoadOnStartup(this.loadOnStartup);if(this.multipartConfig!=null){registration.setMultipartConfig(this.multipartConfig);}}

至此,DispatchServlet已配置好,后续流程和web.xml配置调用流程基本相同。

FrameworkServlet.initWebApplicationContext()

protectedWebApplicationContextinitWebApplicationContext(){//此处获取根容器,就是Spring初始化的XmlWebApplicationContext,//在上面把它添加到了ServletContext的属性中,标记根容器,这里把它获取出来//StringROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName()+".ROOT";//servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);//===========上面为使用web.xml时的分析,下面为SpringBoot嵌入式Tomcat分析============//同样是获取根容器,不过一般为AnnotationConfigServletWebServerApplicationContextWebApplicationContextrootContext=WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContextwac=null;//此时webApplicationContext还是null,因为DispatchServlet是被tomcat创建的,需要无参构造器//构造器中没有设置webApplicationContext的代码,所以此时webApplicationContext还是null//===========上面为使用web.xml时的分析,下面为SpringBoot嵌入式Tomcat分析============//注意:在SpringBoot使用嵌入式Tomcat时,这个webApplicationContext不为null,因为FrameworkServlet还//实现了ApplicationContextAware接口,所以当SpringBoot的上下文准备好之后,会回调setApplicationContext方法//注入ApplicationContext,后面在细说if(this.webApplicationContext!=null){//Acontextinstancewasinjectedatconstructiontime->useitwac=this.webApplicationContext;if(wacinstanceofConfigurableWebApplicationContextcwac&&!cwac.isActive()){//Thecontexthasnotyetbeenrefreshed->provideservicessuchas//settingtheparentcontext,settingtheapplicationcontextid,etcif(cwac.getParent()==null){//Thecontextinstancewasinjectedwithoutanexplicitparent->set//therootapplicationcontext(ifany;maybenull)astheparentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}if(wac==null){//Nocontextinstancewasinjectedatconstructiontime->seeifone//hasbeenregisteredintheservletcontext.Ifoneexists,itisassumed//thattheparentcontext(ifany)hasalreadybeensetandthatthe//userhasperformedanyinitializationsuchassettingthecontextid//此处主要是获取web.xml配置的WebApplicationContext//可以通过设置参数contextAttribute来设置加载SpringMVC的ApplicationContext//比如下面这样。除非项目中有多个WebApplicationContext,需要使用其他WebApplicationContext才会用到//一般都是null//<context-param>//<param-name>contextAttribute</param-name>//<param-value>myWebApplicationContext</param-value>//</context-param>//===========上面为使用web.xml时的分析,下面为SpringBoot嵌入式Tomcat分析//因为wac此时不为null,这里不会进入wac=findWebApplicationContext();}if(wac==null){//现在进入到创建SpringMVC的ApplicationContext流程//也就是加载contextConfigLocation定义的xml文件//===========上面为使用web.xml时的分析,下面为SpringBoot嵌入式Tomcat分析//因为wac此时不为null,这里不会进入,所以没有SpringMVC的容器,也就是没有父子容器之分,SpringBoot项目中只有一个容器//Nocontextinstanceisdefinedforthisservlet->createalocalonewac=createWebApplicationContext(rootContext);}if(!this.refreshEventReceived){//EitherthecontextisnotaConfigurableApplicationContextwithrefresh//supportorthecontextinjectedatconstructiontimehadalreadybeen//refreshed->triggerinitialonRefreshmanuallyhere.synchronized(this.onRefreshMonitor){//初始化策略对象//比如:HandlerMapping,HandlerAdapter,ViewResolver等等onRefresh(wac);}}if(this.publishContext){//Publishthecontextasaservletcontextattribute.StringattrName=getServletContextAttributeName();getServletContext().setAttribute(attrName,wac);}returnwac;}

感谢各位的阅读,以上就是“web.xmlSpringBoot打包可执行Jar运行SpringMVC的方法是什么”的内容了,经过本文的学习后,相信大家对web.xmlSpringBoot打包可执行Jar运行SpringMVC的方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是主机评测网,小编将为大家推送更多相关知识点的文章,欢迎关注!


上一篇:怎么在Windows电脑上安装XAMPP并配置PHP环境

下一篇:php7.1服务找不到的问题怎么解决


Copyright © 2002-2019 测速网 www.inhv.cn 皖ICP备2023010105号
测速城市 测速地区 测速街道 网速测试城市 网速测试地区 网速测试街道
温馨提示:部分文章图片数据来源与网络,仅供参考!版权归原作者所有,如有侵权请联系删除!

热门搜索 城市网站建设 地区网站制作 街道网页设计 大写数字 热点城市 热点地区 热点街道 热点时间 房贷计算器