SpringMVC这篇文章吃透了,最少最少涨5000

  1. 阶段 9:销毁 2 个容器

3、阶段 1:Servlet 容器初始化


3.1、ServletContainerInitializer

咱们知道 servlet3.0 中新增了一个接口:ServletContainerInitializer,这个接口功能特别的牛逼,有了它之后,web.xml 配置文件可要可不要了。

public interface ServletContainerInitializer {

public void onStartup(Set<Class<?>> c, ServletContext ctx)

throws ServletException;

}

这个接口的实现类,如果满足下面 2 个条件,Servlet 容器启动的过程中会自动实例化这些类,然后调用他们的 onStartUp 方法,然后我们就可以在这些类的 onStartUp 方法中干活了,在 web.xml 干的所有事情,都可以在这个方法中干,特别强大:

  1. 这个类必须实现 ServletContainerInitializer 接口,且非抽象类
  1. 这个类的全类名必须要放在META-INF/services/javax.servlet.ServletContainerInitializer这个文件中

3.2、SpringServletContainerInitializer

下面重点来了,springmvc 提供了一个类SpringServletContainerInitializer,满足了上面个条件。

b5e2cd12a8f5245e0fa85a716e6cde5c.png

spring-web-5.3.6.jar!\META-INF\services\javax.servlet.ServletContainerInitializer

cfe152a75ec058aea76cf740a1bc4cd6.png

所以 SpringServletContainerInitializer 的 onStart 方法会 servlet 容器自动被调用

3.3、SpringServletContainerInitializer#onStartup 方法

这个类的源码,大家先看一下,这个类干的事情:

  1. 类上有@HandlesTypes(WebApplicationInitializer.class) 这个注解,注解的值为WebApplicationInitializer.class,所以 onStartup 方法的第一个参数是WebApplicationInitializer类型的集合,这个集合由 web 容器自动扫描获取,然后传入进来
  1. 实例化 WebApplicationInitializer 集合
  1. 对 WebApplicationInitializer 集合进行排序
  1. 循环调用 WebApplicationInitializer 的 onStartup 方法

@HandlesTypes(WebApplicationInitializer.class) //@1

public class SpringServletContainerInitializer implements ServletContainerInitializer {

@Override

public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)

throws ServletException {

List initializers = Collections.emptyList();

if (webAppInitializerClasses != null) {

initializers = new ArrayList<>(webAppInitializerClasses.size());

for (Class<?> waiClass : webAppInitializerClasses) {

// Be defensive: Some servlet containers provide us with invalid classes,

// no matter what @HandlesTypes says…

if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&

WebApplicationInitializer.class.isAssignableFrom(waiClass)) {

try {

initializers.add((WebApplicationInitializer)

ReflectionUtils.accessibleConstructor(waiClass).newInstance());

}

catch (Throwable ex) {

throw new ServletException(“Failed to instantiate WebApplicationInitializer class”, ex);

}

}

}

}

if (initializers.isEmpty()) {

servletContext.log(“No Spring WebApplicationInitializer types detected on classpath”);

return;

}

servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");

AnnotationAwareOrderComparator.sort(initializers);

for (WebApplicationInitializer initializer : initializers) {

initializer.onStartup(servletContext);

}

}

}

下面重点要看WebApplicationInitializer接口了。

3.4、WebApplicationInitializer:web 应用初始化

接口比较简单,就一个方法,参数是 servlet 上下文对象,有了个对象,可以干 web.xml 中的一切事情了,比如注册 servlet、filter、监听器等等

public interface WebApplicationInitializer {

void onStartup(ServletContext servletContext) throws ServletException;

}

如下图,看一下类的继承关系,咱们的 MvcInit 就实现了这个接口,所以 MvcInit 的 onStartup 方法会被调费用

988e006c67479cb1e95d14084db6ed48.png

关键代码在这 3 个类中

148177da348ac47b14dd4920661da108.png

3.5、进入 AbstractDispatcherServletInitializer#onStartup 方法

public void onStartup(ServletContext servletContext) throws ServletException {

super.onStartup(servletContext);

registerDispatcherServlet(servletContext);

}

