1.创建一个web模块

(1).创建SpringBoot应用,选中我们需要的模块;

(2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来

(3).自己编写业务代码;

自动配置原理

xxxxAutoConfiguration:帮我们给容器中自动配置组件;

xxxxProperties:配置类来封装配置文件的内容;

2.SpringBoott对静态资源的映射规则

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)

public class ResourceProperties implements ResourceLoaderAware {

//可以设置和静态资源有关的参数,缓存时间等

WebMvcAuotConfiguration:

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

if (!this.resourceProperties.isAddMappings()) {

logger.debug("Default resource handling disabled");

return;

}

Integer cachePeriod = this.resourceProperties.getCachePeriod();

if (!registry.hasMappingForPattern("/webjars/**")) {

customizeResourceHandlerRegistration(

registry.addResourceHandler("/webjars/**")

.addResourceLocations(

"classpath:/META-INF/resources/webjars/")

.setCachePeriod(cachePeriod));

}

String staticPathPattern = this.mvcProperties.getStaticPathPattern();

//静态资源文件夹映射

if (!registry.hasMappingForPattern(staticPathPattern)) {

customizeResourceHandlerRegistration(

registry.addResourceHandler(staticPathPattern)

.addResourceLocations(

this.resourceProperties.getStaticLocations())

.setCachePeriod(cachePeriod));

}

}

//配置index映射

@Bean

public WelcomePageHandlerMapping welcomePageHandlerMapping(

ResourceProperties resourceProperties) {

return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),

this.mvcProperties.getStaticPathPattern());

}

//配置图标

@Configuration

@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)

public static class FaviconConfiguration {

private final ResourceProperties resourceProperties;

public FaviconConfiguration(ResourceProperties resourceProperties) {

this.resourceProperties = resourceProperties;

}

@Bean

public SimpleUrlHandlerMapping faviconHandlerMapping() {

SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();

mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);

//所有  **/favicon.ico

mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",

faviconRequestHandler()));

return mapping;

}

@Bean

public ResourceHttpRequestHandler faviconRequestHandler() {

ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();

requestHandler

.setLocations(this.resourceProperties.getFaviconLocations());

return requestHandler;

}

}

(1). 所有/webjars/**,都去 classpath:/META-INF/resources/webjars/ 找资源;webjars:以jar包的方式引入静态资源

http://www.webjars.org/

<!--引入jquery-webjar 在访问的时候只需要写webjars下面资源的名称即可 -->

<dependency>

<groupId>org.webjars</groupId>

<artifactId>jquery</artifactId>

<version>3.3.1</version>

</dependency>

访问:localhost:8080/webjars/jquery/3.3.1/jquery.js

(2)."/**" 访问当前项目的任何资源,到'静态资源文件夹'找映射

"classpath:/META-INF/resources/",

"classpath:/resources/",

"classpath:/static/",

"classpath:/public/"

"/":当前项目的根路径

访问:http://localhost:8080/asserts/js/Chart.min.js

localhost:8080/abc ---> 去静态资源文件夹里面找abc

(3).index页; 静态资源文件夹下的所有index.html页面;被"/**"映射;

访问:localhost:8080/

(4). 图标**/favicon.ico都在静态资源文件下找

3.模板引擎

JSP、Velocity、Freemarker、Thymeleaf

SpringBoot推荐使用的Thymeleaf,语法更简单,功能更强大.

(1).引入thymeleaf

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>

<!--2.1.6-->

</dependency>

切换thymeleaf版本

<properties>

<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>

<!-- 布局功能的支持程序  thymeleaf3主程序  layout2以上版本 -->

<!-- thymeleaf2   layout1-->

<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 = Charset.forName("UTF-8");

private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");

public static final String DEFAULT_PREFIX = "classpath:/templates/";

public static final String DEFAULT_SUFFIX = ".html";

//

把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;

开发文档

[1].添加名称空间

xmlns:th="http://www.thymeleaf.org"

[2].使用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).thymeleaf语法

[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: _

4.SpringMVC自动配置

官方文档

(1).Spring MVC auto-configuration

Spring Boot 自动配置SpringMVC

SpringBoot对SpringMVC的默认配置(WebMvcAutoConfiguration)

Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))

ContentNegotiatingViewResolver:组合所有的视图解析器的

如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;

Support for serving static resources, including support for WebJars (see below).

静态资源文件夹路径,webjars

Automatic registration of Converter, GenericConverter, Formatter beans.

Converter:转换器;  public String hello(User user):类型转换使用Converter

Formatter格式化器;  2017.12.17===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).

