在没有使用SpringMVC之前,我们开发Web应用是使用Servlet
+Jsp
的方式,后来又演化成HTML
+AJAX
+Servlet
的方式。XiXi对于原生的JavaWeb那套开发也是相当不熟悉,因为我刚毕业就是SpringBoot、SpringCloud微服务那套东西了,但是毕竟大学时候也用原生的JavaWeb做过几个课设,所以也略知一二,但是知道的确实不多。 在使用SpringMVC之后,我们就不用写Servlet
了,只要简单的写一个标注有@Controller
或@RestController
的类,在类中定义相关处理方法并标识@GetMapping
、@RequestMapping
等注解,请求会自动的找到我们这个方法,执行方法中的逻辑。在Servlet开发中,解析请求参数、解析响应参数等等一系列麻烦的操作,SpringMVC都帮我们处理,只需要我们添加相应的注解即可。 大家在感受到框架带来的便利时,是不是也对其背后的原理产生强烈的好奇,那么今天,我们就开始研究一下SpringMVC是如何让我们可以这么方便的开发一个后端接口的。
原理概述
SpringMVC框架也是要以Servlet为基础的,也就是说,请求发送,Tomcat服务器接收并最终到Servlet组件去处理,这段流程不管你用不用SpringMVC框架,都是一样。SpringMVC帮我们写好了一个Servlet
组件(DispatcherServlet
)去处理所有请求,此时处理请求需要运用到一些其他类,这些类用于解析入参、逻辑处理(用户自定义的Controller)、解析出参等。这些类都在一个“百宝箱”中,这个“百宝箱”就是我们的IOC容器
(Spring应用上下文)。
图中描述的就是我想说的一个原理 SpringMVC框架实现原理本质上:一段请求处理逻辑+
ApplicationContext
处理逻辑:统一所有请求的处理逻辑 ApplicationContext
:获取处理请求时用到的类
关于请求到Tomcat服务器最后到的Servlet处理这段逻辑,属于Tomcat源码内容,XiXi这里也不会,我总不能再把Tomcat的源码读一遍吧。下图是我找的一张Tomcat架构图。
图中我们注意一下,一个Tomcat服务器可以部署多个应用(每个应用对应一个
Context
),每个Context
里都有多个Servlet
Spring上下文何时被创建
文中服务器以Tomcat为例
我们知道DispatcherServlet
需要一个Spring应用上下文,Tomcat服务器启动后,这个Spring应用上下文在哪里被创建呢? 这个还要分两种情况讨论,非SpringBoot方式和SpringBoot方式
-
非SpringBoot方式:应用写好后打成war包放在Tomcat服务器上,Tomcat服务器启动后创建Spring应用上下文 -
SpringBoot方式:先创建Spring应用上下文,再启动内嵌Tomcat服务器
可以看到非SpringBoot方式和SpringBoot方式,服务器和Spring应用上下文的创建顺序还是不同的。下面我们来具体分析一下
非SpringBoot方式
非SpringBoot方式,Tomcat服务器启动后,触发ServletContextListener#contextInitialized
,因此SpringMVC框架在这个地方存在创建Spring应用上下文的机会。另外Servlet#init()
会在Servlet初始化时调用,SpringMVC框架在这个地方也存在创建Spring应用上下文的机会。
JavaWeb基础知识
@WebListener
public class ServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("servletContext 启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servletContext 销毁");
}
}
Tomcat服务启动后,
contextInitialized
方法被调用
@WebServlet(name = "MyServlet", urlPatterns = { "/myservlet" }, loadOnStartup = 1)
public class MyServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init ......");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
}
}
Servlet的
init()
会在创建时被调用1次
web.xml方式
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
ContextLoaderListener#contextInitialized
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
//..............
</web-app>
在Spring官方的
web.xml
配置中,可以看到配置了一个监听器和一段context-param
,这两处就是用于创建Spring应用上下文的。下面我们一起来看一下
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
contextInitialized()
->initWebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
//........
try {
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
代码如上,这里会创建
Root-ApplicationContext
,具体创建Spring应用上下文逻辑又调用了createWebApplicationContext
,创建Root-ApplicationContext
后被塞到ServletContext
的属性中, 属性名称为:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
上面这段代码是反射的方式创建应用上下文
Root-Application
protected void configureAndRefreshWebApplicationContext(
ConfigurableWebApplicationContext wac,
ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//应用上下文 设置了 ServletContext
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();
}
这段代码是完成了
Root-ApplicationContext
的刷新
可以看到,最终Root-ApplicationContext
和ServletContext
是做到了一个你中有我,我中有你的状态
HttpServletBean#init()
<web-app>
//.....................
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
该方法调用了
initServletBean()
@Override
protected final void initServletBean() throws ServletException {
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
}
可以清除的看到
FrameworkServlet#initServletBean
会继续调用initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
最终进行了一个
DispatcherServlet
应用上下文的创建
编程式
Servlet3.0开始可以不通过
web.xml
配置web应用,因此出现了纯编程式的Web应用配置方式。
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { AppConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] {new LoggerFilter()};
}
}
这种方式应用上下文的创建方式和
web.xml
不同,其核心是Servlet3.0出现的ServletContainerInitializer
可以用于注册Servlet
、Listener
、Filter
。这里我就不细说了。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
//.....................
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
可以看到循环调用了
WebApplicationInitializer#onStartup
,我们看下AbstractContextLoaderInitializer
,它是WebApplicationInitializer
实现类
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
@Nullable
protected abstract WebApplicationContext createRootApplicationContext();
@Nullable
protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
return null;
}
}
可以看到先创建一个Spring应用上下文放到
ContextLoaderListener
中,随后ContextLoaderListener
会执行容器启动的方法contextInitialized
完成Spring应用上下文设置到ServletContext
,对于创建应用上下文,我们看下AbstractDispatcherServletInitializer
和AbstractAnnotationConfigDispatcherServletInitializer
的实现
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
public static final String DEFAULT_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
}
这个类创建了
DispatcherServlet
并设置了Spring应用上下文
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
context.register(configClasses);
}
return context;
}
@Nullable
protected abstract Class<?>[] getRootConfigClasses();
@Nullable
protected abstract Class<?>[] getServletConfigClasses();
}
这个类的
createRootApplicationContext()
创建了根上下文被AbstractContextLoaderInitializer
调用
好的兄弟们,我们可以总结一下,编程式的Spring应用上下文设置到ServletContext
和DispatcherServlet
中的方式主要是在WebApplicationInitializer
实现类中new好,给到ContextLoaderListener
和DispatcherServlet
的。下面我们看看SpringBoot又是怎么去设置的。
SpringBoot方式
SpringBoot在启动Web应用时,使用的是AnnotationConfigServletWebServerApplicationContext
,这个应用上下文在refresh()
方法中的onRefresh()
完成了内嵌服务器的创建和启动工作。就是在这个时候应用上下文被设置到ServletContext
中,后续DispatcherServlet
启动,使用的是ServletContext
中的应用上下文
JavaWeb基础知识
在了解SpringBoot方式前,我们需要先介绍一个接口ServletContainerInitializer
,在web容器启动时会调用接口的onStartup()
方法,这个方法里可以注册Filter、Servlet、Listener,是Servlet3.0引入的接口。
package javax.servlet;
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
https://blog.csdn.net/lixinlong_8888/article/details/133002147
下面我给大家简单演示一下用法
-
在自己工程的包下写一个 ServletContainerInitializer
实现类
@HandlesTypes(HttpServlet.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println(c);
System.out.println("hello");
}
}
服务器启动后,会调用
onStartup
Set<Class<?>> c
:会根据@HandlesTypes(HttpServlet.class)
指定的类型,将应用中所有类型加载到c入参中ServletContext
:Servlet上下文
-
在resources目录下创建如下结构

