一、SpringBoot对动态资源的映射规定

动态资源的映射规定都在WebMvcAutoConfiguration中。

(1)webjars:以jar包的形式引入动态资源

所有 /webjars/** ,都在 classpath:/META-INF/resources/webjars/ 查找资源。

<!--引入jquery-webjar-->在拜访的时候只须要写webjars上面资源的名称即可<dependency>    <groupId>org.webjars</groupId>    <artifactId>jquery</artifactId>    <version>3.5.0</version></dependency>

webjars:以jar包的形式引入动态资源

拜访localhost:8080/webjars/jquery/3.5.0/jquery.js

(2)/**拜访以后我的项目的任何资源,都去(动态资源的文件夹)找映射。

动态资源文件,比方一些JS、CSS、jQuery文件,SpringBoot默认是从以下这些门路中读取的:

-- "classpath:/META-INF/resources/", -- "classpath:/resources/",-- "classpath:/static/", -- "classpath:/public/" "/":以后我的项目的根门路

例如:

(3)首页(欢送页)

动态资源文件夹下的所有index.html页面,都被/**映射。

例如localhost:8080/会去动态资源文件下找index页面。

(4)自定义图标图标

把ico格局的图标放在默认动态资源文件门路下,并以favicon.ico命名,利用图标会主动变成指定的图标。所有的 **/favicon.ico 都在动态资源文件下查找。

(5)在application.properties中手动配置动态资源拜访门路。

在application.properties配置文件中如下编辑:

# 自定义动态资源拜访门路,能够指定多个,之间用逗号隔开spring.resources.static-locations=classpath:/test1/,classpath:/test2/

特地要留神:自定义动态资源拜访门路后,SpringBoot默认的动态资源门路将不再起作用

三、模板引擎

JSP、Velocity、Freemarker、Thymeleaf......

SpringBoot举荐的Thymeleaf:语法更简略,性能更弱小。

1、引入Thymeleaf

        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-thymeleaf</artifactId>        </dependency>        <!--发现默认用的2.3.4版本,需切换thymeleaf版本-->        <properties>            <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>            <!-- 布局性能的反对程序  thymeleaf3主程序  layout2以上版本 -->            <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>        </properties>

2、Thymeleaf应用

默认规定:

@ConfigurationProperties(prefix = "spring.thymeleaf")public class ThymeleafProperties {    private static final Charset DEFAULT_ENCODING;    public static final String DEFAULT_PREFIX = "classpath:/templates/";    public static final String DEFAULT_SUFFIX = ".html";    private boolean checkTemplate = true;    private boolean checkTemplateLocation = true;    private String prefix = "classpath:/templates/";    private String suffix = ".html";    private String mode = "HTML";

只有咱们把HTML页面放在classpath:/templates/下,thymeleaf就能主动渲染。

应用:

  1. 导入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  1. 应用thymeleaf语法
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head>    <meta charset="UTF-8">    <title>Title</title></head><body>    <h1>胜利!</h1>    <!-- th:text 将div外面的文本内容设置为指定的值 -->    <div th:text="${hello}">这是显示欢送信息</div></body></html>

3、语法规定

(1)th:text——扭转以后元素外面的文本内容

th:任意html属性——替换原生属性的值

(2)表达式

Simple expressions:(表达式语法)    Variable Expressions: ${...}:获取变量值;OGNL            1)获取对象的属性、调用办法            2)应用内置的根本对象                #ctx : the context object.                #vars: the context variables.                #locale : the context locale.                #request : (only in Web Contexts) the HttpServletRequest object.                #response : (only in Web Contexts) the HttpServletResponse object.                #session : (only in Web Contexts) the HttpSession object.                #servletContext : (only in Web Contexts) the ServletContext object.                                ${session.foo}            3)内置的一些工具对象:                #execInfo : information about the template being processed.                #messages : methods for obtaining externalized messages inside variables expressions, in the                  same way as they would be obtained using #{…} syntax.                #uris : methods for escaping parts of URLs/URIs                #conversions : methods for executing the configured conversion service (if any).                #dates : methods for java.util.Date objects: formatting, component extraction, etc.                #calendars : analogous to #dates , but for java.util.Calendar objects.                #numbers : methods for formatting numeric objects.                #strings : methods for String objects: contains, startsWith, prepending/appending, etc.                #objects : methods for objects in general.                #bools : methods for boolean evaluation.                #arrays : methods for arrays.                #lists : methods for lists.                #sets : methods for sets.                #maps : methods for maps.                #aggregates : methods for creating aggregates on arrays or collections.                #ids : methods for dealing with id attributes that might be repeated (for example, as a                     result of an iteration).Selection Variable Expressions: *{...}:抉择表达式,其实和${}在性能上是一样    补充性能:配合 th:object="${session.user}:    <div th:object="${session.user}">        <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>        <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>        <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>    </div>    Message Expressions: #{...}:获取国际化内容    Link URL Expressions: @{...}:定义URL        @{/order/process(execId=${execId},execType='FAST')}            Fragment Expressions: ~{...}:片段援用表达式        <div th:insert="~{commons :: main}">...</div>            Literals(字面量)      Text literals: 'one text' , 'Another one!' ,…      Number literals: 0 , 34 , 3.0 , 12.3 ,…      Boolean literals: true , false      Null literal: null      Literal tokens: one , sometext , main ,…      Text operations:(文本操作)    String concatenation: +    Literal substitutions: |The name is ${name}|    Arithmetic operations:(数学运算)    Binary operators: + , - , * , / , %    Minus sign (unary operator): -    Boolean operations:(布尔运算)    Binary operators: and , or    Boolean negation (unary operator): ! , not    Comparisons and equality:(比拟运算)    Comparators: > , < , >= , <= ( gt , lt , ge , le )    Equality operators: == , != ( eq , ne )    Conditional operators:条件运算(三元运算符)    If-then: (if) ? (then)    If-then-else: (if) ? (then) : (else)    Default: (value) ?: (defaultvalue)    Special tokens:    No-Operation: _ 

