加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

4.Spring Boot web开发

发布时间:2020-12-15 01:14:53 所属栏目:大数据 来源:网络整理
导读:1.创建一个web模块 (1).创建SpringBoot应用,选中我们需要的模块; (2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 (3).自己编写业务代码; 自动配置原理 xxxxAutoConfiguration:帮我们给容器中自动配置组件; x

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,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,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,Object> model) {

????//所有的ErrorViewResolver得到ModelAndView

???for (ErrorViewResolver resolver : this.errorViewResolvers) {

??????ModelAndView modelAndView = resolver.resolveErrorView(request,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].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,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

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读