定义错误代码生成规则

Static index.html support.

静态首页访问

Custom Favicon support (see below).

自定义favicon.ico 图标

Automatic use of a ConfigurableWebBindingInitializer bean (see below).

我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)

初始化WebDataBinder;

请求数据=====JavaBean;

org.springframework.boot.autoconfigure.web:web所有自动场景;

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.

(2).扩展SpringMVC

<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;

在保留所有的自动配置情况下,也能用我们扩展的配置

//使用 WebMvcConfigurer可以来扩展SpringMVC功能

//因为WebMvcConfigurerAdapter已经过时,所以我们使用接口WebMvcConfigurer替代

@Configuration

public class MyMvcConfig  implements WebMvcConfigurer {

@Override

public void addViewControllers(ViewControllerRegistry registry) {

//super.addViewControllers(registry)

        //浏览器发送/pluto请求来到success

        registry.addViewController("/pluto").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即可;

//使用 WebMvcConfigurer可以来扩展SpringMVC功能

//因为WebMvcConfigurerAdapter已经过时,所以我们使用接口WebMvcConfigurer替代

@EnableWebMvc

@Configuration

public class MyMvcConfig  implements WebMvcConfigurer {

@Override

public void addViewControllers(ViewControllerRegistry registry) {

//super.addViewControllers(registry)

//浏览器发送/pluto请求来到success

registry.addViewController("/pluto").setViewName("success");

}

}

原理解析:@EnableWebMvc自动配置失效

[1].@EnableWebMvc的核心

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE})

@Documented

@Import({DelegatingWebMvcConfiguration.class})

public @interface EnableWebMvc {

}

[2].DelegatingWebMvcConfiguration

@Configuration(

proxyBeanMethods = false

)

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

[3].WebMvcAutoConfiguration

@Configuration(

proxyBeanMethods = false

)

@ConditionalOnWebApplication(

type = Type.SERVLET

)

@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})

//容器中没有这个组件的时候,这个自动配置类才生效

@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

@AutoConfigureOrder(-2147483638)

@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})

public class WebMvcAutoConfiguration {

[4].@EnableWebMvc将WebMvcConfigurationSupport组件导入进来

[5].导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能

5.修改SpringBoot的默认配置

模式:

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

(2).在SpringBoot中有非常多的xxxConfigurer帮助我们进行扩展配置

(3).在SpringBoot中v  有很多的xxxCustomizer帮助我们进行定制配置

6.RestfulCRUD

(1).访问首页(默认)

//使用 WebMvcConfigurer可以来扩展SpringMVC功能

//因为WebMvcConfigurerAdapter已经过时,所以我们使用接口WebMvcConfigurer替代

//@EnableWebMvc

@Configuration

public class MyMvcConfig implements WebMvcConfigurer {

@Override

public void addViewControllers(ViewControllerRegistry registry) {

//super.addViewControllers(registry)

        //浏览器发送/pluto请求来到success

        registry.addViewController("/pluto").setViewName("success");

}

//所有的WebMvcConfigurer组件会一起起作用

    //@Bean将组件注册在容器中

    @Bean

public WebMvcConfigurer webMvcConfigurer(){

WebMvcConfigurer  dapter = new WebMvcConfigurer() {

@Override

public void addViewControllers(ViewControllerRegistry registry) {

registry.addViewController("/").setViewName("login");

registry.addViewController("/index.html").setViewName("login");

}

};

return dapter;

}

}

(2).国际化

[1].编写国际化配置文件

[2].使用ResourceBundleMessageSource管理国际化资源文件

[3].在页面使用fmt:message取出国际化内容

实现步骤:

[1].编写国际化配置文件

#login_zh_CN.properties

login.btn=登录

login.password=密码

login.remember=记住我

login.tip=请登录

login.username=用户名

#login_en_US.properties

login.btn=Sing In

login.password=Password

login.remember=remember me

login.tip=Please sign in

login.username=UserName

#login.properties

login.btn=登录~

login.password=密码~

login.remember=记住我~

login.tip=请登录~

login.username=用户名~

[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">

<img class="mb-4" th:src="@{asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">

<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>

防止表单重复提交的办法:重定向

#LoginController.java

if(!StringUtils.isEmpty(username)&& "123456".equals(password)){

//登录成功 防止表单重复提交,可以重定向到主页

    return "redirect:/main.html";

#MyMvcConfig.java

//所有的WebMvcConfigurer组件会一起起作用

//@Bean将组件注册在容器中

@Bean

public WebMvcConfigurer webMvcConfigurer(){

WebMvcConfigurer  dapter = new WebMvcConfigurer() {

@Override

public void addViewControllers(ViewControllerRegistry registry) {

registry.addViewController("/").setViewName("login");

registry.addViewController("/index.html").setViewName("login");

            registry.addViewController("/main.html").setViewName("dashboard");

}

};

return dapter;

}

(4).拦截器进行登陆检查

[1].拦截器

/**

 * 登录检查

 */

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;

}

}

}