这里是重点:这个方法中干了 4 件事情

  1. 创建父容器,只是实例化了,并未启动
  1. 创建了监听器 ContextLoaderListener,这是一个 ServletContextListener 类型的监听器,稍后会在这个监听器中启动父容器
  1. 创建 springmvc 容器,只是实例化了,并未启动,启动的事情会在 DispatcherServlet#init 中做,稍后会说
  1. Servlet 容器中注册 DispatcherServlet

下面,咱们来详细看这几个步骤,把这几个步骤作为阶段来解读。

4、阶段 2:创建父容器


父容器可有可无,并不是必须的,为了更好的管理 bean,springmvc 建议我们用父子容器,controller 之外的 bean,比如 service,dao 等,建议放到父容器中,controller 层的和 springmvc 相关的一些 bean 放在 springmvc 容器中,咱们继续。

4.1、过程

AbstractDispatcherServletInitializer#onStartup方法中会调用父类的onStartup,即AbstractContextLoaderInitializer#onStartup,我们进到这个方法中,代码如下图,干了 2 个事情

  1. 图中编号 ①:创建父容器,只是实例化了,并未启动
  1. 图中编号 ②:创建了一个监听器 ContextLoaderListener,这是一个 ServletContextListener 类型的监听器,稍后会在这个监听器中启动父容器

6cd8e179cfead8df1cc9e86fb18933fb.png

下面来分别来细说下上面 2 段代码干的活。

4.2、①:负责创建父容器

AbstractAnnotationConfigDispatcherServletInitializer#createRootApplicationContext,只是创建了一个AnnotationConfigWebApplicationContext对象,并将父容器配置类 rootConfigClass 注册到容器中,并没有启动这个容器,若 rootConfigClass 为空,父容器不会被创建,所以父容器可有可无。

a4559bae2da16e6fd604f924643ff02d.png

4.2、②:创建 ContextLoaderListener 监听器

代码如下,创建的时候将父容器对象 rootAContext 传进去了。

ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);

//getRootApplicationContextInitializers()返回置为ApplicationContextInitializer数组,是个函数式接口,在父容器初始化的过程中,会作为一个扩展点预留给开发者用

listener.setContextInitializers(getRootApplicationContextInitializers());

servletContext.addListener(listener);

ContextLoaderListener,这是一个 ServletContextListener 类型的监听器,所以在 web 容器启动和销毁的过程中会被调用,如下图,这个监听器干了 2 件事

  1. contextInitialized 方法:这个方法会在 web 容器启动时被调用,内部负责启动父容器
  1. 在 contextDestroyed 方法:这个方法会在 web 容器销毁时被调用,内部负责关闭父容器

004085310864ba72bb537358a82a8654.png

5、阶段 3&4:创建 springmvc 容器&注册 DispatcherServlet


在回到AbstractDispatcherServletInitializer#onStartup,看这个方法的第二行,如下图

b8b587130bc3c679757e68d88d73555f.png

registerDispatcherServlet源码如下

protected void registerDispatcherServlet(ServletContext servletContext) {

//①:DispatcherServlet的servlet名称,默认为:dispatcher

String servletName = getServletName();

//②:创建springmvc容器

WebApplicationContext servletAppContext = createServletApplicationContext();

//③:创建DispatcherServlet,注意这里将springmvc容器对象做为参数传递给DispatcherServlet了

FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);

//设置ApplicationContextInitializer列表,可以对springmvc容器在启动之前进行定制化

dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

//④:将 dispatcherServlet 注册到servlet上下文中

ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

registration.setLoadOnStartup(1);

registration.addMapping(getServletMappings());

registration.setAsyncSupported(isAsyncSupported());

//⑤:注册Filter

Filter[] filters = getServletFilters();

if (!ObjectUtils.isEmpty(filters)) {

for (Filter filter : filters) {

registerServletFilter(servletContext, filter);

}

}

//⑥:这个方法预留给咱们自己去实现,可以对dispatcherServlet做一些特殊的配置

customizeRegistration(registration);

}

protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {

return new DispatcherServlet(servletAppContext);

}

6、阶段 5:启动父容器:ContextLoaderListener


6.1、过程

上面的onStartup方法执行完毕之后,会执行监听器ContextLoaderListener的初始化,会进入到他的contextInitialized方法中

