一、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就能主动渲染。
应用:
- 导入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
- 应用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
andBeanNameViewResolver
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):类型转换应用ConverterFormatter
格式化器, 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. 编写国际化配置文件
- 应用ResourceBundleMessageSource治理国际化资源文件
- 在页面应用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">  <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 | |
---|---|---|
查问 | getEmp | emp---GET |
增加 | addEmp?xxx | emp---POST |
批改 | updateEmp?id=xxx&xxx=xx | emp/{id}---PUT |
删除 | deleteEmp?id=1 | emp/{id}---DELETE |
(2)试验的申请架构
试验性能 | 申请URI | 申请形式 |
---|---|---|
查问所有员工 | emps | GET |
查问某个员工(来到批改页面) | emp/1 | GET |
来到增加页面 | emp | GET |
增加员工 | emp | POST |
来到批改页面(查出员工进行信息回显) | emp/1 | GET |
批改员工 | emp | PUT |
删除员工 | emp/1 | DELETE |
(3)员工列表
thymeleaf公共页面元素抽取
1、抽取公共片段<div th:fragment="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">© 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> © 2011 The Good Thymes Virtual Grocery </footer></div><footer> © 2011 The Good Thymes Virtual Grocery</footer><div> © 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