[2].注册拦截器

//所有的WebMvcConfigurer组件会一起起作用

//@Bean将组件注册在容器中

@Bean

public WebMvcConfigurer webMvcConfigurer(){

WebMvcConfigurer  dapter = new WebMvcConfigurer() {

@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 dapter;

}

(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-员工添加

templates/emp/add.html

<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@atguigu.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>

[1].格式问题

若提交的日期格式不对,则会出现404

SpringMVC将页面提交的值需要转换为指定的类型

spring:

  mvc:

    format:

      date: yyyy-MM-dd

(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@atguigu.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>

7.错误处理机制

(1).SpringBoot默认的错误处理机制

[1].默认效果

浏览器:返回一个默认的错误页面

浏览器发送请求的请求头

其它客户端:默认响应一个json数据

[2].原理

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

@Controller

@RequestMapping({"${server.error.path:${error.path:/error}}"})

public class BasicErrorController extends AbstractErrorController {

private final ErrorProperties errorProperties;

#产生html类型的数据,浏览器发送的请求来到这个方法处理

@RequestMapping(

produces = {"text/html"}

)

public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {

HttpStatus status = this.getStatus(request);

Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));

response.setStatus(status.value());

//到那个页面作为错误页面;包含页面地址和页面内容

ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);

return modelAndView != null ? modelAndView : new ModelAndView("error", model);

}

#产生json数据,其他客户端来到这个方法处理

@RequestMapping

public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {

HttpStatus status = this.getStatus(request);

if (status == HttpStatus.NO_CONTENT) {

return new ResponseEntity(status);

} else {

Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));

return new ResponseEntity(body, status);

}

}

3].ErrorMvcAutoConfiguration.ErrorPageCustomizer

#系统出现错误后到error请求进行处理

#web.xml注册的错误页面规则

public class ErrorProperties {

@Value("${error.path:/error}")

private String path = "/error";

4].DefaultErrorViewResolverConfiguration

@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);

}

[3].步骤

系统出现4xx或者5xx的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);来到/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).定制错误响应

[1].如何定制错误页面

1].模板引擎存在

有模板引擎的情况下;error/状态码;将错误页面命名为  错误状态码.html 放在模板引擎文件夹里面的 error文件夹下

我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);

页面能获取的信息

timestamp

时间戳

status

状态码

error

错误提示

exception

异常对象

message

异常消息

errors

JSR303数据校验的错误

2].模板引擎不存在

模板引擎找不到这个错误页面,静态资源文件夹下找;

3].其它

以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;

[2].如何定制错误json数据

1].自定义异常处理&返回定制json数据

8.配置嵌入式Servlet容器

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

(1).定制|修改Servlet容器配置

[1].修改server相关配置(ServerProperties)

server.port=8081

server.context-path=/crud

server.tomcat.uri-encoding=UTF-8

//通用的Servlet容器设置

server.xxx

//Tomcat的设置

server.tomcat.xxx

[2].WebServerFactoryCustomizer

编写WebServerFactoryCustomizer:嵌入式Servlet容器定制器;修改Servlet容器的配置

//spring1.0支持 EmbeddedServletContainerCustomizer

//spring2.0支持 webServerFactoryCustomizer

//参考文档:https://blog.csdn.net/Stitch__/article/details/88751497

@Bean

public WebServerFactoryCustomizer webServerFactoryCustomizer(){

return new WebServerFactoryCustomizer <ConfigurableWebServerFactory>() {

//定制嵌入式的servlet容器相关规则

        @Override

public void customize(ConfigurableWebServerFactory factory) {

factory.setPort(8088);

}

};

}

(2).注册Servlet三大组件(Servlet、Filter、Listener)

注册三大组件用以下方式:ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

[1].ServletRegistrationBean

@Bean

public FilterRegistrationBean myFilter(){

FilterRegistrationBean registrationBean = new FilterRegistrationBean();

registrationBean.setFilter(new MyFilter());

registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));

return registrationBean;

}

[2].FilterRegistrationBean

@Bean