例如:

    @RequestMapping("/success")    public String success(Map<String,Object> map){        map.put("hello","<h1>你好</h1>");        map.put("users", Arrays.asList("zhangsan","lisi","wangwu"));        return "success";    }
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head>    <meta charset="UTF-8">    <title>Title</title></head><body><h1>胜利!</h1><div th:text="${hello}"></div><div th:utext="${hello}"></div><!-- th:each每次遍历都会生成以后这个标签: 3个h4 --><h4 th:text="${user}" th:each="user:${users}"></h4></hr><h4>    <span th:each="user:${users}">[[${user}]]</span></h4></body></html>

输入后果:

四、SpringMVC主动配置

1、Spring MVC auto-configuration

SpringBoot对SpringMVC的默认配置:(WebMvcAutoConfiguration)

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 主动配置了ViewResolver(视图解析器:依据办法的返回值失去视图对象(View),视图对象决定如何渲染(转发/重定向))
    • ContentNegotiatingViewResolver:组合所有的视图解析器。
    • ==如何定制:咱们能够本人给容器中增加一个视图解析器,主动的将其组合进来。==
  • Support for serving static resources, including support for WebJars (see below).动态资源文件夹门路,webjars。
  • Static index.html support. 动态首页拜访
  • Custom Favicon support (see below). favicon.ico
  • Automatic registration of Converter, GenericConverter, Formatter beans.

    • Converter转换器—— public String hello(User user):类型转换应用Converter
    • Formatter 格式化器, 2020.10.10/2020-10-10——>Date
        @Bean        @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规定        public Formatter<Date> dateFormatter() {            return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件        }

==增加的格式化器转换器,咱们只须要放在容器中即可。==

  • Support for HttpMessageConverters (see below).

    • HttpMessageConverter:SpringMVC用来转换Http申请和响应的,User对象---Json数据;
    • HttpMessageConverters 是从容器中确定,获取所有的HttpMessageConverter;

      ==给容器中增加HttpMessageConverter,只须要将本人的组件注册容器中(@Bean,@Component)==

  • Automatic registration of MessageCodesResolver (see below).定义谬误代码生成规定。
  • Automatic use of a ConfigurableWebBindingInitializer bean (see below).

    ==能够本人配置一个ConfigurableWebBindingInitializer来替换默认的(增加到容器)==,初始化WebDataBinder数据绑定器,将申请数据绑定到JavaBean中

org.springframework.boot.autoconfigure.web:web的所有主动场景

2、扩大SpringMVC

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

    <mvc:view-controller path="/hello" view-name="success"/>    <mvc:interceptors>        <mvc:interceptor>            <mvc:mapping path="/hello"/>            <bean></bean>        </mvc:interceptor>    </mvc:interceptors>

==编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型,不能标注@EnableWebMvc==

既保留了所有的主动配置,也能用咱们扩大的配置。

//应用WebMvcConfigurerAdapter能够来扩大SpringMVC的性能@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter {    @Override    public void addViewControllers(ViewControllerRegistry registry) {        //浏览器发送 /kai 申请来到 success        registry.addViewController("/kai").setViewName("success");    }}

原理:

1)WebMvcAutoConfiguration是SpringMVC的主动配置类。

2)在做其余主动配置时会导入,@Import(EnableWebMvcConfiguration.class)

    @Configuration    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {      private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();     //从容器中获取所有的WebMvcConfigurer      @Autowired(required = false)      public void setConfigurers(List<WebMvcConfigurer> configurers) {          if (!CollectionUtils.isEmpty(configurers)) {              this.configurers.addWebMvcConfigurers(configurers);                //一个参考实现;将所有的WebMvcConfigurer相干配置都来一起调用;                  @Override                 // public void addViewControllers(ViewControllerRegistry registry) {                  //    for (WebMvcConfigurer delegate : this.delegates) {                   //       delegate.addViewControllers(registry);                   //   }              }          }    }

3)容器中所有的WebMvcConfigurer都会一起起作用。

4)咱们的配置类也会被调用。

成果:SpringMVC的主动配置和咱们的扩大配置都会起作用。

3、全面接管SpringMVC

SpringBoot对SpringMVC的主动配置不须要了,所有都是咱们本人配置,所有的SpringMVC的主动配置都生效了。

在配置类中增加@EnableWebMvc即可

//应用WebMvcConfigurerAdapter能够来扩大SpringMVC的性能@EnableWebMvc@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter {    @Override    public void addViewControllers(ViewControllerRegistry registry) {       // super.addViewControllers(registry);        //浏览器发送 /kai 申请来到 success        registry.addViewController("/kai").setViewName("success");    }}