3edcdc262245d9f0b9ea91ab6c5e5905.png

initWebApplicationContext源码如下,截取了主要的几行

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

//this.context就是父容器对象

ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;

//①:配置及启动父容器

configureAndRefreshWebApplicationContext(cwac, servletContext);

//将父容器丢到servletContext中进行共享,方便其他地方获取

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

}

6.2、代码 ①:配置父容器以及启动父容器

//①:配置及启动父容器

configureAndRefreshWebApplicationContext(cwac, servletContext);

configureAndRefreshWebApplicationContext方法关键代码如下

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

//①:定制上线文,这里主要是遍历ApplicationContextInitializer列表,调用每个ApplicationContextInitializer#initialize方法来对容器进行定制,相当于一个扩展点,可以有程序员自己控制

customizeContext(sc, wac);

//②:刷新容器,就相当于启动容器了,此时就会组装里面的bean了

wac.refresh();

}

customizeContext方法,我们进去看一下,这里涉及到了一个新的类,所以有必要去看一下,混个脸熟,源码如下,这是给开发者留的一个扩展点,通过ApplicationContextInitializer这个来做扩展,这是一个函数式接口,下面代码会遍历ApplicationContextInitializer列表,然后调用其initialize方法,我们可以在这个方法中对 spring 上线文进行定制

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {

List<Class<ApplicationContextInitializer>> initializerClasses =

determineContextInitializerClasses(sc);

for (Class<ApplicationContextInitializer> initializerClass : initializerClasses) {

Class<?> initializerContextClass =

GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);

if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {

throw new ApplicationContextException(String.format(

"Could not apply context initializer [%s] since its generic parameter [%s] " +

"is not assignable from the type of application context used by this " +

“context loader: [%s]”, initializerClass.getName(), initializerContextClass.getName(),

wac.getClass().getName()));

}

this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));

}

AnnotationAwareOrderComparator.sort(this.contextInitializers);

for (ApplicationContextInitializer initializer : this.contextInitializers) {

initializer.initialize(wac);

}

}

6.3、ApplicationContextInitializer 接口:容器启动前用来初始化容器

是个函数式接口,在容器启动之前用来对容器进行定制,作为一个扩展点预留给开发者用,父容器和 springmvc 容器都用到了。

@FunctionalInterface

public interface ApplicationContextInitializer {

/**

* 初始化给定的spring容器

* @param applicationContext the application to configure

*/

void initialize(C applicationContext);

}

7、阶段 6:启动 springmvc 容器:DispatcherServlet#init()


到目前为止父容器已经启动完毕了,此时 DispatcherServlet 会被初始化,会进入到他的 init()方法中。

7.1、DispatcherServlet 类图

991cdc1240f1b20ed3754b09ec5d4982.png

7.2、HttpServletBean#init()

这个方法会调用initServletBean()这个方法,其他的先不看

ae38e03b19ca2a1a1fbb2ebca5c2cc83.png

7.3、FrameworkServlet#initServletBean

提取了关键的代码,就 2 行

@Override

protected final void initServletBean() throws ServletException {

//初始化springmvc容器,就是启动springmvc容器

this.webApplicationContext = initWebApplicationContext();

//这个方法内部是空的,预留给子类去实现的,目前没啥用

initFrameworkServlet();

}

下面咱们进到initWebApplicationContext方法中去。

7.4、FrameworkServlet#initWebApplicationContext

关键代码如下,干了 3 件事情:

  1. 从 servlet 上线文对象中找到父容器
  1. 为 springmvc 容器指定父容器
  1. 调用 configureAndRefreshWebApplicationContext 方法配置 springmvc 容器以及启动容器,这个是关键咯

protected WebApplicationContext initWebApplicationContext() {

//①:从servlet上线文中获取父容器

WebApplicationContext rootContext =

WebApplicationContextUtils.getWebApplicationContext(getServletContext());

WebApplicationContext wac = null;

//②:this.webApplicationContext就是springmvc容器,此时这个对对象不为null,所以满足条件

if (this.webApplicationContext != null) {

wac = this.webApplicationContext;

if (wac instanceof ConfigurableWebApplicationContext) {

ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;

//springmvc容器未启动

if (!cwac.isActive()) {

//springmvc容器未设置父容器,则给其设置父容器,此时rootContext可能为空,这个没什么关系

if (cwac.getParent() == null) {

cwac.setParent(rootContext);

}

//③:配置springmvc容器以及启动springmvc容器

configureAndRefreshWebApplicationContext(cwac);

}

}

}

//这里省略了一部分代码,如果springmvc采用配置文件的方式会走这部分代码

//返回容器

return wac;

}

