一、Web 开发
自动配置原理:
xxxAutoConfiguration: 帮助我们给容器中自动配置组件;
xxxProperyties: 配置类来
1、 SpringBoot 对静态资源的映射规则
1 |
|
==所有的/webjars/**,都去 classpath:/META-INF/resources/web/找资源==
webjars :以 jar 包的方式引入静态资源;http://www.webjars.org/
导入依赖:
1 | <!--引入jquery-webjar 访问的时候只需要写webjars下面的资源名称即可--> |
测试:localhost:8080/webjars/jquery/3.3.1/dist/jquery.js
- ==”/**“ 访问当前项目的任何资源,都去(静态资源的文件夹)找映射。==
- “classpath:/META-INF/resources/“
- “classpath:/resources/“
- “classpath:/static/“
- “classpath:/public/“
- “/“:当前项目
例如: localhost:8080/abc == 去静态资源文件夹里面找 abc
==欢迎页: 静态资源文件下的所有 index.html 页面;被”/**“映射==
==所有的 **/favicaon.ico 都是在静态资源文件下找==
2、引擎模板
jsp、velocity、Freemarker、Thymeleaf
SpringBoot 推荐的 Tymeleaf,语法更简单,功能更强大;
① 引入 thymeleaf
1 | <dependency> |
测试:
1 |
|
切换 thymeleaf 版本
1 | <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version> |
==启动报错==:
1 | *************************** |
==解决方案==:
1 | <properties> |
原因分析:
这里用的 org.springframework.boot 下的 spring-boot-starter-thymeleaf,使用<thymeleaf.version>做标签时的可能与 org.thymeleaf 头冲突,导致包获取不正确。
② Thymeleaf 使用
1 |
|
只要我们把 HTML 页面的放在 classpath:/templates/, thymeleaf 就能自动渲染。
使用:
- 导入 thymeleaf 的名称空间
1 | <html lang="en" xmlns:th="http://www.thymeleaf.org"></html> |
- 使用 thymeleaf 语法
1 |
|
③ 语法规则
th:text: 改变当前元素里面的内容。
th: 任意 html 属性;来替换原生的值。
- 表达式
1 | Simple expression: (表达式语法) |
3、SpringMVC 自动配置
① Spring MVC auto-configuration
Spring Boot 自动配置好了 SpringMVC
以下是 SpringBoot 对 SpringMVC 的默认配置:==(WebMvcAutoConfiguration)==
Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 自动配置 ViewResource(视图解析器:根据方法的返回值得视图对象的(View), 视图对象决定如何渲染(转发?重定向?))
- ContextNegotiatingViewResolver: 组合所有的视图解析器的;
- ==如何定制:我们可以给自己容器中添加一个视图解析器;自动的将其组合进来;==
Support for serving static resources, including support for WebJars (see below). 静态资源文件夹路径,webjars
Static
index.html
support。静态首页的访问Custom
Favicon
support (see below). favicon.ico自动注册 of
Converter
,GenericConverter
,Formatter
bean- Converter: 转换器;public String hello(User user): 类型转换使用 Converter
- Formatter 格式化器: 2020.8.29 == Date;
1 |
|
==自己添加的格式化器转换器,我们只需要放在容器中即可==
Support for HttpMessageConverters (see below)
HttpMessageConverter: SpringMVC 用来转换 Http 请求和响应的 :User—json;
HttpMessageConverters 是从容器中确定;获取所有的 HttpMessageConverter;
给自己的容器中添加 HttpMessageConverter,只需要将自己的组件注册容器中(@Bean, @Component)
Automatic registration of
MessageCodesResolver
(see below). 定义错误的代码生成规则Automatic use of a ConfigurableWebBindingInitializer bean (see below).
==我们可以配置一个 ConfigurableWebBindingInitialzer 来替换默认的:(添加到容器)==
1 | 初始化WebDataBinder; |
org.springframework.boot.autoconfigure.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.
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
② 扩展 SpringMVC
1 | <mvc:view-controller path="/hello" view-name="success"/> |
==编写一个配置类(@Configuration),是 WebMvcConfigAdapter 类型;不能标注@EnableWebMVC==
既保留所有的自动配置,也能用我们扩展的配置;
注:WebMvcConfigurerAdapter该方法在 spring boot 2.0,Spring 5.0 之后,已经被废弃
1 | // 使用WebMvcConfigurer可以来扩展SpringMVC的功能 |
测试:
原理:
1. WebMvcAutoConfiguration是SpringMVC的自动配置类
- 在做其他的自动配置时会导入:@Import(EnableWebMvcConfiguration.class)
1 |
|
容器中所有的 WebMvcConfigurer 都会一起起作用
我们的配置类也会被调用;
效果:SpringMVC 的自动配置和我们的扩展配置都会起作用;
③、全面接管 SpringMVC;
SpringBoot 对 SpringMVC 的自动配置不需要了,所有都是我们自己配置;所有的 SpringMVC 的自动配置都失效了
我们需要在配置类中添加@EnableWebMvc 即可;
1 | // 使用WebMvcConfigurer可以来扩展SpringMVC的功能 |
原理:(为什么@EnableWebMvc 自动配置就失效了)
- @EnableWebMvc 的核心
1 |
|
2.
1 |
|
3.
1 |
|
- @EnableWebMvc 将 WebMvcConfiguationSupport 组件导入进来
- 导入的 WebMvcConfiguration 只是 SpringMVC 最基本功能;
5、如何修改 SpringBoot 的默认设置
模式:
- SpringBoot 在自动配置很多组件的时候,先看容器中有没有自己配置的(@Bean、@Component)如果有就用户自己配置,如果没有,才自动配置和自己默认组合起来;
- 在 SpringBoot 中国会有非常多的 xxxConfigurer 帮助我们进行扩展配置
- 在 SpringBoot 中会有很多的 xxxCustomize 帮助我们进行定制配置
6、RestfulCRUD
配置 pom 配置文件:
1 | <properties> |
① 默认访问首页
方式一:(不推荐)
1 | // 在Controller中配置 |
方式二:(推荐)
1 | // 使用WebConfigurerAdapter可以来扩展SpringMVC的功能 |
【application.properties】
1 | server.servlet.context-path=/curd |
测试:
提前在 POM.xml 文件中引入 bootstrap 依赖(后面需要)
1 | <!--引入bootstrap--> |
② 国际化
编写国际化配置文件:
SpringBoot 自动配置好了管理国际化资源文件的组件(2.x 版本):
1 |
|
1.x 版本:
1 |
|
- 在application.properties 文件中配置:
1 | #指定管理国际化资源文件 |
- 去页面获取的国际化的值、
1 |
|
测试:(更改浏览器语言)
原理:
国际化 Locale(区域信息对象); LocaleResolver(获取区域信息对象)
1 |
|
- 点击切换国际化
【MyLocaleResolver.calss】
1 | // 可以在连接上携带区域的信息 |
【MyMvcConfig.class】
1 |
|
【login.html】
1 | <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a> |
③ 登录
开发期间模板的引擎页面修改以后,要实时生效
- 禁用模板引擎的缓存
1 | # 禁用缓存 |
- 页面修改完成完成以后 ctrl +f9 : 重新编译;
【MyLocaleResolver.class】
1 | public class MyLocaleResolver implements LocaleResolver{ |
【LonginController.class】
1 |
|
【MyMvcConfig.class】
1 |
|
登录错误提示消息
【login.html】
1 | <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p> |
④ 拦截器进行登陆检查
拦截器:
1 | /** |
注册拦截器:【MyMvcConfig.class】
注: 拦截器必须配置不拦截 css 样式,不然页面的 css 样式会失效
1 | // 所有的webMvcConfig组件都会一起起作用 |
⑤ CRUD-员工列表
实验要求:
- RestfulCURD: CURD 满足 Rest 风格;
URL:/ 资源名称 / 资源标识 HTTP 请求的方式区分对资源 CRUD 操作
普通 CURD(uri 来区分操作) | RestfulCRUD | |
---|---|---|
查询 | getEmp | emp—GET |
添加 | addEmp?xxx | emp—POST |
修改 | updateEmp?id=xxx&xxx=xx | emp/{id}—PUT |
删除 | deleteEmp?id=1 | emp/{id}—DELETE |
- 实验的请求架构:
实验功能 | 请求 URL | 请求方式 |
---|---|---|
查询所有员工 | emps | GET |
查询某个员工(来到修改页面) | emp/1 | GET |
来到添加页面 | emp | GET |
添加员工 | emps | POST |
来到修改页面(查出员工进行信息回显) | emp/1 | GET |
修改员工 | emp | PUT |
删除员工 | emp/1 | DELETE |
- 员工列表
thymeleaf 公共页面元素的抽取
1、抽取公共片段
<div th:fragment=”copy”>
© 2011 The Good Thymes Virtual Grocery
</div>2、引入公共片段
<div th:insert=”~{footer:: copy}”>
~{templatename :: selector} : 模板名 :: 选择器
~{templatename :: fragmentname} : 模板名 :: 片段名
3、默认效果
insert 的公共片段在 div 标签中
如果使用 th:insert 等属性进行引入,可以不用写~{};
行内写法可加上 :[[{}]]; [[{}]]
三种引入片段的 th 属性:
th: insert: 将公共整个片段插入到声明引入的元素中
th:replace: 将声明引入的元素换为公共的片段
th:include: 将被引入的片段的内容包含进这个标签中
1 | <footer th:fragment="copy">© 2011 The Good Thymes Virtual Grocery</footer> |
抽取出公共的页面【bar.html】:
1 | <!--topbar--> |
引入(选择高亮):
1 | <!--引入抽取的topbar--> |
【EmployeeController.java】
1 |
|
使用 thymeleaf 模板列出所有的员工信息【list.html】
1 | // 修改这个两处地方 |
⑥ CRUD-员工添加
将【list.html】替换为:
1 | 替换前: |
【EmployeeController.java】
1 |
|
在 templates/emp 新建页面【add.html】:
1 | // 复制list.html 进行细微的修改,删除原 |
【application.properties】
1 | # 格式化时间 |
日期的格式化: SpringMVC 将页面的提交的值需要转换为指定的类型;
例如:2017-12-12—-Date; 类型转换,格式化;
默认日期的按照/的方式
【EmployeeController.java】
1 | /** |
修改【list.html】中的 gender
1 | <td th:text="${emp.gender}==0?'女':'男'"></td> |
效果:
⑦ 员工修改
先通过 id 查询修改员工的信息【EmployeeController.java】
1 | /** |
修改添加二合一表单【list.html】
1 | <!--引入抽取的topbar--> |
获取修改的数据,后台进行处理【EmployeeController.class】
1 | /** |
⑧ 员工删除
【EmployeeController.java】
1 | /** |
【list.html】
- 修改 button 添加属性
1 | <button |
- 添加 form 表单
1 | <form id="deleteEmpForm" method="post"> |
- 用 jquery 来处理用户删除
1 | // 添加th:src 防止js、bootstrat等无法获取资源 |
1 | <script> |
- 异常处理(delete 请求被拦截)
There was an unexpected error (type=Method Not Allowed, status=405)
解决方法:在 SpringBoot 配置文件中【application.properties】中配置
1 | # 取消delete请求拦截 |
7、错误处理机制
① SpringBoot 默认的错误处理机制
默认效果:
- 浏览器,返回一个默认的错误页面
浏览器发送的请求的请求头:
- 如果是其他客户端,默认响应一个 josn 数据
原理:可以参照 ErrorMvcAutoConfiguration; 错误处理的自动配置;
给容器中添加一下组件
- . DefaultErrorAttributes;
1 | 帮助我们在页面共享信息; |
- .BasicErrorController: 处理默认的/error 请求
1 |
|
3).ErrorPageCustomizer:
1 |
|
4). DefaultErroViewResolver:
1 |
|
步骤:一旦系统出现 4xx 或者 5xx 之类的错误;ErrorPageCustmizer 就会生效(定制错误的响应规则);就会来到/error 请求;就会BasicErrorController处理;
- 响应页面;去哪个页面的 DefaultErrorViewResolver 解析得到的;
1 | protected ModelAndView resolveErrorView(HttpServletRequest request, |
② 如果定制错误响应:
1). 如何定制错误的页面
1. 有模板引擎的情况下:error/状态码;【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error 文件下】,发生此状态码的错误就会来到对应的页面;
我们可以使用 4xx 和 5xx 作为错误的文件名来匹配这种类型的所有的错误,精确优先(优先寻找精确的状态码.html);
页面能获取的信息:
- timestamp: 时间戳
- staus: 状态码
- error: 错误提示
- exception: 异常对象
- mesage: 异常消息
- errors: JSR303 数据校验的错误都在这里
1 | <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> |
没有模板引擎(模板引擎找不到这个错误的页面),静态资源文件夹下找;
以上都都没有错误页面,就是默认来到 SpringBoot 默认的错误提示页面;
2).如何定制错误的 json 数据;
- 自定义异常处理&返回定制的 json 数据;
1 |
|
- 转到到/error 进行自适应想响应的效果处理
1 | public String handException(Exception e, HttpServletRequest request){ |
3).将我们的定制数据携带出去;
出现错误以后,会来到/error 请求,会被 BasicErrorController 处理,响应出去可以获取的数据是由 getErrorAttributes 得到的(是 AbstractErrorController(ErrorController)规定的方法);
- 完全来编写一个 ErrorController 的实现类【或者编写 AbstractErrorController 的子类】,放在容器中;
- 页面上能用的数据,或者是 Json 返回的能用的数据都是通过 errAttributes.getErrorAttributes 得到,容器中 DefaultErrorAttrutes.getErrorAttributes();默认进行数据处理的;
自定义 ErrorAttributes
1 | // 给容器中加入我们自己的定义的ErrorAttributes |
最终效果:响应是自适应的,可以通过定制 ErrorAttributes 改变需要返回的内容,
8、配置嵌入式 Servle 容器
SpringBoot 默认使用 Tomcat 作为嵌入式的 Servlet 容器;
① 定制和修改 Servlet 容器的相关配置;
- 修改和 Servlet 有关的配置(ServletProperties【也是 EmbeddedServletContainerCustomizer】);
1 | server.port=8081 |
- 编写一个 EmbeddedServerContainerCustomizer(2.x 以上已经不支持),使用ConfigurableServletWebServerFactory:嵌入式的 Servlet 容器的定制器;来修改 Servlet 容器的配置
1 |
|
② 注册 Servlet 三大组件【Servlet、Filter、Listener】
由于 SpringBoot 默认是以 jar 包的的方式启动嵌入式的 Servlet 容器来启动 SpringBoot 的 web 应用,没有 web.xml 文件
注册三大组件用以下的方式
- ServletRegistrationBean
1 |
|
- FilterRegistrationBean
1 |
|
- ServletListenerRegistrationBean
1 |
|
SpringBoot 帮我们自动 SpringMVC 的时候,自动注册 SpringMVC 的前端控制器;DispartcherServlet;
DispatcherServletAutoConfiguration 中:
1 |
|
③ 替换为其他嵌入式 Servlet 容器
默认支持:
==Tomcat==(默认使用)
1 | <dependency> |
==Jetty==
1 | <!--引入web模块--> |
==Undertow==
1 | <!--引入web模块--> |
④ 嵌入式 Servlet 容器自动配置原理(1.x 版本)
1 |
|
- EmbeddedServletContainerFactory(嵌入式 Servlet 容器工厂)
1 | public interface EmbeddedServletContainerFactory { |
- EmbeddedServerContainer:(嵌入式的 Servlet 容器)
- 以 TomcatEmbeddedServletContainerFactory 为例
1 |
|
- 我们对嵌入式容器的配置修改是怎么生效的
ServerProperties、EmbeddedServleContainerCustomizer
5.容器中导入了 EmbeddedServletContainerCustomizerBeanPostProcessor
1 | //初始化之前 |
步骤:
SpringBoot 根据导入的依赖情况,给容器中添加相应的
EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactroy】
容器中某个组件要创建对象就会惊动后置处理器;
EmbeddedServletContainCustomizerBeanPostProcessor;
只要是嵌入式的 Servlet 容器工厂,后置处理器就工作;
后置处理器,从容中获取所有的 EmbeddedServletContainerCustomizer,调用定制器的定制方法
9、 嵌入式 Servlet 容器启动原理
获取嵌入式的 Servlet 容器工厂:
1. SpringBoot应用启动运行run方法
- refreshContext(context);SpringBoot 刷新 IOC 容器【创建 IOC 容器,并初始化容器,创建容器中的每一个组件】;如果是 web 应用创建AnnotainConfigEmbedderWebApplicationContext;否则;AnnotaionConfigApplicationContext
- refresh(context); 刷新刚才创建的 ioc 容器;
1 | public void refresh() throws BeansException, IllegalStateException { |
onRefrsh(); web 的 ioc 容器重写 onRefresh 方法
webioc 容器会创建嵌入式的 Servlet 容器; createEmbeddedServletContainer();
获取嵌入式的 Servlet 容器工厂
EmbeddedServletContionerFactory containerFactory = getEmbeddedServletContianFactory();
从容器中获取 EmbeddedServletContian 组件;TomcatEmbeddedServletContainerFactory 创建对象,后置处理器一看是这个对象,就能获取所有定制器来定制 Servlet 容器的相关配置;
使用容器工厂获取嵌入式的 Server 容器:
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
嵌入式的 Servlet 容器创建对象并启动 Server 容器;
先启动嵌入式 Servlet 容器,再将 ioc 容器下没有创建出的对象获取出来;
IOC 容器启动创建嵌入式的 Servlet 容器
10、使用外置的 Servlet 容器
嵌入式 Servle 容器: 应用打成可执行的 jar
优点:简单、便携
缺点:默认不支持 JSP、优先定制比较复杂(使用定制器【ServerProperties、自定义 EmbeddesdServletContianCustizer】, 自己编写嵌入式 Servlet 容器的创建工厂【EmbeddedServletContainerFactory 】)
外置 Servlet 容器:外面安装 Tomcat – 应用 war 包的方式打开;
步骤:
必须创建一个 web 项目;
参考博客:https://blog.csdn.net/qq_45738810/article/details/108509314
建入嵌入式的 Tomcat 指定的 provided;
1 | <dependency> |
- 必须编写一个 SpringBootServletInitiallzer 的子类,并调用 configure 方法
1 | public class ServletInitializer extends SpringBootServletInitializer { |
- 启动服务器就可以使用;
==原理:==
jar 包: 执行 SpringBoot 主类的 main 方法,启动 ioc 容器,创建嵌入式的 Servlet 容器;
war 包: 启动服务器,服务器启动 SpringBoot 应用【SpringBootServletInitializer】,启动 ioc 容器;
规则:
服务器启动(web 应用启动)会创建当前 web 应用里面每一个 jar 包里面 ServerContainnerInitializer 实例:
ServletContainerInitializer 的实现放在 jar 包的 META-INF/servies 文件下,有一个名为 java
还可以使用@HandlesTypes,在应用启动的时候加载配置的类
流程:
启动 Tomcat
D:\RepMaven\org\springframework\spring-web\5.2.8.RELEASE\spring-web-5.2.8.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer:
Spring 的 web 模块里面的这个文件:org.springframework.web.SpringServletContainerInitializer
SpringServletContainerInitializer 将@HandlesTypes(WebApplicationInitializer.class )标注的所有这个类型的类都传入 onStratup 方法的 Setup 方法的<Set>; 为这些 WebApplicationInitializer 类型的类创建实例:
每一个 WebApplicaionInitialzer 都调用自己的 onStarup;
- 相当于我们的 SpringBootServletInitalizer 的创建对象,并执行 onStarup 方法
- SpringBootServletInitializer 实例执行 onStartup 的时候会 createRootApplicationContext;创建容器
1 | protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) { |
- Spring 的应用就启动并且创建 IOC 容器
1 | public ConfigurableApplicationContext run(String... args) { |
==启动 Servlet 容器,再启动 SpringBoot 应用==