原理:

为什么@EnableWebMvc主动配置就生效了。

(1)@EnableWebMvc的外围

@Import(DelegatingWebMvcConfiguration.class)public @interface EnableWebMvc {
@Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
@Configuration@ConditionalOnWebApplication@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,        WebMvcConfigurerAdapter.class })//容器中没有这个组件的时候,主动配置类才失效@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,        ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration {

(2)可见,@EnableWebMvc将WebMvcConfigurationSupport组件导入进来,导入的WebMvcConfigurationSupport只是SpringMVC最根本的性能。

五、如何批改SpringBoot的默认配置

小结:

1. SpringBoot在主动配置很多组件的时候,先看容器中有没有用户本人配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才主动配置;如果有些组件能够有多个(ViewResolver)将用户配置的和本人默认的组合起来。

2. 在SpringBoot中会有十分多的xxxConfigurer帮忙咱们进行扩大配置。

3. 在SpringBoot中会有很多的xxxCustomizer帮忙咱们进行定制配置。

六、RestfulCRUD

1、默认拜访首页

//应用WebMvcConfigurerAdapter能够来扩大SpringMVC的性能//@EnableWebMvc  不要接管SpringMVC@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter {    @Override    public void addViewControllers(ViewControllerRegistry registry) {       // super.addViewControllers(registry);        //浏览器发送 /kai 申请来到 success        registry.addViewController("/kai").setViewName("success");    }    //所有的WebMvcConfigurerAdapter组件都会一起起作用    @Bean //将组件注册在容器    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {            @Override            public void addViewControllers(ViewControllerRegistry registry) {                registry.addViewController("/").setViewName("login");                registry.addViewController("/index.html").setViewName("login");            }        };        return adapter;    }}

2、国际化

1. 编写国际化配置文件

  1. 应用ResourceBundleMessageSource治理国际化资源文件
  2. 在页面应用fmt:message取出国际化内容

步骤:

(1)编写国际化配置文件,抽取页面须要显示的国际化音讯

(2)SpringBoot主动配置好了治理国际化资源文件的组件;

@ConfigurationProperties(prefix = "spring.messages")public class MessageSourceAutoConfiguration {        /**     * Comma-separated list of basenames (essentially a fully-qualified classpath     * location), each following the ResourceBundle convention with relaxed support for     * slash based locations. If it doesn't contain a package qualifier (such as     * "org.mypackage"), it will be resolved from the classpath root.     */    private String basename = "messages";      //咱们的配置文件能够间接放在类门路下叫messages.properties;        @Bean    public MessageSource messageSource() {        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();        if (StringUtils.hasText(this.basename)) {            //设置国际化资源文件的根底名(去掉语言国家代码的)            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(                    StringUtils.trimAllWhitespace(this.basename)));        }        if (this.encoding != null) {            messageSource.setDefaultEncoding(this.encoding.name());        }        messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);        messageSource.setCacheSeconds(this.cacheSeconds);        messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);        return messageSource;    }

(3)去页面获取国际化的值;

<!DOCTYPE html><html lang="en"  xmlns:th="http://www.thymeleaf.org">    <head>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">        <meta name="description" content="">        <meta name="author" content="">        <title>Signin Template for Bootstrap</title>        <!-- Bootstrap core CSS -->        <link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">        <!-- Custom styles for this template -->        <link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">    </head>    <body class="text-center">        <form class="form-signin" action="dashboard.html">            ![](@{/asserts/img/bootstrap-solid.svg})            <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>            <label class="sr-only" th:text="#{login.username}">Username</label>            <input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">            <label class="sr-only" th:text="#{login.password}">Password</label>            <input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">            <div class="checkbox mb-3">                <label>                  <input type="checkbox" value="remember-me"/> [[#{login.remember}]]        </label>            </div>            <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>            <p class="mt-5 mb-3 text-muted">© 2017-2018</p>            <a class="btn btn-sm">中文</a>            <a class="btn btn-sm">English</a>        </form>    </body></html>

成果:依据浏览器语言设置的信息切换了国际化。

原理:

国际化Locale(区域信息对象),LocaleResolver(获取区域信息对象)。

        @Bean        @ConditionalOnMissingBean        @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")        public LocaleResolver localeResolver() {            if (this.mvcProperties                    .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {                return new FixedLocaleResolver(this.mvcProperties.getLocale());            }            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();            localeResolver.setDefaultLocale(this.mvcProperties.getLocale());            return localeResolver;        }默认的就是依据申请头带来的区域信息获取Locale进行国际化

(4)点击链接切换国际化

/** * 能够在连贯上携带区域信息 */public class MyLocaleResolver implements LocaleResolver {        @Override    public Locale resolveLocale(HttpServletRequest request) {        String l = request.getParameter("l");        Locale locale = Locale.getDefault();        if(!StringUtils.isEmpty(l)){            String[] split = l.split("_");            locale = new Locale(split[0],split[1]);        }        return locale;    }    @Override    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {    }} @Bean    public LocaleResolver localeResolver(){        return new MyLocaleResolver();    }}

3、登陆

开发期间模板引擎页面批改当前,要实时失效。

(1)禁用模板引擎的缓存