7.5、FrameworkServlet#configureAndRefreshWebApplicationContext

为了让大家看清楚,如下代码,这里只提取了关键代码,主要干了 3 件事情

  1. 代码 ①:向 springmvc 容器中添加了一个 ContextRefreshListener 监听器,这个监听器非常非常重要,springmvc 容器启动完毕之后会被调用,会出现在阶段 8 中
  1. 代码 ②:给开发者预留的一个扩展点,通过 ApplicationContextInitializer#initialize 方法对容器进行定制
  1. 代码 ③:启动容器

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {

//①:向springmvc容器中添加了一个监听器对象,这个监听器特别重要,稍后在

wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

//②:扩展点:循环遍历ApplicationContextInitializer列表,调用其initialize方法对容器进行定制

applyInitializers(wac);

//③:刷新容器,相当于启动容器

wac.refresh();

}

8、阶段 7:springmvc 容器启动过程中处理@WebMVC


8.1、SpringMVC 配置类被处理

此时 springmvc 容器启动了,此时注意下MvcConfig这个类,由于其上有@Conguration 注解,所以会被当做一个配置类被处理,这个类有 2 个非常重要的特征。

  1. 标注了@EnableWebMvc 注解
  1. 实现了 WebMvcConfigurer 接口

d9d007d7cb888ef1d4edc56b7a05c930.png

下面来说说这 2 个特征的作用。

8.2、@EnableWebMvc:配置 springmvc 所需组件

看一下这个注解的源码,如下,重点在于它上面的@Import(DelegatingWebMvcConfiguration.class)注解,这个注解的功能不知道的,可以回头去看我的 spring 系列,从头看一遍。

e6caffc1d224aef39a768f4624a88167.png

8.3、进入 DelegatingWebMvcConfiguration 类

代码如下,先注意下面 3 个特征

  1. 代码编号 ①:标注有@Configuration 注解,说明是一个配置类
  1. 代码编号 ②:继承了 WebMvcConfigurationSupport 类,这个类中有很多@Bean 标注的方法,用来定义了 springmvc 需要的所有组件
  1. 代码编号 ③:注入了WebMvcConfigurer列表,注意下,我们的 WebConfig 类就实现了 WebMvcConfigurer 这个接口,内部提供了很多方法可以用来对 springmvc 的组件进行自定义配置

b703184841f78d5e3047edd783e155c5.png

先来看看 WebMvcConfigurationSupport 这个类。

8.4、WebMvcConfigurationSupport:配置 springmvc 所需所有组件

这个类中会定义 springmvc 需要的所有组件,比如:RequestMapping、HandlerAdapter、HandlerInterceptor、HttpMessageConverter、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler 等等,所以如果你感觉@WebMVC 注解满足不了你的需求时,你可以直接继承这个类进行扩展。

这个类的源码我就不贴了,截几个图给大家看看

d326312623c5736feea27dea3e7debfd.png

8.5、WebMvcConfigurer 接口

这个接口就是我们用来对 springmvc 容器中的组件进行定制的,WebMvcConfigurationSupport 中创建 springmvc 组件的时候,会自动调用 WebMvcConfigurer 中对应的一些方法,来对组件进行定制,比如可以在 WebMvcConfigurer 中添加拦截器、配置默认 servlet 处理器、静态资源处理器等等,这个接口的源码如下

public interface WebMvcConfigurer {

/**

* 配置PathMatchConfigurer

*/

default void configurePathMatch(PathMatchConfigurer configurer) {

}

/**

* 配置ContentNegotiationConfigurer

*/

default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

}

/**

* 异步处理配置

*/

default void configureAsyncSupport(AsyncSupportConfigurer configurer) {

}

/**

* 配置默认servlet处理器

*/

default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

}