public FilterRegistrationBean myFilter(){

FilterRegistrationBean registrationBean = new FilterRegistrationBean();

registrationBean.setFilter(new MyFilter());

registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));

return registrationBean;

}

[3].ServletListenerRegistrationBean

@Bean

public ServletListenerRegistrationBean myListener(){

ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean(new MyListener());

return registrationBean;

}

SpringBoot帮我们自动SpringMVC的时候,自动的注册SpringMVC的前端控制器;DIspatcherServlet;

@Bean(

name = {"dispatcherServletRegistration"}

)

@ConditionalOnBean(

value = {DispatcherServlet.class},

name = {"dispatcherServlet"}

)

public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {

DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());

//默认拦截:/ 所有请求 包括静态资源 但是不拦截jsp  /*会拦截jsp

//可以通过server.serverletPath修改SrpingMVC前段控制器默认拦截的请求

registration.setName("dispatcherServlet");

registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());

multipartConfig.ifAvailable(registration::setMultipartConfig);

return registration;

}

}

(3).springboot使用其它servlet容器

[1].tomcat

Tomcat(默认使用)

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器;

</dependency>

[2].Jetty

目前只在springboot1.5.10版本上可以成功,spring2.0版本的都有错误

<!-- 引入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>

[3].Undertow

Undertow在springBoot1.5.10和SpringBoot2.0都成功运行

<!-- 引入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容器自动配置原理

[1].SpringBoot1.5.10版本

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

EmbeddedServletContainerFactory:嵌入式Servlet容器工厂

public interface EmbeddedServletContainerFactory {

//获取嵌入式的Servlet容器

EmbeddedServletContainer getEmbeddedServletContainer(

ServletContextInitializer... initializers);

}

2].EmbeddedServletContainer

EmbeddedServletContainer:嵌入式的Servlet容器

3].TomcatEmbeddedServletContainerFactory

@Override

public 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

5]修改原理

EmbeddedServletContainerCustomizerBeanPostProcessor

容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor

//初始化之前

@Override

public 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也是定制器

6].步骤

1).SpringBoot根据导入的依赖情况,给容器中添加相应的

EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】

2).容器中某个组件要创建对象就会惊动后置处理器

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

3).后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法

[2].SpringBoot2.0版本

(5).嵌入式Servlet容器启动原理

问题:

①.何时创建嵌入式的Servlet容器工厂

②.何时获取嵌入式的Servlet容器并启动Tomcat

[1].获取嵌入式的Servlet容器工厂

1].SpringBoot应用启动运行run方法

2].refreshContext

refreshContext(context);

SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;

如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext,否则:AnnotationConfigApplicationContext

3].refresh

refresh(context);

refresh刷新刚才创建好的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

onRefresh()web的ioc容器重写了onRefresh方法

5].createEmbeddedServletContainer

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容器

9.外置Servlet容器

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

嵌入式Servlet容器优点:简单、便携

嵌入式Servlet容器缺点:默认不支持JSP、优化定制比较复杂(使用定制器[ServerProperties、自定义EmbeddedServletContainerCustomizer[,自己编写嵌入式Servlet容器的创建工厂[EmbeddedServletContainerFactory[);

外置式Servlet容器:外面安装Tomcat---应用war包的方式打包;

(1).步骤

[1].创建war项目

利用idea创建好目录结构

[2].修改pom文件

将嵌入式的Tomcat指定为provided

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

<scope>provided</scope>

</dependency>

[3].SpringBootServletInitializer

编写一个SpringBootServletInitializer的子类,并调用configure方法

public class ServletInitializer extends SpringBootServletInitializer {

@Override

protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

//传入SpringBoot应用的主程序

return application.sources(SpringBoot04WebJspApplication.class);

}

}

[4].启动服务器

(2).原理

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

war包:启动服务器,服务器启动SpringBoot应用[SpringBootServletInitializer],启动ioc容器;

[1].规则

1].服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例

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

3].使用@HandlesTypes,在应用启动的时候加载需要的类;

[2].流程

1].启动Tomcat

2].org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.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应用

参考文档

http://www.webjars.org/

https://www.thymeleaf.org/

https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#howto-use-thymeleaf-3

https://github.com/thymeleaf/thymeleaf/releases

https://github.com/ultraq/thymeleaf-layout-dialect/releases

https://www.thymeleaf.org/documentation.html

https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications

4.Spring Boot web开发的更多相关文章

  1. Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件

    1. 拦截器 Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置部分.并没有本质的不同,都是通过实现 HandlerInterceptor ...

  2. Springboot 系列(七)Spring Boot web 开发之异常错误处理机制剖析

    前言 相信大家在刚开始体验 Springboot 的时候一定会经常碰到这个页面,也就是访问一个不存在的页面的默认返回页面. 如果是其他客户端请求,如接口测试工具,会默认返回JSON数据. { &quo ...

  3. Springboot 系列(五)Spring Boot web 开发之静态资源和模版引擎

    前言 Spring Boot 天生的适合 web 应用开发,它可以快速的嵌入 Tomcat, Jetty 或 Netty 用于包含一个 HTTP 服务器.且开发十分简单,只需要引入 web 开发所需的 ...

  4. Spring Boot Web 开发注解篇

    本文提纲 1. spring-boot-starter-web 依赖概述 1.1 spring-boot-starter-web 职责 1.2 spring-boot-starter-web 依赖关系 ...

  5. 四、Spring Boot Web开发

    四.Web开发 1.简介 使用SpringBoot: 1).创建SpringBoot应用,选中我们需要的模块: 2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可 ...

  6. spring boot系列(二)spring boot web开发

    json 接口开发 在以前的spring 开发的时候需要我们提供json接口的时候需要做如下配置: 1 添加jackjson等jar包 2 配置spring controller扫描 3 对接的方法添 ...

  7. Spring Boot Web 开发@Controller @RestController 使用教程

    在 Spring Boot 中,@Controller 注解是专门用于处理 Http 请求处理的,是以 MVC 为核心的设计思想的控制层.@RestController 则是 @Controller ...

  8. Spring Boot Web开发中Thymeleaf模板引擎的使用

    这里使用的是idea 1.新建Spring Boot项目 File-->New-->Project...,然后选择左边的Spring Initializr-->Next,可根据自己的 ...

  9. (5)Spring Boot web开发 --- Restful CRUD

    文章目录 `@RestController` vs `@Controller` 默认访问首页 设置项目名 国际化 登陆 & 拦截 Restful 风格 @RestController vs @ ...

随机推荐

  1. spring boot:实现图片文件上传并生成缩略图(spring boot 2.3.1)

    一,为什么要给图片生成缩略图? 1, 用户上传的原始图片如果太大,不能直接展示在网站页面上, 因为不但流费server的流量,而且用户打开时非常费时间, 所以要生成缩略图. 2,服务端管理图片要注意的 ...

  2. B站弹幕爬取

    B站弹幕爬取 单个视频弹幕的爬取 ​ B站弹幕都是以xml文件的形式存在的,而xml文件的请求地址是如下形式: http://comment.bilibili.com/233182992.xml ​ ...

  3. 通俗的讲解Python中的__new__()方法

    2020-3-17更新本文,对本文中存争议的例子进行了更新! 曾经我幼稚的以为认识了python的__init__()方法就相当于认识了类构造器,结果,__new__()方法突然出现在我眼前,让我突然 ...

  4. CentOS7使用firewalld管理防火墙

    firewalld的基本使用 #启动 systemctl start firewalld #关闭 systemctl stop firewalld #查看状态 systemctl status fir ...

  5. 如何实现 axios 的自定义适配器 adapter

    Axios 是一个非常优秀的基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中.并且提供了很多便捷的功能,例如: 支持 Promise API 拦截请求和响应 转换请求数据和 ...

  6. vue 组件传值,(太久不用就会忘记,留在博客里,方便自己查看)

    一 :父组件 传值给 子组件 方法: props //父组件 <template lang="html"> <div> <h3>我是父亲< ...

  7. js 小数点失精度

    解决方法思路:将小数化成整数后再作运算.具体代码如下:  /*** 加法运算,避免数据相加小数点后产生多位数和计算精度损失.** @param num1加数1 | num2加数2*/function ...

  8. C. Bank Hacking 解析(思維)

    Codeforce 796 C. Bank Hacking 解析(思維) 今天我們來看看CF796C 題目連結 題目 略,請直接看原題. 前言 @copyright petjelinux 版權所有 觀 ...

  9. 【总结】mysql调优

    一.事务 1.事务的特性 (1)原子性(Atomicity),可以理解为一个事务内的所有操作要么都执行,要么都不执行. (2)一致性(Consistency),可以理解为数据是满足完整性约束的,也就是 ...

  10. 解决pl/sql developer中数据库插入数据乱码问题

    最近学习SSM项目开发,用到oracle数据库, 使用管理软件PL/sql developer往数据库表中插入数据时记录乱码.  结果如下: 可以看到中文数据都乱码成了???????问号, 看了网上各 ...