# 禁用缓存spring.thymeleaf.cache=false 

(2)页面批改实现当前ctrl+f9:从新编译。

登陆谬误音讯的显示

<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

4、拦截器进行登陆查看

拦截器

/** * 登陆查看, */public class LoginHandlerInterceptor implements HandlerInterceptor {    //指标办法执行之前    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        Object user = request.getSession().getAttribute("loginUser");        if(user == null){            //未登陆,返回登陆页面            request.setAttribute("msg","没有权限请先登陆");            request.getRequestDispatcher("/index.html").forward(request,response);            return false;        }else{            //已登陆,放行申请            return true;        }    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {    }}

注册拦截器

  //所有的WebMvcConfigurerAdapter组件都会一起起作用    @Bean //将组件注册在容器    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {            @Override            public void addViewControllers(ViewControllerRegistry registry) {                registry.addViewController("/").setViewName("login");                registry.addViewController("/index.html").setViewName("login");                registry.addViewController("/main.html").setViewName("dashboard");            }            //注册拦截器            @Override            public void addInterceptors(InterceptorRegistry registry) {                //super.addInterceptors(registry);                //动态资源;  *.css , *.js                //SpringBoot曾经做好了动态资源映射                registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")                        .excludePathPatterns("/index.html","/","/user/login");            }        };        return adapter;    }

5、CRUD-员工列表

试验要求:

(1)RestfulCRUD:CRUD满足Rest格调;

URI:/资源名称/资源标识 HTTP申请形式辨别对资源CRUD操作

一般CRUD(URI来辨别操作)RestfulCRUD
查问getEmpemp---GET
增加addEmp?xxxemp---POST
批改updateEmp?id=xxx&xxx=xxemp/{id}---PUT
删除deleteEmp?id=1emp/{id}---DELETE

(2)试验的申请架构

试验性能申请URI申请形式
查问所有员工empsGET
查问某个员工(来到批改页面)emp/1GET
来到增加页面empGET
增加员工empPOST
来到批改页面(查出员工进行信息回显)emp/1GET
批改员工empPUT
删除员工emp/1DELETE

(3)员工列表

thymeleaf公共页面元素抽取

1、抽取公共片段<div th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery</div>2、引入公共片段<div th:insert="~{footer :: copy}"></div>~{templatename::selector}:模板名::选择器~{templatename::fragmentname}:模板名::片段名3、默认成果:insert的公共片段在div标签中如果应用th:insert等属性进行引入,能够不必写~{}:行内写法能够加上:[[~{}]];[(~{})];

三种引入公共片段的th属性:

th:insert:将公共片段整个插入到申明引入的元素中

th:replace:将申明引入的元素替换为公共片段

th:include:将被引入的片段的内容蕴含进这个标签中

<footer th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery</footer>引入形式<div th:insert="footer :: copy"></div><div th:replace="footer :: copy"></div><div th:include="footer :: copy"></div>成果别离为<div>    <footer>    &copy; 2011 The Good Thymes Virtual Grocery    </footer></div><footer>    &copy; 2011 The Good Thymes Virtual Grocery</footer><div>    &copy; 2011 The Good Thymes Virtual Grocery</div>

引入片段的时候传入参数:

<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">    <div class="sidebar-sticky">        <ul class="nav flex-column">            <li class="nav-item">                <a class="nav-link active"                   th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"                   href="#" th:href="@{/main.html}">                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>                        <polyline points="9 22 9 12 15 12 15 22"></polyline>                    </svg>                    Dashboard <span class="sr-only">(current)</span>                </a>            </li><!--引入侧边栏;传入参数--><div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>

6、CRUD-员工增加

增加页面

<form>    <div class="form-group">        <label>LastName</label>        <input type="text" class="form-control" placeholder="zhangsan">    </div>    <div class="form-group">        <label>Email</label>        <input type="email" class="form-control" placeholder="zhangsan@kai.com">    </div>    <div class="form-group">        <label>Gender</label><br/>        <div class="form-check form-check-inline">            <input class="form-check-input" type="radio" name="gender"  value="1">            <label class="form-check-label">男</label>        </div>        <div class="form-check form-check-inline">            <input class="form-check-input" type="radio" name="gender"  value="0">            <label class="form-check-label">女</label>        </div>    </div>    <div class="form-group">        <label>department</label>        <select class="form-control">            <option>1</option>            <option>2</option>            <option>3</option>            <option>4</option>            <option>5</option>        </select>    </div>    <div class="form-group">        <label>Birth</label>        <input type="text" class="form-control" placeholder="zhangsan">    </div>    <button type="submit" class="btn btn-primary">增加</button></form>

提交的数据格式不对:生日:日期。

2017-12-12;2017/12/12;2017.12.12。

日期的格式化;SpringMVC将页面提交的值须要转换为指定的类型。

2017-12-12---Date; 类型转换,格式化。

默认日期是依照/的形式。

7、CRUD-员工批改

批改增加二合一表单

<!--须要辨别是员工批改还是增加;--><form th:action="@{/emp}" method="post">    <!--发送put申请批改员工数据-->    <!--1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot主动配置好的)2、页面创立一个post表单3、创立一个input项,name="_method";值就是咱们指定的申请形式-->    <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>    <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">    <div class="form-group">        <label>LastName</label>        <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">    </div>    <div class="form-group">        <label>Email</label>        <input name="email" type="email" class="form-control" placeholder="zhangsan@kai.com" th:value="${emp!=null}?${emp.email}">    </div>    <div class="form-group">        <label>Gender</label><br/>        <div class="form-check form-check-inline">            <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">            <label class="form-check-label">男</label>        </div>        <div class="form-check form-check-inline">            <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">            <label class="form-check-label">女</label>        </div>    </div>    <div class="form-group">        <label>department</label>        <!--提交的是部门的id-->        <select class="form-control" name="department.id">            <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>        </select>    </div>    <div class="form-group">        <label>Birth</label>        <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">    </div>    <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'批改':'增加'">增加</button></form>