/**

* 配置Formatter

*/

default void addFormatters(FormatterRegistry registry) {

}

/**

* 添加拦截器

*/

default void addInterceptors(InterceptorRegistry registry) {

}

/**

* 静态资源配置

*/

default void addResourceHandlers(ResourceHandlerRegistry registry) {

}

/**

* 跨越的配置

*/

default void addCorsMappings(CorsRegistry registry) {

}

/**

* 配置ViewController

*/

default void addViewControllers(ViewControllerRegistry registry) {

}

/**

* 注册视图解析器(ViewResolverRegistry)

*/

default void configureViewResolvers(ViewResolverRegistry registry) {

}

/**

* 注册处理器方法参数解析器(HandlerMethodArgumentResolver)

*/

default void addArgumentResolvers(List resolvers) {

}

/**

* 注册处理器方法返回值处理器(HandlerMethodReturnValueHandler)

*/

default void addReturnValueHandlers(List handlers) {

}

/**

* 注册http报文转换器(HttpMessageConverter)

*/

default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

}

/**

* 扩展报文转换器

*/

default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

}

/**

* 配置异常解析器(HandlerExceptionResolver)

*/

default void configureHandlerExceptionResolvers(List resolvers) {

}

/**

* 扩展异常解析器(HandlerExceptionResolver)

*/

default void extendHandlerExceptionResolvers(List resolvers) {

}

/**

* 获得验证器

*/

@Nullable

default Validator getValidator() {

return null;

}

/**

* 获得MessageCodesResolver

*/

@Nullable

default MessageCodesResolver getMessageCodesResolver() {

return null;

}

}

9、阶段 8:组装 DispatcherServlet 中各种 SpringMVC 需要的组件


9.1、触发 ContextRefreshListener 监听器

大家回头看一下 8.5 中,有这样一段代码,注册了一个监听器ContextRefreshListener

5ffafa65fb90ecb4f594a2f55b799f6d.png

再来看看这个监听器的源码,如下图,包含 2 点信息

  1. 会监听 ContextRefreshedEvent 事件
  1. 监听到事件之后将执行FrameworkServlet.this.onApplicationEvent(event);,稍后会具体说这个代码

d79bc133b784abaec505faaf49f29b06.png

如下代码,springmvc 容器启动完毕之后,会发布一个ContextRefreshedEvent事件,会触发上面这个监听器的执行

f101e2c4ce4e891b8628037c66071ca1.png

9.2、进入 FrameworkServlet.this.onApplicationEvent(event);

public void onApplicationEvent(ContextRefreshedEvent event) {

//标记已收到刷新事件

this.refreshEventReceived = true;

synchronized (this.onRefreshMonitor) {

onRefresh(event.getApplicationContext());

}

}

上面的onRefresh(event.getApplicationContext());会进到DispatcherServlet#onRefresh方法中。

9.3、进入 DispatcherServlet#onRefresh

protected void onRefresh(ApplicationContext context) {

initStrategies(context);

}

里面会调用initStrategies方法。

9.4、DispatcherServlet#initStrategies:初始化 DispatcherServlet 中的组件

代码如下,这里面会初始化 DispatcherServlet 中的各种组件,这里的所有方法初始化的过程基本上差不多,就是先从 springmvc 容器中找这个组件,如果找不到一般会有一个兜底的方案

protected void initStrategies(ApplicationContext context) {

//初始化MultipartResolver

initMultipartResolver(context);

//初始化LocaleResolver

initLocaleResolver(context);

//初始化ThemeResolver

initThemeResolver(context);

//初始化HandlerMappings

initHandlerMappings(context);

//初始化HandlerAdapters

initHandlerAdapters(context);

//初始化HandlerExceptionResolvers

initHandlerExceptionResolvers(context);

//初始化RequestToViewNameTranslator

initRequestToViewNameTranslator(context);

//初始化视图解析器ViewResolvers

initViewResolvers(context);

//初始化FlashMapManager

initFlashMapManager(context);

}

下面我们以initHandlerMappings(context);为例来看一下是如何初始化这些组件的。

9.5、initHandlerMappings(context);

源码如下,就是先从容器中找,找不到走兜底的方案。

