小随笔:
@RestController和@Controller区别:
1.@RestController:方法无法返回jsp页面,或者html
配置的视图解析器 InternalResourceViewResolver 不起作用,返回的内容就是Return 里的内容。
2.如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver 才行。
如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。(@RestController 不用加 body 注解,但是无法返回到 jsp,html 页面)
报错o.s.b.d.LoggingFailureAnalysisReporter:
在启动类上加 exclude = {DataSourceAutoConfiguration.class}
注解作用:将DataSourceAutoConfiguration ,DataSourceTransactionManagerAutoConfiguration 排队在外
也就是不会自动配置 DataSource 与 DataSourceTransactionManager 。
maven 打包失败:
可能没有更新命令
先在 plugins 下的 spring-boot 下的 repackage 更新,然后再打包,就可以成功了
Spring Boot 入门:
微服务:
一种架构风格
一个应用应该是一组小型服务,可以通过 HTTP 的方式进行互通
单体应用:ALL IN ONE
每一个功能元素最终都是一个可独立替换和独立升级的软件单元(微服务)
Hello World:
主程序:(IDEA可以自动配置)
@SpringBootApplication
public class SpringBoot01HelloQuickApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot01HelloQuickApplication.class, args);
}
}
运行即可启动 spring boot
请求类:
浏览器发送 hello 请求,服务器接收请求并处理,响应 Hello World 字符串
直接用 RequestMapping 注解即可(value 为目录,返回的字符串为 文件名)
如果直接发送请求(不用文件,直接显示返回的字符串),则需要加 @ResponseBody 注解
(标识为 @RestController 则不需要加 body,但是无法跳转到目标页面)
(@ResponseBody 也可以加在类名上,表明该类的所有方法放回的字符串都是直接写在页面上的(返回的对象生成为一个JSON给页面))
简化部署:
maven 中加入依赖:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
功能:将这个应用打成 jar 包,直接在控制台使用 java -jar 的命令执行
(命令更新要先 repackage 再打包)
(命令提示符中可以用 Tab 自动补全名称)
POM.xml文件:
父项目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这个父项目spring-boot-starter-parent又依赖一个父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
下面有个属性,定义了对应的版本号
<properties>
<activemq.version>5.15.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.63</appengine-sdk.version>
<artemis.version>2.4.0</artemis.version>
<aspectj.version>1.8.13</aspectj.version>
<assertj.version>3.9.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
Spring Boot 的版本仲裁中心 会自动导入对应的版本,不需要我们自己导入依赖
没有 dependencies 里面管理的依赖自己声明
启动器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-web:帮我们导入web模块正常运行所依赖的组件
spring boot将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目里引入这些starter相关场景的所有依赖都会被导入进来,要用什么功能就导入什么场景的启动器。
resources 目录解析:
resources文件夹目录结构:
● static:保存所有的静态文件如 js css images
● templates:保存所有的模板页面;
(Spring Boot 默认jar包使用嵌入式的 Tomcat,默认不支持JSP)
可以使用模板引擎(freemarker.thymeleaf);
● application.properties:可以修改 Spring Boot 的默认配置
例如 server.port=8081(改 Tomcat 端口号)
配置文件:
Spring Boot使用全局配置文件,配置文件名是固定的;
● application.properties
● application.yml
配置文件作用:修改 Spring Boot 自动配置的默认值(在底层自动配置好的值)
YAML(YAML AIN’T Markup Language)
YAML a Markup Language:是一个标记语言
YAML isn’t Markup Language:不是一个标记语言
标记语言:
以前的配置文件,大多使用 xxx.xml 文件
YAML:以数据为中心,比 json、xml 等更适合做配置文件
以前的 XML 配置端口:
<server>
<port>8081</port>
</server>
YAML:
server:
port: 8081
YAML:
基本语法:
k:(空格)v: 表示一对键值对(空格必须有,在冒号后面)
以空格的缩进来表示层级关系
只要是左对齐的,都是同一个层级的
server:
port: 8081
path: /hello
属性和值是大小写敏感的
值的写法:
字面量:普通的值(数字、字符串、布尔):
k: v: 字面量直接来写,字符串默认不用加引号
双引号:不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思
(name: “hello \n” 输出为:hello 换行)
单引号:会转义特殊字符,特殊字符最终只是作为一个字符串输出
(name: “hello \n” 输出为:hello \n)
对象、Map(属性和值)(键值对):
k: v: 在下一行来写对象的属性和值的关系,注意缩进
对象还是 k: v 的方式
例如:
friends:
lastName: zhangsan
age: 20
还可以用行内写法:
friends: {lastName: zhangsan,age: 20}
数组(List、Set):
用 - 值 表示数组中的一个元素
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
例子:
配置:
person:
lastName: zhangsan
age: 20
boss: false
birth: 2018/01/01
maps:
k1: v1
k2: 12
lists:
- lisi
- zhaoliu
dog:
name: 小狗
age: 2
(lastName 可以写为 last-name ,效果一样)
相应类上面要有
@Component 和 @ConfigurationProperties 注释:
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties: 告诉 spring boot 将本类中的所有属性和配置文件中相关的配置绑定
* prefix="person" : 配置文件中哪个下面的所有属性进行一一映射
*
* 只有这个组件是容器中的组件才能使用容器提供的 @ConfigurationProperties 功能
*/
@Component
@ConfigurationProperties(prefix="person")
@Component (把普通pojo实例化到spring容器中,相当于配置文件中的
<bean id="" class=""/>
)
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。
@ConfigurationProperties:与配置文件关联
属性 prefix:与配置文件中的哪个属性绑定
可以自动注入对象:
@Controller
public class hello {
@Autowired
Person person;
@ResponseBody
@RequestMapping("/")
private Person index(){
return person;
}
}
加入 @Autowired 自动注入对象
在 maven 中可以加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
配置文件处理器,以后编写配置就有提示了
也可以用 properties 进行配置:
person.last-name=张三
person.age=20
person.birth=2018/01/01
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=dog
person.dog.age=15
但是有中文乱码问题,所以要在 IDEA 的设置里设置 properties 文件中文自动转为 ISCII 码值
可以用 @Value 代替 @ConfigurationProperties 注解:
@Value("${person.last-name}")
private String lastName;
@Value("#{11*2}")
private Integer age;
@Value("true")
private Boolean boss;
支持 Spring 语法
区别:
@ConfigurationProperties | @Value | |
功能 | 批量注入配置文件属性 | 单个指定 |
松散绑定(语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
松散绑定:比如 lastName 可以写成 last-name 或者 last_name
SpEL:表达式,如 #{10+3}
JSR303校验:加入 @Validated ,可以在属性上面加 @Email ,表明该属性只能填为邮箱格式
复杂类型封装:比如 map ,@Value 不支持
如果只是用配置文件中的某个值的时候,可以用 @Value:
@RestController
public class HelloController {
@Value("${person.last-name}")
private String name;
@RequestMapping("/sayHello")
public String sayHello() {
return "Hello" + name;
}
}
@PropertySource:
因为 @ConfigurationProperties 是加载全局配置的,如果所有配置都写在全局配置中就过于臃肿
用 @PropertySource 注解可以加载其他的配置文件:
比如有一个 person.properties 里面写着 person 的配置
则可以在 Person 上加入 @PropertySource(value = {“classpath:person.properties”}) 引入配置
@ImportResource:
导入 Spring 的配置文件,使配置文件里面的内容生效
Spring boot 里面没有 Spring 的配置文件,我们自己编写的配置文件也不能自动识别
想让 Spring 配置文件生效,加载进来,可以将 @ImportResource 标识在主启动类上
@ImportResource(locations = {“classpath:beans.xml”})
这样就导入了 Spring 的配置文件让其生效
但是太麻烦!
Spring Boot 推荐给容器添加组件的方式:
推荐使用全注解的方式
配置类 ======== Spring 配置文件
加入 @Configuration 注解,标明这是一个配置类,来替代 Spring 配置文件
在 xml 中用 <bean>
添加组件
而在 配置类 中,用 @Bean 添加组件:
@Bean
public HelloService helloService() {
return new HelloService();
}
@Bean:将方法的返回值添加到容器中,容器中这个组件默认的 id 就是方法名
配置文件占位符:
配置文件可以使用随机数:
${random.value}、${random.int} 等属性配置占位符:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
可以在配置文件中引用前面配置过的属性
${app.name:默认值} 来指定找不到属性时的默认值
Profile:
Profile 是 Spring 对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境
多Profile文件:
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
- application.properties
- application-dev.properties
- application-prod.properties
默认使用 application.properties
激活指定Profile:
application.properties 配置文件指定:
spring.profiles.active=dev
YAML支持多文档块:
server:
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: prod
分多个文档块,在最上层选择激活哪个:
spring:
profiles:
active: dev
激活:
1、在配置文件中激活:–spring.profiles.active=dev
2、命令行:
优先级大于配置文件
打包 成jar后
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar –spring.profiles.active=dev
可以直接在测试的时候,配置传入命令行参数
3、虚拟机参数
-Dspring.profiles.active=dev
配置文件优先级:
SpringBoot启动扫描以下位置的 application.properties 或者 application.yml 文件作为 Spring boot的默认配置文件
file:./config/
file./
classpath:/config/
classpath:/
优先级从高到低顺序,高优先级会覆盖低优先级的相同配置
配置形成互补,如 server.servlet.context-path=/boot03
这样在低优先级配置后,输入 /boot03/项目名,会以高优先级的端口号运行低优先级的路径
(注:spring boot1x 是server.context.path=/boot02)
priority:
还可以通过spring.config.location来改变配置文件的位置
项目打包好了以后,可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置
指定配置文件和默认的配置文件会共同起作用,互补配置
java -jar spring-boot-config-02-0.0.1-SNAPSHOT.jar –spring.config.location=E:/work/application.properties
运维比较有用,从外部加载,不用修改别的文件
外部配置加载:
Spring Boot 也可以从以下位置加载配置:优先级从高到低,高优先级的配置会覆盖低优先级的配置,所有的配置会形成互补配置
1.命令行参数
java -jar spring-boot-config-02-0.0.1-SNAPSHOT.jar –server.port=8084 –server.context-path=/abc
中间一个空格
(和内部配置同时生效)
2.来自java:comp/env的JNDI属性
3.java系统属性(System.getProperties())
4.操作系统环境变量
5.RandomValuePropertySource配置的random.*属性值
由 jar 包外向 jar 包内寻找,高优先级覆盖低优先级
优先加载带 profile
6.jar包外部的 application-{profile}.properties 或 application.yml(带Spring.profile) 配置文件
7.jar包内部的 application-{profile}.properties 或 application.yml(带Spring.profile) 配置文件
再来加载不带 profile
8.jar包外部的 application.properties 或 application.yml(带Spring.profile) 配置文件
9.jar包内部的 application.properties 或 application.yml(不带spring.profile) 配置文件
10.@Configuration注解类的@PropertySource
11.通过SpringApplication.setDefaultProperties指定的默认属性
Spring Boot 与 日志:(未完)
日志框架:
日志门面(日志的抽象层) | 日志实现 |
SLF4j | Logback |
左边选一个门面,右边选一个实现。
日志门面:SLF4j
日志实现:Logback
Spring Boot :底层是 Spring 框架,Spring 框架使用的是 JCL
Spring Boot 选用的是 SLF4j 和 Logback
SLF4j:(未完待续)
日志记录方法的调用不应该直接调用日志的实现类,要调用日志的抽象层里面的方法。
静态资源映射:
1.所有 /webjars/**,都去 classpath:/META-INF/resources/webjars 找资源;
webjars:以 jar 包的方式引入静态资源
https://www.webjars.org/
2.”/**” 访问当前项目的任意资源
如:localhost:8080/abc 去静态资源文件夹里找 abc
3.欢迎页,去静态资源文件夹里找 index.html(所有的 index 被 /** 映射)
如:localhost:8080/ 找 index
4.所有的 **/favicon.ico 都是在资源文件夹里找(网站小图标)
模板引擎:
作用:网页显示动态属性,如何将数据在网页上动态的显示出来。
Thymeleaf:
引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
版本在 properties 里面设置:
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
Thymeleaf 前缀设置:templates/
后缀:.html
语法:
1.导入命名空间:
在 <html>
标签里加入 xmlns:th=”http://www.thymeleaf.org”
2.使用 thymeleaf 的语法:
在标签里加入 th:text,比如:
<div th:text=${hello}>
(将 div 里面的文本内容设置为传入的数据)
可以 th:任意 html 属性 来替换原来属性的值。
3.规则:
功能 | 标签 | 功能和jsp对比 | |
---|---|---|---|
1 | Fragment inclusion | th:insert th:replace | include(片段包含) |
2 | Fragment iteration | th:each | c:forEach(遍历) |
3 | Conditional evaluation | th:if th:unless th:switch th:case | c:if(条件判断) |
4 | Local variable definition | th:object th:with | c:set(声明变量) |
5 | General attribute modification | th:attr th:attrprepend th:attrappend | 属性修改支持前面和后面追加内容 |
6 | Specific attribute modification | th:value th:href th:src … | 修改任意属性值 |
7 | Text (tag body modification) | th:text th:utext | 修改标签体内容 utext:不转义字符 |
8 | Fragment specification | th:fragment | 声明片段 |
4.表达式语法:
Simple expressions:(表达式语法)
Variable Expressions: ${...}
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.
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使用,object=${object} 以后获取就可以使用*{a} 相当于${object.a}
<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链接
#<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
Fragment Expressions: ~{...}//片段文档
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: _
5.行内写法:
[[]] -->th:text
[()] -->th:utext
如:
<span th:each="user:${users}">[[${user}]]</span>
CRUD:
自己配置 mvc 的视图解析跳转:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login.html").setViewName("login");
registry.addViewController("/").setViewName("login");
}
}
自己写一个类实现 WebMvcConfigurer 接口
重写 addViewControllers 方法,调用 addViewController.setViewName 方法
前面是地址,后面是跳转
Thymeleaf 运用:
<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">
加入 th:href=”@{url}” 可以指定访问动态的 url 地址
(假如地址改变,这里也会跟着改)
国际化:
1.编写国际化配置文件,抽取页面需要显示的国际化消息:
创建好三个文件后,任意打开一个文件,下面切换到 Resource Bundle 模式
可以增加属性,同时为三个文件赋值
(中文要用 ASCII 码表示,可以在 IDEA 的 File Encodings 设置里设置自动转换)
2.Spring Boot 自动配置好了管理国际化资源文件的组件:
(ResourceBundleMessageSource)
在 application 配置文件中配置:
spring.messages.basename=i18n.login
3.去页面获取国际化的值:
在页面标签中直接用 Thymeleaf 引用即可
(加入 th:text="#{login.**}"
)
input 标签不能加入 th,只能用行内表达式:
<input type="checkbox" value="remember-me" /> [[#{login.remember}]]
4.在页面中点击按钮切换:
Spring Boot 默认的区域信息转换器是根据请求头来定的
如果想在页面上点击按钮切换,可以自己配置信息转换器:
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String l = httpServletRequest.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 httpServletRequest, @Nullable HttpServletResponse httpServletResponse, @Nullable Locale locale) {
}
}
实现 LocaleResolver 接口,用 request 的 getParameter 方法获取
新 new 一个 Locale,默认为浏览器自带(getDefault)
用 StringUtils(引入的 framework 下的类)的 isEmpty 方法判断 l 是否为空
不为空的话,加入连接符 _ (String[] split = l.split(“_”);)
配置 locale = new Locale(split[0], split[1]);,返回
在 mvc 配置中加入容器:
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
在页面显示:
<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
登陆:
1.开发期间,模板引擎页面修改以后,要实时生效
(1)禁用模板引擎的缓存
spring.thymeleaf.cache=false
(2)页面修改完成以后 ctrl + F9 ,重新编译
用 th:action=@{url} 来跳转到登陆 controller
在 controller 中用 @RequestParam 传值(与 name 属性一致)
设置用户名密码相等才返回跳转
可以传一个 map<String, Object>
来保存错误信息:
map.put("msg", "用户名密码错误");
新设置一个标签,用 th:text="${msg}"
来取出错误信息
用 th:if="${not #strings.isEmpty(msg)}"
来判断:
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
防止表单重复提交:
因为用的 post 请求,所以刷新表单会重复提交
重定向到指定成功页面即可解决
注意:不能直接重定向到 templates 文件夹里面的 html 页面
(因为 templates 文件夹是受保护的,不能直接 url 访问)
所以要重定向到一个其他的 url ,再由这个 url 转向指定的 html
衍生问题:如果重定向到一个其他的 url,那么直接访问这个 url 也可以跳转到成功页面,登陆就失去了意义
解决:拦截器进行登陆检查
如何定制错误页面:
原理:
ErrorMvcAutoConfiguration:错误处理的自动配置
容器中添加了以下组件:
1、DefaultErrorAttributes
(在页面共享信息)
2、BasicErrorController
(处理默认 /error 请求)
3、ErrorPageCustomizer
(系统出现错误后来到 /error 请求进行处理)
4、DefaultErrorViewResolver
步骤:
一旦系统出现4xx或者5xx之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),来到 /error
如何定制错误响应:
1.有模板引擎的情况下, error/状态码,将错误页面命名为 错误状态码.html 放在模板引擎文件夹(templates 文件夹)里面的 error 文件夹下,发生此状态码错误就会来到该页面
(可以用 4xx 名称去匹配所有以 4 开头的错误状态码的错误,如果有精确的状态码页面,优先寻找精确的状态码页面,找不到才去 4xx 页面)
页面能获取的信息:
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里
(获得方式,用 thymeleaf 的行内写法:[[${status}]] 直接拿)
2.没模板引擎的情况下,静态资源文件夹下找(static)
3.模板引擎和静态资源文件夹下都没有,默认来到 Spring Boot 默认的错误页面
配置嵌入式 Servlet 容器:
Spring Boot 默认使用的是嵌入式的 Servlet 容器(Tomcat)
1.如何定制和修改 Servlet 容器的相关配置
1) 修改和 Servlet 有关的配置:(ServerProperties【也是 EmbeddedServletContainerCustomizer】)
server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx
2) 编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器;来修改Servlet容器的配置
@Bean //一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
//定制嵌入式的Servlet容器相关的规则
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8083);
}
};
}
注册 Servlet 三大组件 【Servlet、Filter、Listener】
由于 Spring Boot 默认是以 jar 包的方式启动嵌入式的 Servlet 容器来启动 web 应用,没有 web.xml 文件
注册三大组件用以下方式:
1) ServletRegistrationBean:
@Bean
public ServletRegistrationBean myServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/servlet");
return registrationBean;
}
第一个是 Servlet 的 class 映射,第二个是地址映射
2)FilterRegistrationBean:
Filter:实现 Filter 接口(javax.servlet 包下的 Filter)
在过滤器中最后要执行 chain 的 doFilter 方法,将 request 和 response 传入
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myFilter"));
return registrationBean;
}
setFilter 设置 Filter 的 class
setUrlPatterns 设置访问路径
(Arrays.asList :将若干字符串集合,使其访问多重路径都可实现目的)
3)ServletListenerRegistrationBean:
Listener 要实现 ServletContextListener 接口
@Bean
public ServletListenerRegistrationBean myListener() {
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
}
注意,返回的是 ServletListenerRegistrationBean
(因为其中是 web 启动和 web 停止时的处理,所以不需要 url )
Spring Boot 帮我们自动注册 Spring MVC 的前端控制器:DispatcherServlet
Docker:
Docker 是一个开源的应用容器引擎
Docker 支持将软件编译成一个镜像,然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以使用这个镜像
运行中的镜像称为容器,容器启动时非常快速的
核心概念:
Docker 主机(Host):安装了 Docker 程序的机器(Docker 直接安装在操作系统之上)
Docker 客户端(Client):连接 Docker 主机进行操作
Docker 仓库(Registry):用来保存各种打包好的软件镜像
Docker 镜像(Images):软件打包好的镜像,放在 Docker 仓库中
Docker 容器(Container):镜像启动后的实例称为一个容器
使用 Docker 的步骤:
1. 安装 Docker
2. 去 Docker 仓库找到这个软件对应的镜像
3. 使用 Docker 运行这个镜像,这个镜像就会生成一个 Docker 容器
4. 对容器的启动停止就是对软件的启动停止
安装 Docker:
- 安装 Linux 虚拟机
- 在 Linux 虚拟机上安装 Docker
检查内核版本,必须是 3.10 及以上
uname -r
安装 Docker :
yum install docker
启动 Docker :
systemctl start docker
输入:docker -v ,如果出现版本号,说明启动成功
可以设置为开机启动:
systemctl enable docker
停止 Docker :
systemctl stop docker
镜像操作:
检索:docker search 关键字
拉取:docker pull 镜像名:tag(tag可选,多为软件的版本)
列表:docker images
删除:docker rmi image-id
Spring Boot 与数据访问:
配置:
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/jdbc?useSSL=true
driver-class-name: com.mysql.jdbc.Driver
schema:
- classpath:department.sql
默认寻找文件 schema.sql 或者 schema-all.sql
可以使用:
schema:
- classpath:department.sql
来指定目标文件
Spring Boot 有自动配置的 JdbcTemplate 来操作数据库:
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@GetMapping("/query")
public Map<String, Object> map() {
List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * FROM department");
return list.get(0);
}
配置好 application 连接数据库,然后自动注入 JdbcTemplate,调用 query 返回查询语句,即可查询
整合 druid 数据源:
// 导入druid数据源
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
在 yml 里配置 druid 数据源:
type: com.alibaba.druid.pool.DruidDataSource
# druid 初始化
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
SSL 安全套接层:
SSL 协议分为两层:SSL 记录协议 和 SSL 握手协议
SSL 记录协议建立在可靠的传输协议上
SSL 握手协议建立在 SSL 记录协议之上
在基于 B/S 的 Web 应用中,是通过 HTTPS 来实现 SSL 的(B/S:browser/server ,浏览器与服务端模式),HTTPS 的安全基础是 SSL
使用 SSL 首先需要一个证书,在配置了 JAVA_HOME 的 bin 目录加入到 Path 后,可在控制台调用该命令:
keytool -genkey -alias tomcat
在当前目录下将生成一个 .keystore 文件
在 application.properties 中做出 SSL 的相关配置:
server.port = 8443
server.ssl.key-store = .keystore
server.ssl.key-store-password = 111111
server.ssl.keyStoreType = JKS
server.ssl.keyAlias: tomcat
关于 http 对于 https 的自动转向:
需要配置 TomcatEmbeddedServletContainerFactory,并且添加 Tomcat 的 connector 来实现
做如下配置(Configuration):
package com.example;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringBootHttpsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHttpsApplication.class, args);
}
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
constraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
constraint.addCollection(collection);
context.addConstraint(constraint);
}
};
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}
@Bean
public Connector httpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
// Connector监听的http的端口号
connector.setPort(8080);
connector.setSecure(false);
// 监听到http的端口号后转向到的https的端口号
connector.setRedirectPort(8443);
return connector;
}
}
Spring data JPA 的综合运用:
小实战例子:
(开发使用 Oracle XE 数据库)
1. 对于 application.properties 的配置:
server.port=8888
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot
#1
spring.jpa.hibernate.ddl-auto=update
#2
spring.jpa.show-sql=true
#3
spring.jackson.serialization.indent-output=true
(因为 Oracle 管理端的端口占用8080,故使用端口8888)
连接用户名和密码为 boot 的数据库
spring.jpa.hibernate.ddl-auto=update
:使实用类和数据库中的表单数据对应实时更新
spring.jpa.show-sql=true
:控制台展示SQL语句
spring.jackson.serialization.indent-output=true
:美观 json 数据
2. 实体类的构建:
package com.demo05.demo05.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
@Entity // 数据库中的实体类标注
// 给查询语句起名字
@NamedQuery(name = "Person.withNameAndAddressNamedQuery", query = "SELECT p FROM Person p where p.name=?1 and address=?2")
public class Person {
@Id // 表明这个属性映射为数据库的主键
@GeneratedValue // 默认使用主键生成方式为递增
private Long id;
private String name;
private Integer age;
private String address;
public Person() {
super();
}
public Person(Long id, String name, Integer age, String address) {
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
@Entity :标明实体类
@NamedQuery :为查询语句取一个名字(对应 Person 中相应的方法)
@Id :表明这个属性映射为数据库的主键
@GeneratedValue :默认使用主键生成方式为递增
3. 对于相应接口的构建:
package com.demo05.demo05.repository;
import com.demo05.demo05.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByAddress(String name);
Person findByNameAndAddress(String name, String address);
/**
* @Param 注解注入参数,与 JPQL中的参数相对应
*/
@Query("SELECT p FROM Person p where p.name= :name and p.address= :address")
Person withNameAndAddressQuery(@Param("name")String name, @Param("address")String address);
Person withNameAndAddressNamedQuery(String name, String address);
}
在接口中写相应方法,接口继承类 JpaRepository<Person, Long>
4. 最后构建路径对应:
package com.demo05.demo05.controller;
import com.demo05.demo05.model.Person;
import com.demo05.demo05.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DataController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/save")
public Person save(String name, String address, Integer age) {
Person p = personRepository.save(new Person(null, name, age, address));
return p;
}
@RequestMapping("/q1")
public List<Person> q1(String address) {
List<Person> people = personRepository.findByAddress(address);
return people;
}
@RequestMapping("/q2")
public Person q2(String name, String address) {
Person person = personRepository.findByNameAndAddress(name, address);
return person;
}
@RequestMapping("/q3")
public Person q3(String name, String address) {
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/q4")
public Person q4(String name, String address) {
Person p = personRepository.withNameAndAddressNamedQuery(name, address);
return p;
}
@RequestMapping("/sort")
public List<Person> sort() {
List<Person> people = personRepository.findAll(new Sort(Sort.Direction.ASC, "age"));
return people;
}
@RequestMapping("/page")
public Page<Person> page() {
Page<Person> pagePeople = personRepository.findAll(new PageRequest(1, 2));
return pagePeople;
}
}
各个方法对应路径直接调用即可
(save 方法为传参后进行保存,sort 为排序,page 为分页)