8、CRUD-员工删除

<tr th:each="emp:${emps}">    <td th:text="${emp.id}"></td>    <td>[[${emp.lastName}]]</td>    <td th:text="${emp.email}"></td>    <td th:text="${emp.gender}==0?'女':'男'"></td>    <td th:text="${emp.department.departmentName}"></td>    <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>    <td>        <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>        <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>    </td></tr><script>    $(".deleteBtn").click(function(){        //删除以后员工的        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();        return false;    });</script>

七、错误处理机制

1、SpringBoot默认的错误处理机制

默认成果:

1. 浏览器,返回一个默认的谬误页面。

浏览器发送申请的申请头:

2. 如果是其余客户端,默认响应一个json数据

原理:

能够参照ErrorMvcAutoConfiguration,错误处理的主动配置;

  给容器中增加了以下组件

(1)DefaultErrorAttributes:

帮咱们在页面共享信息;@Override    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,            boolean includeStackTrace) {        Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();        errorAttributes.put("timestamp", new Date());        addStatus(errorAttributes, requestAttributes);        addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);        addPath(errorAttributes, requestAttributes);        return errorAttributes;    }

(2)BasicErrorController:解决默认/error申请

@Controller@RequestMapping("${server.error.path:${error.path:/error}}")public class BasicErrorController extends AbstractErrorController {        @RequestMapping(produces = "text/html")//产生html类型的数据;浏览器发送的申请来到这个办法解决    public ModelAndView errorHtml(HttpServletRequest request,            HttpServletResponse response) {        HttpStatus status = getStatus(request);        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));        response.setStatus(status.value());                //去哪个页面作为谬误页面;蕴含页面地址和页面内容        ModelAndView modelAndView = resolveErrorView(request, response, status, model);        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);    }    @RequestMapping    @ResponseBody    //产生json数据,其余客户端来到这个办法解决;    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {        Map<String, Object> body = getErrorAttributes(request,                isIncludeStackTrace(request, MediaType.ALL));        HttpStatus status = getStatus(request);        return new ResponseEntity<Map<String, Object>>(body, status);    }

(3)ErrorPageCustomizer:

    @Value("${error.path:/error}")    private String path = "/error";  零碎呈现谬误当前来到error申请进行解决;(web.xml注册的谬误页面规定)

(4)DefaultErrorViewResolver:

@Override    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,            Map<String, Object> model) {        ModelAndView modelAndView = resolve(String.valueOf(status), model);        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);        }        return modelAndView;    }    private ModelAndView resolve(String viewName, Map<String, Object> model) {        //默认SpringBoot能够去找到一个页面?  error/404        String errorViewName = "error/" + viewName;                //模板引擎能够解析这个页面地址就用模板引擎解析        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders                .getProvider(errorViewName, this.applicationContext);        if (provider != null) {            //模板引擎可用的状况下返回到errorViewName指定的视图地址            return new ModelAndView(errorViewName, model);        }        //模板引擎不可用,就在动态资源文件夹下找errorViewName对应的页面   error/404.html        return resolveResource(errorViewName, model);    }

步骤:

一但零碎呈现4xx或者5xx之类的谬误,ErrorPageCustomizer就会失效s(定制谬误的响应规定),就会来到/error申请,就会被BasicErrorController解决。

(1)响应页面,去哪个页面是由DefaultErrorViewResolver解析失去的:

protected ModelAndView resolveErrorView(HttpServletRequest request,      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {    //所有的ErrorViewResolver失去ModelAndView   for (ErrorViewResolver resolver : this.errorViewResolvers) {      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);      if (modelAndView != null) {         return modelAndView;      }   }   return null;}

2、定制谬误响应

2.1 定制谬误的页面

(1)有模板引擎的状况下:error/状态码【将谬误页面命名为 谬误状态码.html 放在模板引擎文件夹外面的 error文件夹下】,产生此状态码的谬误就会来到 对应的页面。

咱们能够应用4xx和5xx作为谬误页面的文件名来匹配这种类型的所有谬误,准确优先(优先寻找准确的状态码.html)。

页面能获取的信息;

timestamp:工夫戳

status:状态码

error:谬误提醒

exception:异样对象

message:异样音讯

errors:JSR303数据校验的谬误都在这里

(2)没有模板引擎(模板引擎找不到这个谬误页面),动态资源文件夹下找;

(3)以上都没有谬误页面,就是默认来到SpringBoot默认的谬误提醒页面;

2.2 定制谬误的json数据

(1)自定义异样解决&返回定制json数据;

@ControllerAdvicepublic class MyExceptionHandler {    @ResponseBody    @ExceptionHandler(UserNotExistException.class)    public Map<String,Object> handleException(Exception e){        Map<String,Object> map = new HashMap<>();        map.put("code","user.notexist");        map.put("message",e.getMessage());        return map;    }}//没有自适应成果...

(2)转发到/error进行自适应响应成果解决

 @ExceptionHandler(UserNotExistException.class)    public String handleException(Exception e, HttpServletRequest request){        Map<String,Object> map = new HashMap<>();        //传入咱们本人的谬误状态码  4xx 5xx,否则就不会进入定制谬误页面的解析流程        /**         * Integer statusCode = (Integer) request         .getAttribute("javax.servlet.error.status_code");         */        request.setAttribute("javax.servlet.error.status_code",500);        map.put("code","user.notexist");        map.put("message",e.getMessage());        //转发到/error        return "forward:/error";    }

2.3 将定制数据携带进来

呈现谬误当前,会来到/error申请,会被BasicErrorController解决,响应进来能够获取的数据是由getErrorAttributes失去的(是AbstractErrorController(ErrorController)规定的办法);

1、齐全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中。

2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes失去。

容器中DefaultErrorAttributes.getErrorAttributes():默认进行数据处理的。

自定义ErrorAttributes

//给容器中退出咱们本人定义的ErrorAttributes@Componentpublic class MyErrorAttributes extends DefaultErrorAttributes {    @Override    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);        map.put("company","kai");        return map;    }}

最终的成果:响应是自适应的,能够通过定制ErrorAttributes扭转须要返回的内容。

八、配置嵌入式Servlet容器

SpringBoot默认应用Tomcat作为嵌入式的Servlet容器。

1、定制和批改Servlet容器的相干配置

(1)批改和server无关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】

server.port=8081server.context-path=/crudserver.tomcat.uri-encoding=UTF-8//通用的Servlet容器设置server.xxx//Tomcat的设置server.tomcat.xxx

(2)编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器,来批改Servlet容器的配置

@Bean  //肯定要将这个定制器退出到容器中public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){    return new EmbeddedServletContainerCustomizer() {        //定制嵌入式的Servlet容器相干的规定        @Override        public void customize(ConfigurableEmbeddedServletContainer container) {            container.setPort(8083);        }    };}

2、注册Servlet三大组件【Servlet、Filter、Listener】

因为SpringBoot默认是以jar包的形式启动嵌入式的Servlet容器来启动SpringBoot的web利用,没有web.xml文件。

注册三大组件用以ServletRegistrationBean形式

//注册三大组件@Beanpublic ServletRegistrationBean myServlet(){    ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");    return registrationBean;}

FilterRegistrationBean

@Beanpublic FilterRegistrationBean myFilter(){    FilterRegistrationBean registrationBean = new FilterRegistrationBean();    registrationBean.setFilter(new MyFilter());    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));    return registrationBean;}

ServletListenerRegistrationBean

@Beanpublic ServletListenerRegistrationBean myListener(){    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());    return registrationBean;}

SpringBoot帮咱们主动SpringMVC的时候,主动的注册SpringMVC的前端控制器:DIspatcherServlet。

DispatcherServletAutoConfiguration中:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public ServletRegistrationBean dispatcherServletRegistration(      DispatcherServlet dispatcherServlet) {   ServletRegistrationBean registration = new ServletRegistrationBean(         dispatcherServlet, this.serverProperties.getServletMapping());    //默认拦挡:所有申请;包动态资源,然而不拦挡jsp申请;   /*会拦挡jsp    //能够通过server.servletPath来批改SpringMVC前端控制器默认拦挡的申请门路       registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);   registration.setLoadOnStartup(         this.webMvcProperties.getServlet().getLoadOnStartup());   if (this.multipartConfig != null) {      registration.setMultipartConfig(this.multipartConfig);   }   return registration;}

3、替换为其余嵌入式Servlet容器

默认反对:

Tomcat(默认应用)

<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-web</artifactId>   引入web模块默认就是应用嵌入式的Tomcat作为Servlet容器;</dependency>

Jetty

<!-- 引入web模块 --><dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-web</artifactId>   <exclusions>      <exclusion>         <artifactId>spring-boot-starter-tomcat</artifactId>         <groupId>org.springframework.boot</groupId>      </exclusion>   </exclusions></dependency><!--引入其余的Servlet容器--><dependency>   <artifactId>spring-boot-starter-jetty</artifactId>   <groupId>org.springframework.boot</groupId></dependency>

Undertow

<!-- 引入web模块 --><dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-web</artifactId>   <exclusions>      <exclusion>         <artifactId>spring-boot-starter-tomcat</artifactId>         <groupId>org.springframework.boot</groupId>      </exclusion>   </exclusions></dependency><!--引入其余的Servlet容器--><dependency>   <artifactId>spring-boot-starter-undertow</artifactId>   <groupId>org.springframework.boot</groupId></dependency>

4、嵌入式Servlet容器主动配置原理

EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器主动配置?

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration@ConditionalOnWebApplication@Import(BeanPostProcessorsRegistrar.class)//导入BeanPostProcessorsRegistrar:Spring注解版;给容器中导入一些组件//导入了EmbeddedServletContainerCustomizerBeanPostProcessor://后置处理器:bean初始化前后(创立完对象,还没赋值赋值)执行初始化工作public class EmbeddedServletContainerAutoConfiguration {        @Configuration    @ConditionalOnClass({ Servlet.class, Tomcat.class })//判断以后是否引入了Tomcat依赖;    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)//判断以后容器没有用户本人定义EmbeddedServletContainerFactory:嵌入式的Servlet容器工厂;作用:创立嵌入式的Servlet容器    public static class EmbeddedTomcat {        @Bean        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {            return new TomcatEmbeddedServletContainerFactory();        }    }        /**     * Nested configuration if Jetty is being used.     */    @Configuration    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,            WebAppContext.class })    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)    public static class EmbeddedJetty {        @Bean        public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {            return new JettyEmbeddedServletContainerFactory();        }    }    /**     * Nested configuration if Undertow is being used.     */    @Configuration    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)    public static class EmbeddedUndertow {        @Bean        public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {            return new UndertowEmbeddedServletContainerFactory();        }    }

1)EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)

