1. SpringBoot 基础
1.1 简介(自动装配–约定大于配置)
- 用途:快速搭建java EE项目,提供自动化配置方案
- 优势:
- 提供一个快速的spring项目搭建渠道
- 提供一系列通用配置,纯java配置
- 内嵌服务器,快速部署
1.1.1 pom.xml解读
- spring-boot-dependencies :核心依赖在父工程中
- 引入SpringBoot依赖不需要指定版本,因为有这些版本仓库
- 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
SpringBoot会将所有的功能场景,都变成一个个的启动器,要使用什么功能,就只需要找到对应的启动器。
比如spring-boot-starter-web, 他就会帮我们自动导入web环境所有的依赖
官网列出的所有启动器
1.2 手动创建SpringBoot工程(了解)
1.2.1 idea创建maven工程
1.2.2 手动构建项目
- 添加依赖pom.xml文件 spring-boot-starter-parent,作用:
- Java 版本默认使用1.8 .
- 编码格式默认使用UTF-8.
- 提供Dependency Management 进行项目依赖的版本管理。
- 默认的资源过滤与插件配置。
- 启动类
@SpringBootApplication // 1.配置Spring和SpringMVC 2.包扫描
public class App {
public static void main(String[] args){
SpringApplication.run(App.class,args);
}
}
- 启动项目
- idea启动main方法(内置tomcat)
- 引依赖spring-boot-maven-plugin, mvn clean package打包,java -jar 命令启动jar包
-
1.3 快速创建SpringBoot工程(常用)
- 项目的基本信息,包括组织Id 、模块名称、项目构建类型、最终生成包的类型、Java 的版本、开发语言、项目版本号、项目名称、项目描述以及项目的包。
- Spring Boot 项目创建成功之后,几乎零配置, 开发者就可以直接使用Spring 和SpringMVC 中的功能了
1.4 @SpringBootApplication注解
1.4.1 拆成3个注解
包括:
-
@SpringBootConfiguration 表明配置类,类似于Spring
中的aplicationContext.xml 文件-
一般会创建一个专门的类用来配置bean,方便管理
@Configuration public class MyConfig(){ }
-
-
@EnableAutoConfiguration 开启自动化配置
-
@ComponentScan 扫描当前类所在包下面内容,一般把项目启动类放在根包中
- 会扫描@Service , @Repository 、@Component 、
@Controller 、@RestController 和@Configuration 等注解的类
- 会扫描@Service , @Repository 、@Component 、
1.4.2 深入
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取@EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件,并配置好这些组件
- 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作
1.4.3 SpringApplication.run分析
- 判断是普通项目还是web项目
- 查找并加载所有可用初始化器,设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类
1.5 web容器配置
1.5.1 tomcat配置
1.5.1.1 常规配置
spring-boot-starter-web 依赖会默认使用Tomcat 作为Web 容器,可以在application.properties 中进行进一步配置
- server . port = 8081 端口
- server . error . path = /error 出错跳去的路径
- server . servlet . session.timeout = 30m session失效时间
- server . servlet . context-path = /chapter02 访问路径
- server . tomcat . uriencoding = utf-8
- server . tomcat . max-threads = 500 最大线程数
- server . tomcat .basedir = /home/sang/tmp basedir 是一个存放Tomcat 运行日志和临时文件的目录,若不配置,则默认使用系统的临时目录
1.5.1.2 https配置
- 通过工具生成一个https证书
keytool -genkey -alias tomcathttps -keyalg RSA -keysize 2048 -keystore sang.p12 -validity 365
2. 在application.properties 中配置
- server.ssl.key-store=sang.p12
- server.ssl.key-alias=tomcathttps
- server.ssl.key-store-password=root123
然后就可以使用https访问了。
https://localhost:8081/chapter02/getHello
1.6 application.properties配置
1.6.1 加载&注入
- application.properties或application.yml可能出现的4个位置,按照顺序依次加载到Spring Environment中:
- application.properties内容注入到bean实体类中的例子:
- application.properties
book.name=三国演义
book.author=罗贯中
book.price=30
- 或application.yml(多用,直接写json格式使用方便)
//yml文件还能支持对象和列表,如:book,persons
book:
name: 水浒
author_name: 施耐庵
price: 30
persons:
- 武松
- 宋江
- chaogai
等同于
book: {name: 水浒传,author_name: 施耐庵,price: 30,persons: [武松,宋江,chaogai]}
- 实体类Book
@ConfigurationProperties(prefix = “book”)
把application.yml中的内容注入bean中
把配置文件中配置的每一个属性的值映射到这个组件中
(直接使用 @Value 也可以赋值)
@Component
@ConfigurationProperties(prefix = "book")
public class Book {
//@Value("楚门的世界")
//@Value("${book.name}")
//private String name;
private String name;
private String author;
private Float price;
private List<String> persons;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
}
- BookController
@RestController
public class BookController {
@Autowired
Book book;
@GetMapping("/book")
public Book book(){
return book;
}
}
- 运行结果:
- YAML虽然方便, 但是存在缺陷,如:无法使用 @PropertySource注解加载 YAML文件
- @PropertySource可以指定该实体类取哪个 .properties配置文件中的属性值,casey.properties就是application.yml同路径下的一个配置文件,使用@Value取属性值
@Component
//@ConfigurationProperties(prefix = "book")
@PropertySource(value = "classpath:casey.properties")
public class Book {
@Value("${name}")
private String name;
private String authorName;
private Float price;
private List<String> persons;
private Date time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
- yaml文件中可以直接使用el表达式
book:
name: 水浒${random.uuid} #水浒96cbd308-d338-4bca-8897-17380137bc4e
author_name: ${student.name:施耐庵}作者 #student.name不存在,取 施耐庵作者
price: ${random.int} #-698479567
persons:
- 武松
- 宋江
- chaogai
- 松散绑定
配置文件中:last-name
bean类中:lastName
- JSR-303校验
- 使用 @Validated 注解,配合校验属性值
- JSR-303
- 最重要:正则表达式校验
@Pattern 验证 String 对象是否符合正则表达式的规则
@Component
@ConfigurationProperties(prefix = "book")
@Validated
public class Book {
@Email(message = "邮箱格式错误")
private String name;
private String authorName;
private int price;
private List<String> persons;
private Date time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 检查数字是否介于min和max之间.
@Range(min=10000,max=50000,message="range.bean.wage")
private BigDecimal wage;
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
1.6.2 开发环境和生产环境中的配置文件区分
- 在application.yml中进行多环境配置:选择激活哪个配置环境
spring:
profiles:
#表示使用配置文件application-dev.yml启动项目
active: dev
- 可以配置不同的端口号:8080/8081
- 开发环境:application-dev.yml
server:
port: 8080
- 生产环境:application-prod.yml
server:
port: 8081
- 图示
- 也可以写到一个文件application.yml中(不推荐,不容易替换部署到服务器时)
# tomcat配置
server:
port: 8081
#servlet:
# context-path: /chapter02
tomcat:
uri-encoding: utf-8
# https配置
ssl:
key-store: sang.p12
key-alias: tomcathttps
key-store-password: root123
#激活配置文件
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
启动的是dev,端口是8082
1.7 SpringBoot 配置总结
- springboot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?
● xxxAutoConfiguraion:向容器中自动配置组件
● xxxxProperties:自动配置类,装配配置文件中自定义的一些内容
2. SpringBoot 整合视图层
2.1 整合Thymeleaf
参考:整合Thymeleaf
2.1.1 简单实现
- 引pom
<!--thymeleaf都是基于3.x开发-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
- templates文件夹下创建html页面
- xmlns:th=“http://www.thymeleaf.org”
*使用th:text
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<!--th:text就是将div中的内容设置为它指定的值,和之前学习的Vue一样-->
<div th:text="${msg}"></div>
</body>
</html>
- controller访问html页面
(@controller和@restcontroller的区别)
@RequestMapping("/success")
public String success(Model model){
//存入数据
model.addAttribute("msg","Hello,Thymeleaf");
//classpath:/templates/success.html
return "success";
}
2.1.2 语法积累
- url: @{ }
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<form class="form-signin" th:action="@{/user/login}">
- 国际化: #{ }
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
- if th:if
取反: not
字符串为空:#strings.isEmpty( )
<!--如果msg不为空就显示-->
<p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
2.1.3 demo学习
2.1.4 国际化
2.2 整合FreeMarker
- 如果使用的是目前流行的前后端分离技术, 那么在开发过程中不需要整合视图层技术,后端直接提供接口即可
3.SpringBoot 整合Web 开发
3.1 返回json数据
3.1.1 默认返回
由于pom.xml中添加的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
其中默认加入了jackson-databind 作为JSON处理器,如上面例子中返回book对象就属于这种情况。
3.1.2 自定义json处理器
- 除了jackson-databind之外的
3.1.2.1 使用Gson
3.1.2.2 使用fastjson
- 首先除去jackson-databind依赖,引入fastjson依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
- 在SpringBoot 项目中,当开发者引入spring-boot-starter-web 依赖之后,该依赖又依赖了spring-boot-autoconfigure , 在这个自动化配置中,有一个WebMvcAutoConfiguration 类提供了对SpringMVC 最基本的配置, 如果某一项自动化配置不满足开发需求,开发者可以针对该项自定义配置,只需要实现WebMvcConfigurer接口即可
- 配置json解析过程中的一些细节
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd hh:mm:ss");//日期格式
config.setCharset(Charset.forName("UTF-8"));//数据编码
config.setSerializerFeatures(
SerializerFeature.WriteClassName,//是否在生成的JSON 中输出类名
SerializerFeature.WriteMapNullValue,//是否输出value为null的数据
SerializerFeature.PrettyFormat,//生成的json格式化
SerializerFeature.WriteNullListAsEmpty,//空集合输出[]而非null
SerializerFeature.WriteNullStringAsEmpty//空字符串输出""而非null
);
converter.setFastJsonConfig(config);
converters.add(converter);
}
}
3.2 静态资源访问
- 源码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
3.2.1 默认策略
- 使用webjars,通过maven引入包,比如:jquery
- 不同位置静态资源的优先级:
- resources/META-INF/resources
- resources/resources
- resources/static(默认)
- resources/public
- IntelliJ IDEA 创建SpringBoot 项目,会默认创建出resources/static目录,一般静态资源放在此目录下即可
3.结论
3.2.2 首页定制
- 源码
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
- 在静态资源中添加index.html,为默认首页
3.3 文件上传
java中文件上传一般涉及两个组件:
- CommonsMultipartResolver:使用commons-fileupload工具包。
文件上传的原理是IO流实现的,通过二进制流的方式向服务器传输数据,服务器再通过流读取到数据,然后解析成文件,最终保存到服务器上。
commons-fileupload工具包里面封装了对流操作的过程,简化了实现文件上传的代码复杂度。
参考:commons-fileupload实现文件上传 - StandardServletMultipartResolver
3.3.1 单文件上传
1. 在resources 目录下的static 目录中创建一个upload.html 文件,作为上传文件界面
- 上传接口是**/upload**,请求方法是post, enctype 是multipart/form-data,意思是以二进制数据流的方式传输
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFile" value="请选择文件">
<input type="submit" value="上传">
</form>
</body>
</html>
2. 创建文件上传处理接口
package com.cewell.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* ClassName:FileUploadController
* Package:com.cewell.controller
* Description:文件上传
*
* @date:2019/11/26 18:24
* @author:skx@cewell.com
*/
@RestController
public class FileUploadController {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
@PostMapping("/upload")
public String upload(MultipartFile uploadFile, HttpServletRequest req){
//第27代码表示规划上传文件的保存路径为项目运行目录下的uploadFile 文件夹,并在文件夹中通过日期对所上传的文件归类保存。
String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
File folder = new File(realPath + format);
//如果目录不存在,创建
if (!folder.isDirectory()) {
folder.mkdirs();
}
//给上传的文件重命名
String oldName = uploadFile.getOriginalFilename(); //得到上传时的文件名
//UUID.randomUUID().toString() 是JDK提供的一个自动生成主键的方法
//* 截取字符串substring()方法的用法
// 1.substring(int beginIndex); 截取开始索引beginIndex到结束
// "mybaby".substring(3) returns"aby"
// 2.substring(int beginIndex, int endIndex); 截取开始索引beginIndex到结束索引endIndex-1
// "hamburger".substring(3,8) returns "burge"
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."),oldName.length());
//UUID + 截取的文件后缀名
try {
//文件保存 地址,重命名的名字
uploadFile.transferTo(new File(folder,newName));
//生成上传文件的访问路径,并将访问路径返回。
String filePath =
req.getScheme() + "://" //协议
+ req.getServerName() + ":" //ip
+ req.getServerPort() + "/uploadFile/" //端口
+ format + newName;
return filePath;
} catch (IOException e) {
e.printStackTrace();
}
return "上传失败";
}
}
- 执行完成后,返回上传文件的访问路径
- 由访问路径访问文件内容
3.还可以对上传细节进行配置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=0
spring.servlet.multipart.location=E:\\temp
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.resolve-lazily=false
- 第1 行表示是否开启文件上传支持,默认为true。
- 第2 行表示文件写入磁盘的闽值,默认为0 。
- 第3 行表示上传文件的临时保存位直。
- 第4 行表示上传的羊个文件的最大大小,默认为1MB
- 第5 行表示多文件上传时文件的总大小,默认为10MB
- 第6 行表示文件是否延迟解析,默认为false 。
3.3.2 多文件上传
1. 在resources 目录下的static 目录中创建一个uploadMultiple.html 文件,作为上传文件界面(比单个的时候input标签中加一个multiple)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/uploadMultiple" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFiles" value="请选择文件" multiple/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
2. 创建文件上传处理接口
- 传参改成数组MultipartFile[] uploadFiles
@PostMapping("/uploadMultiple")
public List<String> uploadMultiple(MultipartFile[] uploadFiles, HttpServletRequest req) {
//定义一个list用来存放三张图片上传之后的链接
List<String> filePathList = new ArrayList<String>();
for (int i = 0; i < uploadFiles.length; i++) {
//第27代码表示规划上传文件的保存路径为项目运行目录下的uploadFile 文件夹,并在文件夹中通过日期对所上传的文件归类保存。
String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
File folder = new File(realPath + format);
//如果目录不存在,创建
if (!folder.isDirectory()) {
folder.mkdirs();
}
//给上传的文件重命名
String oldName = uploadFiles[i].getOriginalFilename(); //得到上传时的文件名
//UUID.randomUUID().toString() 是JDK提供的一个自动生成主键的方法
//* 截取字符串substring()方法的用法
// 1.substring(int beginIndex); 截取开始索引beginIndex到结束
// "mybaby".substring(3) returns"aby"
// 2.substring(int beginIndex, int endIndex); 截取开始索引beginIndex到结束索引endIndex-1
// "hamburger".substring(3,8) returns "burge"
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
//UUID + 截取的文件后缀名
try {
//文件保存 地址,重命名的名字
uploadFiles[i].transferTo(new File(folder, newName));
//生成上传文件的访问路径,并将访问路径返回。
String filePath =
req.getScheme() + "://" //协议
+ req.getServerName() + ":" //ip
+ req.getServerPort() + "/uploadFile/" //端口
+ format + newName;
filePathList.add(filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
//return "上传失败";
return filePathList;
}
- 返回一个访问路径的list
- 然后可以由访问路径访问文件内容
3.4 @ControllerAdvice全局处理数据
3.4.1 全局异常处理
- 如:3.3中上传文件过大会抛出MaxUploadSizeExceededException异常
- 在系统中定义CustomExceptionHandler 类,然后添加
@ControllerAdvice注解,系统启动时,该类被扫描到spring容器中。 - 如果MaxUploadSizeExceededException改为Exception,表明该方法用来处理所有类型的异常
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter(); //取一个响应客户端的流对象,用来在客户端输出
out.write("上传文件大小超出限制"); //通过PrintWrite,以流方式输出,返回给浏览器
out.flush(); //清空缓冲区的数据流
out.close(); //关闭读写流
//flush()这个函数是清空的意思,用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存中,然后再用数据写到文件中,
// 那么当你数据读完时,我们如果这时调用close()方法关闭读写流,这时就可能造成数据丢失,为什么呢,因为,读入数据完成时不代表写入数据完成,
// 一部分数据可能会留在缓存区中,为了理解这个问题,我们举一个例子:
//
//比如,在农村,几乎每家都有抽水机,抽水机的作用是什么呢,就是把水井里的水抽到水缸中,这时我们就会用水管连接抽水机和水缸(水管就好比是缓冲区),
// 当我们想把水井中的水都抽到水缸中时,我们就让抽水机工作抽水,如果我们发现水井里的水刚好抽完时,我们就会关掉抽水机的开关停止抽水,那么这时,
// 管道里就会遗留一部分水,抽水就是读数据,水缸进水就是写数据,水管充当缓存区的角色,不知道这样是不是具象化了呢
//
//那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,
// 就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。
}
}
3.4.2 添加全局数据
- @ModelAttribute中的value属性(“info”)表示这条返回数据的key,而方法的返回值是返回数据的value
@ControllerAdvice
public class GlobalConfig {
//注解@ModelAttribute中的value属性表示这条返回数据的key,而方法的返回值是返回数据的value
@ModelAttribute(value = "info")
public Map<String,String> userInfo(){
HashMap<String,String> map = new HashMap<>();
map.put("writer","罗贯中");
map.put("bookName","三国演义");
return map;
}
}
- 此时在任意请求的Controller 中,通过方法参数中的Model 都可以获取info 的数据。
@RestController
public class HelloController {
@GetMapping("/helloBook")
public String helloBook(Model model){
Map<String,Object> map = model.asMap();
return map.toString(); //{info={writer=罗贯中, bookName=三国演义}}
}
}
3.4.3 请求参数预处理
- @ControllerAdvice 结合 @InitBinder 还能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。
参考:SpringMVC表单多对象传递小技巧——@InitBinder
3. SpringBoot 整合持久层
3.1 整合Mybatis
3.1.1 pom.xml添加相关依赖
- 添加MyBatis依赖、数据库驱动依赖以及数据库连接池依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
3.1.2 创建数据库和表
3.1.3 创建数据库配置
- 在application.yml中配置数据库基本连接信息
# 数据库配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/exercise?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: root123
3.1.4 创建bean、controller、service、mapper、mapper.xml
//bean
public class Masterpiece {
private Integer id;
private String name;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cewell.dao.MasterpieceMapper">
<resultMap id="BaseResultMap" type="com.cewell.bean.Masterpiece">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="author" jdbcType="VARCHAR" property="author"/>
</resultMap>
<select id="getMasterpiece" resultMap="BaseResultMap">
select * from masterpiece
</select>
</mapper>
3.1.5 在pom.xml中配置扫描 java 目录下的xml文件
- Maven工程中,XML 配置文件建议写在resources目录下
Mapper.xml文件写在java包下,Maven在运行时会忽略包下的XML文件,因此需要在pom.xml文件中重新指明资源文件位置
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<!--<include>**/*.properties</include>
<include>**/*.dic</include>
<include>**/*.xsd</include>-->
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<!--<includes>
<include>**/*.xml</include>
<include>**/*.yml</include>
<include>**/*.ftl</include>
</includes>-->
</resource>
</resources>
3.1.6 controller运行结果
3.2 MyBatis多数据源(暂缺)
4. SpringBoot 整合NoSQL
4.1 整合Redis
4.1.1 redis基础
1.
- redis可以看作一个独立的hashMap,不是在JVM中运行,而是以一个独立进程的形式运行
2. Key-Value数据库
3.
- 当作缓存使用。 因为它比数据库(mysql)快,所以常用的数据,可以考虑放在这里,这样就提高了性能
4.
redis-server.exe | redis-cli.exe |
---|---|
服务端 | 客户端 |
启动redis程序 | 进入redis的命令界面 |
必须启动 | 用jedis之类的操作redis的话就无须启动 |
5.
有5种数据类型 :
- String(字符串)
- List(列表)
- Hash(字典)
- Set(集合)
- Sorted Set(有序集合)
6. jedis:
- 除了使用各种Redis自带客户端的命令行–方式访问Redis服务。 在实际工作中更多用Java代码访问,使用第三方jar包 Jedis就能方便地访问Redis的各种服务了。
import redis.clients.jedis.Jedis;
7. Jedis 和 JedisPool 连接池
1>.Jedis直连
生成Jedis对象
Jedis执行命令
返回执行结果
关闭Jedis连接
Jedis jedis = new Jedis("127.0.0.1","6379");
jedis.set("hello","world");
String value = jedis.get("hello");
2>.使用Jedis连接池
从资源池借Jedis对象
Jedis执行命令
返回执行结果
归还Jedis对象给连接池
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig ,"12.0.0.1",6379);
Jedis jedis = null ;
try{
jedis = jedisPool.getResource();
}catch(Exception e ){
e.printStackTrace;
}finally{
}
8.Redis、Jedis、SpringDataRedis及RedisTemplate关系
(1)Redis、Jedis、SpringDataRedis及RedisTemplate关系
1>.Redis是一个基于内存的Key-Value数据库
2>.Jedis是Redis官方推出的面向Java的客户端Client,提供了很多接口和方法,可以让Java操作使用Redis
3>.SDR是Spring框架集成Redis操作的一个子框架,封装了Redis的很多命令;比Jedis多了自动管理连接池的特性
4>.RedisTemplate是SpringDataRedis中对JedisApi的高度封装
(2)jedis : 操作redis是很简单的,通过new的方式,获取jedis对象,然后增删改查(set,get,del,exists)等,简单来说就是代码的方式来实现操作的;
RedisTemplate : 也可以做到操作redis缓存, 使用spring注入方式的配置
一个比较原生,一个经过spring托管控制;RedisTemplate只做缓存,没有队列,相对jedis功能更少;
单线程使用RedisTemplate缓存即可,如果有多线程,需要使用jedis
4.1.2 SpringBoot整合redis
1. pom.xml 添加相关依赖
<!--添加redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!--从spring-boot-starter-data-redis中排除Lettuce并引入Jedis-->
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入Jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2. 创建redis配置(application.yml 中)
spring:
# 配置Redis
redis:
# 基本连接信息
database: 0 #数据库索引,共16个,编号1-15
host: 127.0.0.1
port: 6379
password:
# 连接池信息
jedis:
pool:
#最大连接数
max-active: 8
#最大空闲连接数
max-idle: 8
#最大阻塞等待时间(负数表示没限制)
max-wait: -1ms
#最小空闲
min-idle: 0
3. 创建实体类bean
//一个类只有实现了Serializable接口,它的对象才是可序列化的
//Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
public class Masterpiece implements Serializable {
private Integer id;
private String name;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "name: " + this.name + " " + "author: " + this.author;
}
}
4. 创建controller
@RestController
@RequestMapping("api")
@GetMapping("/getMasterpieceRedis")
public void getRedis(){
//向redis中存一条记录,再将其读取
ValueOperations<String,String> opsl = stringRedisTemplate.opsForValue(); //获取一个键值操作器
opsl.set("name","三国演义");
String name = opsl.get("name");
System.out.println(name);
//向redis中存一个对象,再将其读取
ValueOperations opsl2 = redisTemplate.opsForValue();
Masterpiece m = new Masterpiece();
m.setId(1);
m.setAuthor("罗贯中");
m.setName("三国演义");
opsl2.set("m",m);
Masterpiece masterpiece = (Masterpiece) opsl2.get("m");
System.out.println(masterpiece.toString());
}
}
5. 访问后结果