private void initHandlerMappings(ApplicationContext context) {

this.handlerMappings = null;

//是否需要查找所有的HandlerMapping,默认为true

if (this.detectAllHandlerMappings) {

//从容器中查找所有的HandlerMapping

Map<String, HandlerMapping> matchingBeans =

BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

//对HandlerMapping列表进行排序

if (!matchingBeans.isEmpty()) {

this.handlerMappings = new ArrayList<>(matchingBeans.values());

AnnotationAwareOrderComparator.sort(this.handlerMappings);

}

}

else {

try {

//查找名称为handlerMapping的HandlerMapping

HandlerMapping hm = context.getBean(“handlerMapping”, HandlerMapping.class);

this.handlerMappings = Collections.singletonList(hm);

}

catch (NoSuchBeanDefinitionException ex) {

}

}

// 如果没有找到HandlerMapping,则走兜底的方案

if (this.handlerMappings == null) {

this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);

}

}

下面我们来看一下兜底的代码如何走的,进入 getDefaultStrategies 方法

9.6、DispatcherServlet#getDefaultStrategies:兜底的方案查找组件

这个方法的源码就不贴出来了,这里只说一下兜底的处理过程,springmvc 有个配置文件:spring-webmvc-5.3.6.jar!\org\springframework\web\servlet\DispatcherServlet.properties,properties 格式的文件,key 为组件的完整类名,value 为多个实现类的列表,在这个配置文件中指定了每种类型的组件兜底的情况下对应的实现类,比如没有找到 RequestMapping 的情况下,如下图红框的部分,有 3 个兜底的实现类,然后 springmvc 会实例化这 3 个类作为 RequestMapping。

0724ebbfa0121342c7693db5b3bde02d.png

10、阶段 9:销毁容器


10.1、销毁 springmvc 容器:DispatcherServlet#destroy

DispatcherServlet 销毁的时候会关闭 springmvc 容器

public void destroy() {

if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {

((ConfigurableApplicationContext) this.webApplicationContext).close();

}

}

10.2、销毁父容器:ContextLoaderListener#contextDestroyed

父容器是在监听器中启动的,所以销毁的也是监听器负责的

public void contextDestroyed(ServletContextEvent event) {

closeWebApplicationContext(event.getServletContext());

ContextCleanupListener.cleanupAttributes(event.getServletContext());

}

springmvc 容器的生命周期到此就结束了,想掌握好这个过程,建议大家 debug 走几遍,就熟悉了,下面带大家 debug 一下代码。

11、带大家 debug 代码


11.1、拉取源码

https://gitee.com/javacode2018/springmvc-series

11.2、将下面这个模块发布到 tomcat

cc77bb136722a4c1b1081fc2f074481d.png

11.2、按照下面配置设置断点,启动,调试代码

依次在下面这些方法中设置断点,然后启动 tomcat,一步步调试,我相信你们肯定可以吃透的。

1、org.springframework.web.SpringServletContainerInitializer#onStartup:入口

2、org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#onStartup

3、org.springframework.web.context.AbstractContextLoaderInitializer#onStartup

4、org.springframework.web.context.AbstractContextLoaderInitializer#registerContextLoaderListener:创建父容器

5、org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#createRootApplicationContext

6、org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#registerDispatcherServlet

7、org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#createServletApplicationContext:创建springmvc容器 & 注册DispatcherServlet

8、org.springframework.web.context.ContextLoaderListener#contextInitialized

9、org.springframework.web.context.ContextLoader#initWebApplicationContext

10、org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext:启动父容器

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
ervletInitializer#createRootApplicationContext

6、org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#registerDispatcherServlet

7、org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#createServletApplicationContext:创建springmvc容器 & 注册DispatcherServlet

8、org.springframework.web.context.ContextLoaderListener#contextInitialized

9、org.springframework.web.context.ContextLoader#initWebApplicationContext

10、org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext:启动父容器

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-HPbMhaG0-1713332541448)]

[外链图片转存中…(img-3LpQfjDT-1713332541448)]

[外链图片转存中…(img-4jXv5YIZ-1713332541448)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

[外链图片转存中…(img-VGPLOaEz-1713332541449)]

[外链图片转存中…(img-buz2t45U-1713332541449)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值