public interface EmbeddedServletContainerFactory {   //获取嵌入式的Servlet容器   EmbeddedServletContainer getEmbeddedServletContainer(         ServletContextInitializer... initializers);}

2)EmbeddedServletContainer:(嵌入式的Servlet容器)

3)以TomcatEmbeddedServletContainerFactory为例

@Overridepublic EmbeddedServletContainer getEmbeddedServletContainer(      ServletContextInitializer... initializers) {    //创立一个Tomcat   Tomcat tomcat = new Tomcat();        //配置Tomcat的根本环节   File baseDir = (this.baseDirectory != null ? this.baseDirectory         : createTempDir("tomcat"));   tomcat.setBaseDir(baseDir.getAbsolutePath());   Connector connector = new Connector(this.protocol);   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);        //将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器   return getTomcatEmbeddedServletContainer(tomcat);}

4)咱们对嵌入式容器的配置批改是怎么失效?

ServerProperties、EmbeddedServletContainerCustomizer

EmbeddedServletContainerCustomizer:定制器帮咱们批改了Servlet容器的配置?

怎么批改的原理?

5)容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor

//初始化之前@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)      throws BeansException {    //如果以后初始化的是一个ConfigurableEmbeddedServletContainer类型的组件   if (bean instanceof ConfigurableEmbeddedServletContainer) {       //      postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);   }   return bean;}private void postProcessBeforeInitialization(            ConfigurableEmbeddedServletContainer bean) {    //获取所有的定制器,调用每一个定制器的customize办法来给Servlet容器进行属性赋值;    for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {        customizer.customize(bean);    }}private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {    if (this.customizers == null) {        // Look up does not include the parent context        this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(            this.beanFactory            //从容器中获取所有这葛类型的组件:EmbeddedServletContainerCustomizer            //定制Servlet容器,给容器中能够增加一个EmbeddedServletContainerCustomizer类型的组件            .getBeansOfType(EmbeddedServletContainerCustomizer.class,                            false, false)            .values());        Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);        this.customizers = Collections.unmodifiableList(this.customizers);    }    return this.customizers;}ServerProperties也是定制器