-
javax.servlet.ServletContainerInitializer
的文件内容就是自己的那个实现类
com.gao.MyServletContainerInitializer
Spring应用上下文设置到ServletContext
一切故事都从onRefresh()
说起
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
直接进入
createWebServer()
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
如果存在 ServletContext
则直接调用getSelfInitializer().onStartup(servletContext)
不存在 ServletContext
则会先获取工厂,再进行创建,入参还是getSelfInitializer()
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);
}
}
返回了一个函数式接口,其方法实现是
selfInitialize
,工厂的获取咱们就不看了,下面我们进入this.webServer = factory.getWebServer(getSelfInitializer());
通过工厂创建webServer
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
说实话不知道Tomcat架构根本看懂上面的代码,反正就是在构建一个tomcat,咱们看看我们的入参在哪被用,继续进入
prepareContext
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
///......
configureContext(context, initializersToUse);
postProcessContext(context);
}
不管实现,我们只关注我们入参合适被用到的。进入
configureContext
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
//...............
}
后面代码咱们不管,它用入参创建了一个
TomcatStarter
。兄弟们我们看看这个类
class TomcatStarter implements ServletContainerInitializer {
private static final Log logger = LogFactory.getLog(TomcatStarter.class);
private final ServletContextInitializer[] initializers;
private volatile Exception startUpException;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
}
兄弟们看到了吧,知道我为啥在JavaWeb基础中点
ServletContainerInitializer
接口了吧,这个接口的实现类会在web容器创建后调用。而onStartup
循环调用我们的入参ServletContextInitializer#onStartup
ServletContextInitializer#onStartup
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);
}
}
兄弟们咱们也不要看全部,就看
prepareWebApplicationContext
的实现
protected void prepareWebApplicationContext(ServletContext servletContext) {
Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//........
try {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
setServletContext(servletContext);
}
catch (RuntimeException | Error ex) {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
是不是,将this设置到了
ServletContext
的属性中,并且ServletContext
也通过setServletContext
被设置到应用上下文中。注意这段代码在ServletWebServerApplicationContext
类中,它是AnnotationConfigServletWebServerApplicationContext
的父类,this就是AnnotationConfigServletWebServerApplicationContext
DispatcherServlet如何获取Spring上下文
和非SpringBoot方式的web.xml配置的情况一样,这里就不细说了,SpringBoot方式下,DispatcherServlet
取到ServletContext
中的应用上下文进行使用。自己并没有另外再创建了。
原文始发于微信公众号(溪溪技术笔记):SpringMVC源码阅读-服务器启动
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207176.html