+
+
+
+报了一个缺少参数的错误,下面看看怎么处理的
+
+#### DispatcherServlet#processDispatchResult()
+
+```java
+ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
+ @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
+ @Nullable Exception exception) throws Exception {
+
+ boolean errorView = false;
+ //如果有异常处理异常,以下if内全是异常处理环节
+ if (exception != null) {
+ if (exception instanceof ModelAndViewDefiningException) {
+ logger.debug("ModelAndViewDefiningException encountered", exception);
+ mv = ((ModelAndViewDefiningException) exception).getModelAndView();
+ }
+ else { //定义无数种异常解析器就会得到不同的异常解析效果
+ Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
+ mv = processHandlerException(request, response, handler, exception); //处理异常,所有的异常解析器都不能干活,这个异常就抛出去了
+ errorView = (mv != null);
+ }
+ }
+ //上面所有的异常解析器都没能处理这个异常,下面直接炸....
+ // 动态策略。 Did the handler return a view to render? 为啥?@ResponseBody(提前在解析返回值的时候,就已经把数据写出去了,所以这一步就没有了)
+ if (mv != null && !mv.wasCleared()) {
+ render(mv, request, response); //渲染ModeAndView,来解析模型和视图;最终决定响应效果
+ if (errorView) {
+ WebUtils.clearErrorRequestAttributes(request);
+ }
+ }
+ else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("No view rendering, null ModelAndView returned.");
+ }
+ }
+
+ if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
+ // Concurrent handling started during a forward
+ return;
+ }
+
+ if (mappedHandler != null) {
+ // Exception (if any) is already handled..
+ mappedHandler.triggerAfterCompletion(request, response, null);
+ }
+ }
+```
+
+
+
+
+
+#### DispatcherServlet#processHandlerException()准备处理异常
+
+```java
+ protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
+ @Nullable Object handler, Exception ex) throws Exception {
+
+ // Success and error responses may use different content types
+ request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
+
+ // Check registered HandlerExceptionResolvers...
+ ModelAndView exMv = null; //所有异常解析器继续解析
+ if (this.handlerExceptionResolvers != null) {
+ for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
+ exMv = resolver.resolveException(request, response, handler, ex);
+ if (exMv != null) {
+ break;
+ }
+ }
+ }
+ if (exMv != null) {
+ if (exMv.isEmpty()) {
+ request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
+ return null;
+ }
+ // We might still need view name translation for a plain error model...
+ if (!exMv.hasView()) {
+ String defaultViewName = getDefaultViewName(request);
+ if (defaultViewName != null) {
+ exMv.setViewName(defaultViewName);
+ }
+ }
+ if (logger.isTraceEnabled()) {
+ logger.trace("Using resolved error view: " + exMv, ex);
+ }
+ else if (logger.isDebugEnabled()) {
+ logger.debug("Using resolved error view: " + exMv);
+ }
+ WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
+ return exMv;
+ }
+
+ throw ex; //如果所有的异常解析器都不能解析就直接抛出这个异常
+ }
+```
+
+
+
+
+
+##### 三个异常解析器概述
+
+
+
+- ExceptionHandlerExceptionResolver:所有@ExceptionHandler注解方式的异常处理由他来做,启动扫描了容器中所有标了@ControllerAdvice的类以及这个类里面所有@Exceptionhandler标注的方法,并且缓存这个方法能处理什么异常
+
+- ResponseStatusExceptionResolver:找异常类上有没有@ResponseStatus注解
+
+- DefaultHandlerExceptionResolver:异常是否是spring内部指定的异常,如果是,直接响应错误页sendError以及错误代码, 并返回new的空的ModelAndView(注意这里返回的是空,不是null)
+
+#### AbstractHandlerExceptionResolver#resolveException()解析异常
+
+```java
+ @Override
+ @Nullable //父类抽象类规定的模板
+ public ModelAndView resolveException(
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
+
+ if (shouldApplyTo(request, handler)) {
+ prepareResponse(ex, response);
+ ModelAndView result = doResolveException(request, response, handler, ex); //留给子类的模板方法
+ if (result != null) {
+ // Print debug message when warn logger is not enabled.
+ if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
+ logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
+ }
+ // Explicitly configured warn logger in logException method.
+ logException(ex, request);
+ }
+ return result;
+ }
+ else {
+ return null;
+ }
+ }
+```
+
+#### AbstractHandlerMethodExceptionResolver#doResolveException()
+
+```java
+ protected final ModelAndView doResolveException(
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
+
+ HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null);
+ return doResolveHandlerMethodException(request, response, handlerMethod, ex);
+ }
+```
+
+下面开始进入实现类,因为index=0的是ExceptionHandlerExceptionResolver,就会先进入这个异常解析器
+
+
+
+
+
+#### ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法
+
+```java
+ protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
+ HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
+ //为当前异常找一个处理方法??? @ExceptionHandler注解标注的方法
+ ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
+ if (exceptionHandlerMethod == null) {
+ return null;
+ }
+ //异常解析器里面 还是利用了以前的 argumentResolvers和 returnValueHandlers扩展了异常解析的功能
+ if (this.argumentResolvers != null) {
+ exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
+ }
+ if (this.returnValueHandlers != null) {
+ exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
+ }
+
+ ServletWebRequest webRequest = new ServletWebRequest(request, response);
+ ModelAndViewContainer mavContainer = new ModelAndViewContainer();
+
+ ArrayList
+
+
+
+接着返回
+
+#### 返回到DispatcherServlet#processHandlerException()
+
+准备循环第二个异常解析器
+
+
+
+
+
+接下来还是父类AbstractHandlerMethodExceptionResolver那个模板方法,前面写了,这里直接跳过
+
+
+
+#### ResponseStatusExceptionResolver#doResolveException()处理@ResponseStatus注解标注的相关异常
+
+```java
+ protected ModelAndView doResolveException(
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
+
+ try {
+ if (ex instanceof ResponseStatusException) {
+ return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
+ }
+ //拿到 ResponseStatus 注解
+ ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
+ if (status != null) {
+ return resolveResponseStatus(status, request, response, handler, ex);
+ }
+
+ if (ex.getCause() instanceof Exception) {
+ return doResolveException(request, response, handler, (Exception) ex.getCause());
+ }
+ }
+ catch (Exception resolveEx) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
+ }
+ }
+ return null;
+ }
+```
+
+
+
+因为咱们也没有标注@ResponseStatus注解,所以也是空,直接来到第三个异常解析器
+
+
+
+
+
+
+
+#### 返回到DispatcherServlet#processHandlerException()
+
+
+
+
+
+
+
+
+
+#### DefaultHandlerExceptionResolver#doResolveException()处理SpringMVC底层的异常
+
+```java
+ @Override
+ @Nullable
+ protected ModelAndView doResolveException(
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
+
+ try { //处理SpringMVC底层的异常
+ if (ex instanceof HttpRequestMethodNotSupportedException) {
+ return handleHttpRequestMethodNotSupported(
+ (HttpRequestMethodNotSupportedException) ex, request, response, handler);
+ }
+ else if (ex instanceof HttpMediaTypeNotSupportedException) {
+ return handleHttpMediaTypeNotSupported(
+ (HttpMediaTypeNotSupportedException) ex, request, response, handler);
+ }
+ else if (ex instanceof HttpMediaTypeNotAcceptableException) {
+ return handleHttpMediaTypeNotAcceptable(
+ (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
+ }
+ else if (ex instanceof MissingPathVariableException) {
+ return handleMissingPathVariable(
+ (MissingPathVariableException) ex, request, response, handler);
+ }
+ else if (ex instanceof MissingServletRequestParameterException) {
+ return handleMissingServletRequestParameter(
+ (MissingServletRequestParameterException) ex, request, response, handler);
+ }
+ else if (ex instanceof ServletRequestBindingException) {
+ return handleServletRequestBindingException(
+ (ServletRequestBindingException) ex, request, response, handler);
+ }
+ else if (ex instanceof ConversionNotSupportedException) {
+ return handleConversionNotSupported(
+ (ConversionNotSupportedException) ex, request, response, handler);
+ }
+ else if (ex instanceof TypeMismatchException) {
+ return handleTypeMismatch(
+ (TypeMismatchException) ex, request, response, handler);
+ }
+ else if (ex instanceof HttpMessageNotReadableException) {
+ return handleHttpMessageNotReadable(
+ (HttpMessageNotReadableException) ex, request, response, handler);
+ }
+ else if (ex instanceof HttpMessageNotWritableException) {
+ return handleHttpMessageNotWritable(
+ (HttpMessageNotWritableException) ex, request, response, handler);
+ }
+ else if (ex instanceof MethodArgumentNotValidException) {
+ return handleMethodArgumentNotValidException(
+ (MethodArgumentNotValidException) ex, request, response, handler);
+ }
+ else if (ex instanceof MissingServletRequestPartException) {
+ return handleMissingServletRequestPartException(
+ (MissingServletRequestPartException) ex, request, response, handler);
+ }
+ else if (ex instanceof BindException) {
+ return handleBindException((BindException) ex, request, response, handler);
+ }
+ else if (ex instanceof NoHandlerFoundException) {
+ return handleNoHandlerFoundException(
+ (NoHandlerFoundException) ex, request, response, handler);
+ }
+ else if (ex instanceof AsyncRequestTimeoutException) {
+ return handleAsyncRequestTimeoutException(
+ (AsyncRequestTimeoutException) ex, request, response, handler);
+ }
+ }
+ catch (Exception handlerEx) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
+ }
+ }
+ return null;
+ }
+```
+
+
+
+#### DefaultHandlerExceptionResolver#handleMissingServletRequestParameter()展示tomcat默认错误页
+
+```java
+ protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
+ //直接 sendError tomcat展示错误页
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
+ return new ModelAndView();
+ }
+```
+
+这就是咱们刚刚报的错,缺少参数
+
+#### 返回到DispatcherServlet#processHandlerException()
+
+
+
+看到这里返回了一个空的ModelAndView,并不是NULL
+
+#### 返回到DispatcherServlet#processDispatchResult()
+
+
+
+最后下面再处理下拦截器
+
+
+
+
+
+最终结束
+
+#### 页面效果
+
+
+
+
+
+
+
+### 传参但报错的情况
+
+我们这样写:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&i=0
+
+> 前面讲的不再重复
+
+#### DispatcherServlet#doDispatch()
+
+
+
+
+
+#### DispatcherServlet#processHandlerException()
+
+到了第三个异常解析器也依然处理不了,于是出现了一个谁都处理不了的异常
+
+
+
+然后就抛出此异常
+
+
+
+#### 返回到DispatcherServlet#processDispatchResult()直接炸了
+
+
+
+
+
+在这一步抛出了异常,整个方法直接炸了,后面的逻辑全都不走了
+
+
+
+
+
+#### 返回到DispatcherServlet#doDispatch()
+
+
+
+
+
+#### DispatcherServlet#triggerAfterCompletion()抛异常
+
+```java
+private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
+ @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
+
+ if (mappedHandler != null) {
+ mappedHandler.triggerAfterCompletion(request, response, ex);
+ }
+ throw ex; //抛出去
+}
+```
+
+执行完拦截器,异常继续往上抛,一层一层往上抛,最终抛给了tomcat
+
+
+
+#### 页面效果
+
+
+
+这就是tomcat的默认错误页+堆栈页
+
+
+
+
+
+
+
+
+
+## 自定义异常处理
+
+
+
+### 测试类
+
+#### InvalidUserException
+
+```java
+@ResponseStatus(value = HttpStatus.CONFLICT, reason = "非法用户")
+public class InvalidUserException extends RuntimeException {
+
+ private static final long serialVersionUID = -7034897190745766222L;
+}
+
+```
+
+
+
+#### HelloController
+
+```java
+package cn.imlql.web.controller;
+
+@Controller
+public class HelloController {
+
+ public HelloController(){
+ System.out.println("HelloController.....");
+ }
+
+
+ @GetMapping("/hello")
+ public String sayHello(@RequestParam(name = "name",required = true) String name {
+ if ("abc".equals(name)) {
+ //非法的用户信息
+ throw new InvalidUserException();
+ }
+ return "index.jsp";
+ }
+
+}
+```
+
+### ResponseStatusExceptionResolver处理
+
+
+
+```java
+ protected ModelAndView doResolveException(
+ HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
+
+ try {
+ if (ex instanceof ResponseStatusException) {
+ return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
+ }
+ //拿到 ResponseStatus 注解
+ ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
+ if (status != null) {
+ return resolveResponseStatus(status, request, response, handler, ex);
+ }
+
+ if (ex.getCause() instanceof Exception) {
+ return doResolveException(request, response, handler, (Exception) ex.getCause());
+ }
+ }
+ catch (Exception resolveEx) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
+ }
+ }
+ return null;
+ }
+```
+
+
+
+
+
+
+
+
+
+```java
+ protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
+ HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
+ //获取注解指定的响应状态码和错误原因
+ int statusCode = responseStatus.code().value();
+ String reason = responseStatus.reason();
+ return applyStatusAndReason(statusCode, reason, response);
+ }
+```
+
+
+
+
+
+```java
+ protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
+ throws IOException {
+
+ if (!StringUtils.hasLength(reason)) {
+ response.sendError(statusCode); //返回默认错误页
+ }
+ else {
+ String resolvedReason = (this.messageSource != null ?
+ this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
+ reason);
+ response.sendError(statusCode, resolvedReason);
+ }
+ return new ModelAndView();
+ }
+```
+
+
+
+页面效果
+
+
+
+
+
+
+
+
+
+
+
+## 最常用的注解版异常解析器 @ExceptionHandler
+
+### 测试类
+
+#### MyExceptionHandler
+
+```java
+@ControllerAdvice //专门处理所有controller异常的,它是一个复合注解,里面有@Component,所以默认加在容器中
+public class MyExceptionHandler {
+
+ @ResponseBody
+ @ExceptionHandler(value = {ArithmeticException.class})
+ public String handleZeroException(Exception exception){
+ //参数位置 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args
+ //返回值 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-return-values
+ return "Error";
+ }
+}
+```
+
+异常处理器能写这么多参数和返回值,也是用了参数解析器,返回值处理器
+
+
+
+#### HelloController
+
+```java
+package cn.imlql.web.controller;
+
+@Controller
+public class HelloController {
+
+ public HelloController(){
+ System.out.println("HelloController.....");
+ }
+
+
+ @GetMapping("/hello")
+ public String sayHello(Integer i) {
+ int x = 10 / i;
+ return "index.jsp";
+ }
+
+}
+```
+
+
+
+### DispatcherServlet#doDispatch()
+
+
+
+### ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法
+
+```java
+ protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
+ HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
+ //为当前异常找一个处理方法??? @ExceptionHandler注解标注的方法
+ ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
+ if (exceptionHandlerMethod == null) {
+ return null;
+ }
+ //异常解析器里面 还是利用了以前的 argumentResolvers和 returnValueHandlers扩展了异常解析的功能
+ if (this.argumentResolvers != null) {
+ exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
+ }
+ if (this.returnValueHandlers != null) {
+ exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
+ }
+
+ ServletWebRequest webRequest = new ServletWebRequest(request, response);
+ ModelAndViewContainer mavContainer = new ModelAndViewContainer();
+
+ ArrayList
+
+### ExceptionHandlerMethodResolver#getMappedMethod()
+
+最终调到这里
+
+```java
+ private Method getMappedMethod(Class extends Throwable> exceptionType) {
+ List
+
+
+
+
+
+### 返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()
+
+
+
+
+
+
+
+
+
+### ServletInvocableHandlerMethod#invokeAndHandle()
+
+
+
+
+
+### 小总结
+
+1. 可以看到异常处理的方法和springmvc的普通Controller方法最终走到了相同的反射执行逻辑
+2. 这也是为什么叫@ControllerAdvice,仅仅是Controller的增强
+
+
+
+
+
+
+
+
+
+
+
+
+
+### ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值?
+
+#### ExceptionHandlerExceptionResolver#afterPropertiesSet()
+
+```java
+ @Override //实现了 InitilazingBean 的组件,在容器创建完对象以后,会初始化调用 InitilazingBean
+ public void afterPropertiesSet() {
+ //初始化@ExceptionHandler 增强的缓存 Do this first, it may add ResponseBodyAdvice beans
+ initExceptionHandlerAdviceCache();
+ //准备好异常解析用的参数解析器
+ if (this.argumentResolvers == null) {
+ List
+
+
+
+3. 我们最想要的结果就是既有我们自定义的组件,也有springmvc默认提供的组件。spring也提供了解决方法@EnableWebMvc+WebMvcConfigurer
+4. 同时@EnableWebMvc+WebMvcConfigurer也使得扩展自定义组件变的很方便
+
+### 测试类-MvcExtendConfiguration
+
+```java
+@EnableWebMvc //启用SpringMVC功能
+@Configuration //
+public class MvcExtendConfiguration implements WebMvcConfigurer {
+
+ public void configureViewResolvers(ViewResolverRegistry registry) {
+ registry.viewResolver(new MeiNvViewResolver());
+ //不改源码就如下操作
+ InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
+ viewResolver.setPrefix("");
+ viewResolver.setSuffix(".jsp"); //controller的返回值就不用写jsp
+ registry.viewResolver(viewResolver);
+ }
+
+}
+```
+
+WebMvcConfigurer就是给我们定制的,@EnableWebMvc就是启用默认的,下面讲讲原理
+
+
+
+### @EnableWebMvc+WebMvcConfigurer如何导入自定义组件
+
+#### 注解@EnableWebMvc
+
+```java
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Import(DelegatingWebMvcConfiguration.class)
+public @interface EnableWebMvc {
+}
+```
+
+#### DelegatingWebMvcConfiguration
+
+```java
+@Configuration(proxyBeanMethods = false)
+public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
+
+
+ //这里是个组合关系
+ private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
+
+
+ @Autowired(required = false) //拿到容器中所有的 WebMvcConfigurer
+ public void setConfigurers(List
+
+
+
+
+
+## 再来简单捋一下SPI如何启动的Web容器
+
+1. 我们看到上面很神奇的效果,我们自己写代码做到了类似SpringBoot的效果,不需要配置本地tomcat,直接就把Web应用启动起来了。
+2. 这里只是简单的捋一下,详细过程在前面讲过
+
+### META-INF/services
+
+
+
+
+
+
+
+
+
+
+
+### AbstractAnnotationConfigDispatcherServletInitializer继承树
+
+我们的QuickAppStarter实现了
+
+
+
+
+
+
+
+
+
+
+
+### SpringServletContainerInitializer
+
+利用Java的SPI加载META-INF/services下的实现类
+
+
+
+
+
+```java
+
+@HandlesTypes(WebApplicationInitializer.class)
+public class SpringServletContainerInitializer implements ServletContainerInitializer {
+
+ /**
+ * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
+ * implementations present on the application classpath.
+ * Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, + * Servlet 3.0+ containers will automatically scan the classpath for implementations + * of Spring's {@code WebApplicationInitializer} interface and provide the set of all + * such types to the {@code webAppInitializerClasses} parameter of this method. + *
If no {@code WebApplicationInitializer} implementations are found on the classpath, + * this method is effectively a no-op. An INFO-level log message will be issued notifying + * the user that the {@code ServletContainerInitializer} has indeed been invoked but that + * no {@code WebApplicationInitializer} implementations were found. + *
Assuming that one or more {@code WebApplicationInitializer} types are detected,
+ * they will be instantiated (and sorted if the @{@link
+ * org.springframework.core.annotation.Order @Order} annotation is present or
+ * the {@link org.springframework.core.Ordered Ordered} interface has been
+ * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
+ * method will be invoked on each instance, delegating the {@code ServletContext} such
+ * that each instance may register and configure servlets such as Spring's
+ * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
+ * or any other Servlet API componentry such as filters.
+ * @param webAppInitializerClasses all implementations of
+ * {@link WebApplicationInitializer} found on the application classpath
+ * @param servletContext the servlet context to be initialized
+ * @see WebApplicationInitializer#onStartup(ServletContext)
+ * @see AnnotationAwareOrderComparator
+ */
+ public void onStartup(@Nullable Set
+
+2. 也就是说是从Servlet最顶层开始一层一层往下面调用
+3. 最终我们发现FrameworkServlet里有一个核心方法
+
+
+
+### FrameworkServlet
+
+```java
+ /** 追踪看web应用启动做了什么。
+ * Overridden method of {@link HttpServletBean}, invoked after any bean properties
+ * have been set. Creates this servlet's WebApplicationContext.
+ */
+ @Override
+ protected final void initServletBean() throws ServletException {
+ getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
+ if (logger.isInfoEnabled()) {
+ logger.info("Initializing Servlet '" + getServletName() + "'");
+ }
+ long startTime = System.currentTimeMillis();
+
+ try {
+ this.webApplicationContext = initWebApplicationContext(); //初始化WebIOC容器,那我们想一下大概率是在这里启动的IOC容器
+ initFrameworkServlet(); //这又是留给子类的
+ }
+ catch (ServletException | RuntimeException ex) {
+ logger.error("Context initialization failed", ex);
+ throw ex;
+ }
+
+ if (logger.isDebugEnabled()) {
+ String value = this.enableLoggingRequestDetails ?
+ "shown which may lead to unsafe logging of potentially sensitive data" :
+ "masked to prevent unsafe logging of potentially sensitive data";
+ logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
+ "': request parameters and headers will be " + value);
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
+ }
+ }
+```
+
+
+
+1. `this.webApplicationContext = initWebApplicationContext();`没错,看名字就知道是从这里开始启动Web容器的。
+
+2. 然后我们就自己搭建了一个MySpringBoot项目,我们这个项目和SpringBoot官方的区别就是官方帮我们封装了很多自动配置类,帮我们给容器中放了很多组件,使得我们感觉开发更方便了。
+
+
+
+
+
+
+
+## SpringBoot启动导入了很多自动配置类
+
+为什么 @SpringBootApplication +SpringApplication.run(SpringbootSourceApplication.class, args);能把Spring+SpringMVC+Tomcat+其他场景都整合进来
+
+
+
+```java
+@SpringBootApplication
+public class SpringbootSourceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringbootSourceApplication.class, args);
+ }
+}
+```
+
+
+
+### pom.xml
+
+```java
+
+