步骤:

  • SpringBoot依据导入的依赖状况,给容器中增加相应的EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
  • 容器中某个组件要创建对象就会轰动后置处理器;EmbeddedServletContainerCustomizerBeanPostProcessor;

只有是嵌入式的Servlet容器工厂,后置处理器就工作

  • 后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制办法

5、嵌入式Servlet容器启动原理

什么时候创立嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat?

获取嵌入式的Servlet容器工厂:

1)SpringBoot利用启动运行run办法。

2)refreshContext(context),SpringBoot刷新IOC容器【创立IOC容器对象,并初始化容器,创立容器中的每一个组件】,如果是web利用创立AnnotationConfigEmbeddedWebApplicationContext,否则:AnnotationConfigApplicationContext

3)refresh(context);刷新方才创立好的ioc容器

public void refresh() throws BeansException, IllegalStateException {   synchronized (this.startupShutdownMonitor) {      // Prepare this context for refreshing.      prepareRefresh();      // Tell the subclass to refresh the internal bean factory.      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();      // Prepare the bean factory for use in this context.      prepareBeanFactory(beanFactory);      try {         // Allows post-processing of the bean factory in context subclasses.         postProcessBeanFactory(beanFactory);         // Invoke factory processors registered as beans in the context.         invokeBeanFactoryPostProcessors(beanFactory);         // Register bean processors that intercept bean creation.         registerBeanPostProcessors(beanFactory);         // Initialize message source for this context.         initMessageSource();         // Initialize event multicaster for this context.         initApplicationEventMulticaster();         // Initialize other special beans in specific context subclasses.         onRefresh();         // Check for listener beans and register them.         registerListeners();         // Instantiate all remaining (non-lazy-init) singletons.         finishBeanFactoryInitialization(beanFactory);         // Last step: publish corresponding event.         finishRefresh();      }      catch (BeansException ex) {         if (logger.isWarnEnabled()) {            logger.warn("Exception encountered during context initialization - " +                  "cancelling refresh attempt: " + ex);         }         // Destroy already created singletons to avoid dangling resources.         destroyBeans();         // Reset 'active' flag.         cancelRefresh(ex);         // Propagate exception to caller.         throw ex;      }      finally {         // Reset common introspection caches in Spring's core, since we         // might not ever need metadata for singleton beans anymore...         resetCommonCaches();      }   }}

4)onRefresh();:web的ioc容器重写了onRefresh办法

5)webioc容器会创立嵌入式的Servlet容器;createEmbeddedServletContainer();

6)获取嵌入式的Servlet容器工厂:

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

从ioc容器中获取EmbeddedServletContainerFactory 组,TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相干配置;

7)应用容器工厂获取嵌入式的Servlet容器**:this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());

8)嵌入式的Servlet容器创建对象并启动Servlet容器。

先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创立出的对象获取进去

==IOC容器启动创立嵌入式的Servlet容器==

九、应用外置的Servlet容器

嵌入式Servlet容器:利用打成可执行的jar

长处:简略、便携;

毛病:默认不反对JSP、优化定制比较复杂(应用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,本人编写嵌入式Servlet容器的创立工厂【EmbeddedServletContainerFactory】)。

外置的Servlet容器:里面装置Tomcat---利用war包的形式打包

步骤

1)必须创立一个war我的项目;(利用idea创立好目录构造)

2)将嵌入式的Tomcat指定为provided;

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

3)必须编写一个SpringBootServletInitializer的子类,并调用configure办法

public class ServletInitializer extends SpringBootServletInitializer {   @Override   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {       //传入SpringBoot利用的主程序      return application.sources(SpringBoot04WebJspApplication.class);   }}

4)启动服务器就能够应用。

原理

jar包:执行SpringBoot主类的main办法,启动ioc容器,创立嵌入式的Servlet容器;

war包:启动服务器,服务器启动SpringBoot利用【SpringBootServletInitializer】,启动ioc容器;

servlet3.0(Spring注解版):

8.2.4 Shared libraries / runtimes pluggability:

规定:

1)服务器启动(web利用启动)会创立以后web利用外面每一个jar包外面ServletContainerInitializer实例;

2)ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名;

3)还能够应用@HandlesTypes,在利用启动的时候加载咱们感兴趣的类。

流程:

1)启动Tomcat

2)orgspringframeworkspring-web4.3.14.RELEASEspring-web-4.3.14.RELEASE.jar!META-INFservicesjavax.servlet.ServletContainerInitializer:

Spring的web模块外面有这个文件:org.springframework.web.SpringServletContainerInitializer

3)SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup办法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创立实例;

4)每一个WebApplicationInitializer都调用本人的onStartup;

5)相当于咱们的SpringBootServletInitializer的类会被创建对象,并执行onStartup办法

6)SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创立容器

protected WebApplicationContext createRootApplicationContext(      ServletContext servletContext) {    //1、创立SpringApplicationBuilder   SpringApplicationBuilder builder = createSpringApplicationBuilder();   StandardServletEnvironment environment = new StandardServletEnvironment();   environment.initPropertySources(servletContext, null);   builder.environment(environment);   builder.main(getClass());   ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);   if (parent != null) {      this.logger.info("Root context already created (using as parent).");      servletContext.setAttribute(            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);      builder.initializers(new ParentContextApplicationContextInitializer(parent));   }   builder.initializers(         new ServletContextApplicationContextInitializer(servletContext));   builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);        //调用configure办法,子类重写了这个办法,将SpringBoot的主程序类传入了进来   builder = configure(builder);        //应用builder创立一个Spring利用   SpringApplication application = builder.build();   if (application.getSources().isEmpty() && AnnotationUtils         .findAnnotation(getClass(), Configuration.class) != null) {      application.getSources().add(getClass());   }   Assert.state(!application.getSources().isEmpty(),         "No SpringApplication sources have been defined. Either override the "               + "configure method or add an @Configuration annotation");   // Ensure error pages are registered   if (this.registerErrorPageFilter) {      application.getSources().add(ErrorPageFilterConfiguration.class);   }    //启动Spring利用   return run(application);}

7)Spring的利用就启动并且创立IOC容器

public ConfigurableApplicationContext run(String... args) {   StopWatch stopWatch = new StopWatch();   stopWatch.start();   ConfigurableApplicationContext context = null;   FailureAnalyzers analyzers = null;   configureHeadlessProperty();   SpringApplicationRunListeners listeners = getRunListeners(args);   listeners.starting();   try {      ApplicationArguments applicationArguments = new DefaultApplicationArguments(            args);      ConfigurableEnvironment environment = prepareEnvironment(listeners,            applicationArguments);      Banner printedBanner = printBanner(environment);      context = createApplicationContext();      analyzers = new FailureAnalyzers(context);      prepareContext(context, environment, listeners, applicationArguments,            printedBanner);              //刷新IOC容器      refreshContext(context);      afterRefresh(context, applicationArguments);      listeners.finished(context, null);      stopWatch.stop();      if (this.logStartupInfo) {         new StartupInfoLogger(this.mainApplicationClass)               .logStarted(getApplicationLog(), stopWatch);      }      return context;   }   catch (Throwable ex) {      handleRunFailure(context, listeners, analyzers, ex);      throw new IllegalStateException(ex);   }}

==启动Servlet容器,再启动SpringBoot利用==

参考

视频教程

webjars官网

Thymeleaf官网文档

Spring Web MVC Framework