springboot
为什么需要springboot
springboot解决了什么问题
快速入门
创建项目
详解
配置原理
java配置
属性注入
springboot的web项目配置
拦截器
转换器
文件上传
...
缓存控制
redis整合
日志管理
常用配置
跨域
banner
打包
整合mybatis
为什么需要springboot
在之前的ssm项目,会出现大量的xml配置,在这种开发的情况之下,程序员花很多的时间在xml的配置当中,如果这些配置出现了问题项目还可能会无法正常运行,影响了具体的业务代码时间
spring项目发现了上述问题,为了解决上述问题就推出了springboot框架
springboot是spring项目的一个子工程,其实也算是一个项目,官方解释:你只需要点一下run就可以很轻松构建一个独立,生产级别的spring项目
springboot把很多项目需要的东西都添加进去了,而且都进行了默认的配置,如果都是使用这些默认的配置就可以快速的创建一个零配置的项目,解决了配置复杂,依赖混乱的问题
springboot提倡的是约定大于配置
快速入门
创建项目
首先创建一个maven的普通项目
在pom.xml文件中去继承springboot项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
添加一个springboot的web启动器依赖,需要注意的是这个依赖不需要添加版本号
因为我们是继承的父项目 父项目对于版本号已经做了约束
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
添加一个控制器UserController
package com.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("hello")
public String helloWorld(){
return "hello world";
}
}
添加启动类
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Hello world!
*
*/
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class,args);
}
}
通过main方法启动后就可以访问 http://localhost:8080/hello
注解解释
@RestController 就是@Controller和@ResponseBody 组合
@GetMapping 就是@RequestMapping请求方式设置为get
@SpringBootApplication 表示当前是springboot的程序 组合注解
@SpringBootConfiguration 表示springboot的配置 还是组合注解
@Configuration 表示为配置类 在springboot中如果需要添加配置的时候可以使用这个注解
声明当前类为配置类
@Indexed 索引
@EnableAutoConfiguration 启动自动配置
@ComponentScan( 组件扫描 类似xml中 component-scan 标签 从当前类的包开始扫描 包含当前包及其子包
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
启动器
在springboot中添加依赖的时候发现依赖总是包含了starter这个词,这个称为启动器。启动器本质就是spring为了帮助我们完成各种自动化配置,提供了自动配置的依赖,这些依赖就是启动器。是由spring-boot-starter-parent 项目将依赖关系声明为一个或者多个启动器。这样的话我们就可以根据实际的需求将需要的启动器引入即可,比如当前是一个web项目,所以引入了一个web的启动器。
需要注意的是引用启动器的时候我们这里没有添加版本号,那是因为继承的父项目中有这个启动器并且声明版本信息,所以不需要添加版本号。但是如果引入的启动器在父项目中不存在,那就需要添加对应版本号了
配置原理
java配置
在springboot中出现的配置可以使用java配置来完成,java配置主要依赖于java的类和注解来完成,可以替换xml配置,和xml配置效果是一样的
比较常见的注解
@Configuration 一般加在类上面,表示当前类是一个配置类,这个配置类可以替换spring中的xml文件
@PropertySource("classpath:db.properties") 可以指定引入外部配置文件的地址
@Value("com.mysql.jdbc.Driver") 或者 @Value("${jdbc.driver}") 普通属性的值注入 可以使用 ${name} 方式注入配置文件中的值
@Bean 可以声明在方法上,将方法的返回值放入spring的容器中,替换了xml中的bean标签
demo 配置druid进去
添加依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-legIMgpe-1681890801112)(assets/1663162661184.png)]
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
创建配置文件 db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///crm57
jdbc.username=kinglee
jdbc.password=root
创建一个配置类
package com.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:db.properties") //默认去读取指定的配置文件
public class DruidConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean //将方法的返回值放入容器
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
使用容器中的对象
package com.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
@RestController
public class UserController {
@Autowired
private DataSource dataSource;
@GetMapping("hello")
public String helloWorld(){
return "hello world";
}
}
在访问的时候如果dataSource中的4个属性值有内容了,就说明刚才的配置是成功的
如果注入的属性非常多,则配置类上就会存在很多的成员变量,还是会有点麻烦的
在springboot中如果需要添加配置,可以在resources目录下去创建一个 application.properties 或者 application.yml 文件
如果两个都存在,先解析properties文件后解析yml文件 最后将解析的结果进行整合
建议大家使用 yml配置
比如我们现在创建一个application.yml 文件 内容如下
jdbc:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql:///
username:
password: root
这个文件是按缩进进行等级划分的,下一级一定比上一级前面多两个空格,名称和值之间使用冒号隔开 ,冒号后面必须添加一个空格
首先创建一个类将所有的成员变量抱起来 DruidProperties类
package com.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
//配置文件中的属性
@ConfigurationProperties(prefix = "jdbc") // 会自动去读取application.yml文件中的指定前缀的配置
public class DruidProperties {
private String driverClassName;
private String url;
private String username;
private String password;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在配置类中如下
package com.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
//@PropertySource("classpath:db.properties") //默认去读取指定的配置文件
@EnableConfigurationProperties(DruidProperties.class)//开启配置属性的功能
public class DruidConfig {
@Autowired
private DruidProperties druidProperties;
@Bean //将方法的返回值放入容器
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(druidProperties.getDriverClassName());
dataSource.setUrl(druidProperties.getUrl());
dataSource.setUsername(druidProperties.getUsername());
dataSource.setPassword(druidProperties.getPassword());
return dataSource;
}
}
这样也可以读取到配置文件中的内容
更加优雅的注入
上面的方式实现起来还是有一些麻烦,所以springboot提供了一种更加优雅的注入方式
package com.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
public class DruidConfig {
@Bean //将方法的返回值放入容器
@ConfigurationProperties(prefix = "jdbc") //读取配置文件中指定的前缀
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
}
web项目配置
创建一个web项目
web项目配置
刚创建好的springboot项目发现启动失败了,主要的原因是因为添加了数据库的相关依赖,启动的时候就需要去连接数据库,但是没有针对连接数据库做配置
解决办法:
1.启动的时候不去初始化连接池,不去连接数据库
package com.springboot2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) // 启动的时候不去自动配置数据库连接池
public class Springboot2Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2Application.class, args);
}
}
2.配置数据库的连接
springboot提供了2个配置文件application.properties和application.yml去操作数据库,选择其中一个即可
建议使用application.yml来配置
server:
port: 80 # 设置tomcat监听的端口
spring:
datasource: # 数据库连接池 Hikari
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///
username:
password: root
springboot中 的默认连接池是 Hikari
访问静态资源
首先因为创建项目的时候选择的打包方式是jar,那么这个项目就不存在webapp目录,所以不能像以前一样去存储静态资源了。
在Resouces类中可以查看到默认的资源路径如下:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
建议使用的资源路径是 classpath:/static/ 路径
静态资源默认都放在static下 html,js,css,image等目录去存放对应的内容
比如 static/html/test.html 访问这个html 的路径是 http://localhost/html/test.html
访问动态的页面
在springboot中默认使用thymeleaf 作为默认的模板引擎,所以显示视图是需要引入 Thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
模板引擎: 是专门为了动态页面显示的一种技术,他可以提供很多标签,后台将数据放入作用域,然后他可以从作用域中拿到数据并渲染
常见的模板引擎: freemarker thymeleaf volecity
在引入依赖以后可以查看 ThymeleafProperties 源码 可以看到默认的前缀和后缀
private String prefix = "classpath:/templates/";
private String suffix = ".html";
通过这个前缀和后缀可以看出默认存放在templates 目录下,后缀是html文件
当然如果不使用这个前后缀也可以通过配置文件修改
spring:
datasource: # 数据库连接池 Hikari
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///
username: kinglee
password: root
thymeleaf:
prefix: classpath:/templates/ # 修改前缀
suffix: .html #修改后缀
可以通过上述方式修改前缀和后缀
=
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--JBLHtmlToThymeleaf-->
</head>
<body>
</body>
</html>
=
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkxmoJNS-1681890698969)(assets/1663250356551.png)]
=
Thymeleaf 介绍
thymeleaf是一个模板引擎,使用起来还是比较简单的
可以通过后台获取数据,将数据放入ModelMap中,然后通过Thymeleaf来进行渲染
首先创建一个控制器 UserController
package com.springboot2.controller;
import com.springboot2.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Controller
public class UserController {
@GetMapping("hello")
public String hello(Model model){
//将对象存入Model
model.addAttribute("user",new User(1L,18,"<b>zhangsan</b>","123","张三",new Date()));
List<User> users = new ArrayList<>();
users.add(new User(2L,18,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(3L,19,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(4L,20,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(5L,21,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(6L,22,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(7L,23,"<b>zhangsan1</b>","123","张三1",new Date()));
users.add(new User(8L,24,"<b>zhangsan1</b>","123","张三1",new Date()));
model.addAttribute("users",users);
return "show";
}
}
模板的使用如下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
</head>
<body>
<!--显示文本信息-->
<!--不识别标签-->
<span th:text="${user.username}"></span>
<!--识别标签-->
<span th:utext="${user.username}"></span>
<!--渲染对象到表单上-->
<form action="">
<input type="hidden" name="uid" th:value="${user.uid}">
<input type="text" name="username" th:value="${user.username}">
<input type="password" name="password" th:value="${user.password}">
<input type="text" name="nickname" th:value="${user.nickname}">
//函数的使用 前面加#号
<input type="text" name="birthday" th:value="${#dates.format(user.birthday,'yyyy-MM-dd')}">
</form>
<!--将对象渲染到表单-->
<form action="" th:object="${user}">
<input type="hidden" name="uid" th:value="*{uid}">
<input type="text" name="username" th:value="*{username}">
<input type="password" name="password" th:value="*{password}">
<input type="text" name="nickname" th:value="*{nickname}">
<input type="text" name="birthday" th:value="*{#dates.format(birthday,'yyyy-MM-dd')}">
</form>
<!--判断
lt <
gt >
eq ==
le <=
ge >=
and or
-->
<div th:if="${user.age lt 18}">
小于18
</div>
<div th:if="${user.age eq 18}">
等于18
</div>
<div th:if="${user.age gt 18}">
大于18
</div>
<!--循环
-->
<table>
<tr th:each="u,stat:${users}">
<td th:text="${u.uid}"></td>
<td th:text="${u.age}"></td>
<td th:text="${u.username}"></td>
<td th:text="${u.password}"></td>
<td th:utext="${u.nickname}"></td>
<td th:utext="${#dates.format(u.birthday,'yyyy-MM-dd')}"></td>
<!--当前循环的所有数据-->
<!-- <td th:utext="${stat}"></td>-->
</tr>
</table>
<!--循环页码 三个参数分别是 from to step-->
<span th:each="i:${#numbers.sequence(1,10,1)}">
<a href="#" th:utext="${i}"></a>
</span>
<!--switch case-->
<div th:switch="${user.age}">
<div th:case="17">17</div>
<div th:case="18">18</div>
<div th:case="19">19</div>
</div>
</body>
</html>
文件下载
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
//获取下载文件的路径
String path = getClass().getClassLoader().getResource("static/html/test.html").getPath();
String fileName = "test.html";
//如果文件名包含中文需要处理一下
fileName = URLEncoder.encode(fileName,"UTF-8");
response.setHeader("Content-Disposition","attachment;filename=" + fileName);
//执行下载
FileUtils.copyFile(new File(path),response.getOutputStream());
}
上面的文件下载使用到了commons-io包依赖如下
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
文件上传
文件上传和springmvc基本上是一样的,只是配置上有一些区别
springboot的web启动器中里面已经包含了文件上传的依赖和配置,所以直接使用即可
@PostMapping("upload")
public String upload(@RequestParam("file") MultipartFile file){
String path = "D://file3/";
//获取文件的真实名字
String originalFilename = file.getOriginalFilename();
//设置文件的保存路径
path += UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//保存文件
try {
file.transferTo(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return "main";
}
如果多文件上传将参数改成数组即可
默认情况下springboot允许上传的默认参数 单文件最大允许1M ,单次请求多文件最大允许10M
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件最大值
max-request-size: 100MB # 单词请求允许的最大值
如果不想使用yml来进行文件上传的配置,还可以通过java配置来进行配置
package com.springboot2.config;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
import javax.servlet.MultipartConfigElement;
@Configuration
public class UploadConfig {
@Bean
public MultipartConfigElement multipartConfigElement(){
MultipartConfigFactory multipartConfigFactory = new MultipartConfigFactory();
multipartConfigFactory.setMaxRequestSize(DataSize.ofMegabytes(100));
multipartConfigFactory.setMaxFileSize(DataSize.ofMegabytes(10));
return multipartConfigFactory.createMultipartConfig();
}
}
上述的两种配置选择一种即可
拦截器
拦截器的实现和springmvc是一样的
首先创建一个类,实现HandlerInterceptor接口,实现其中的方法即可
package com.springboot2.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 请求到达方法之前
* @param request
* @param response
* @param handler
* @return true 就放行 false 就拦截
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("pre");
return true;
}
/**
* 方法执行完毕以后 执行的方法如果抛出异常,这个方法不执行
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("post");
}
/**
* 响应之前 执行的方法如果抛出异常这个还是会执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion");
}
}
拦截器配置
package com.springboot2.config;
import com.springboot2.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/user/*","/download") //配置需要拦截的路径 是可变参数
.excludePathPatterns("/user/login","/user/reg");//配置不需要拦截的路径
}
}
需要注意的是配置拦截器的时候需要实现一个接口 WebMvcConfigurer
类型转换器
package com.springboot2.convert;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class DateConvert implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
类型转换器的配置也是需要使用java配置
package com.springboot2.config;
import com.springboot2.convert.DateConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DateConvert dateConvert;
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(dateConvert);//注册类型转换器
}
}
异常处理
在实际的开发中,出现异常是很正常的情况,需要对这些异常进行一些处理
局部异常处理
在对应的Controller里面添加异常处理的方法即可
@ExceptionHandler(NullPointerException.class)
public String nullPointer(NullPointerException e){
log.error("抛出异常",e);
log.info("异常处理");
return "nullPointer";
}
全局的异常处理
单独实现一个异常处理的控制器
package com.springboot2.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice // 如果返回的是页面 就用这个注解
//@RestControllerAdvice // 如果响应的是json 可以选择这个注解
@Slf4j
public class ExceptionController {
@ExceptionHandler(NullPointerException.class)
public String nullPointer(NullPointerException e){
log.info("异常处理");
return "error";
}
@ExceptionHandler(IndexOutOfBoundsException.class) // 要求是 @RestControllerAdvice
public ResponseEntity indexOutOfBoundsException(IndexOutOfBoundsException e){
log.info("异常处理");
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
自己实现一个异常处理的类,上面添加注解@ControllerAdvice 或者@RestControllerAdvice
里面添加方法 方法上添加注解@ExceptionHandler 去捕获对应的异常
缓存配置
springboot中已经有缓存的启动器,直接使用即可,主要使用的redis
redis的使用
添加redis的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加redis的配置
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
password: 123456
timeout: 5000
在springboot中操作redis主要还是使用的redisTemplate,所以要操作redis还需要先创建RedisTemplate的对象
添加一个Redis的配置类
package com.springboot2.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
RedisTemplate redisTemplate = new RedisTemplate();
//设置连接工厂
redisTemplate.setConnectionFactory(factory);
//手动添加序列化的配置
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//序列化的配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//value序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//key的序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
//hash的序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
测试代码
package com.springboot2;
import com.springboot2.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Date;
@SpringBootTest
class Springboot2ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
User user = new User(1l, 18, "zhangsan", "123456", "张三", new Date());
redisTemplate.opsForValue().set("user",user);
}
}
如果操作对应的类型,那么使用opsFor对应的类型即可
缓存控制
springboot中去控制缓存
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
给缓存取一个名字
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
password: 123456
timeout: 5000
cache_name: tredis
缓存控制器
package com.springboot2.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory){
//手动添加序列化的配置
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//序列化的配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//获取默认的配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration)
.build();
return redisCacheManager;
}
}
使用的时候只需要在service的方法上添加注解@Cacheable
package com.springboot2.service.impl;
import com.springboot2.entity.User;
import com.springboot2.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Cacheable(value = "zltredis",key = "'uid'")
public User selectUserByUid(Long uid){
log.info("执行了方法{},id为{}","selectUserByUid",uid);
User user = new User(1l, 18, "zhangsan", "123456", "张三", new Date());
return user;
}
}
还需要在启动类上添加注解@EnableCaching 启用缓存
package com.springboot2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching //启用缓存
public class Springboot2Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2Application.class, args);
}
}
这样的话第一次访问的时候会执行service中的方法,第二次执行就不再执行而是直接去缓存中根据key进行查询。
目前还有一个较大的问题,所有的数据都是用的同一个key,这样数据就会出现问题,所以还需要配置一个key的生成器
创建key的生成器
package com.springboot2.generator;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class KeyGenerator implements org.springframework.cache.interceptor.KeyGenerator {
/**
* 根据信息生成一个key
* @param target 目标对象
* @param method 调用的方法
* @param params 方法的参数
* @return
*/
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(target.getClass().getName() );
stringBuilder.append(":");
stringBuilder.append(method.getName());
stringBuilder.append(":");
//拼接参数
if(params != null){
for (Object param : params) {
stringBuilder.append(param);
stringBuilder.append(":");
}
}
return stringBuilder;
}
}
再把key的生成器应用上去
package com.springboot2.service.impl;
import com.springboot2.entity.User;
import com.springboot2.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Cacheable(value = "zltredis",keyGenerator = "keyGenerator")
public User selectUserByUid(Long uid){
log.info("执行了方法{},id为{}","selectUserByUid",uid);
User user = new User(uid, 18, "zhangsan", "123456", "张三", new Date());
return user;
}
}
这样的话只要参数不一样的时候缓存的数据的key就可以不一样
@Cacheable 根据注解中的key或者keyGenerator 去查找元素,如果有就直接返回,如果没有就调用方法,然后把方法的返回值放入缓存中
@CacheEvict 用来标注需要清除缓存的方法上,当标记在类上的时候,这个类的所有方法都会清除缓存,当标记在方法上的时候只有这个方法对应的key会清除缓存
@CachePut 对于@Cacheable 标记的方法,spring每次执行的时候都会先去查找对应的key,如果存在就不执行方法,如果不存在再去执行方法
@CachePut 当标记在方法上后,方法执行完毕后的返回值也会放入缓存,区别是 方法执行之前不会检查key是否存在,而是每次执行方法都会将返回值放入缓存
日志配置
springboot默认的日志依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
这个日志的依赖在spring-boot-starter-web 中已经存在,可以不需要单独添加依赖
=
如果使用的是slf4j,我们可以在项目中添加lombok的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
可以直接在类上添加一个注解@Slf4j ,添加以后方法中就可以直接使用变量log,默认的日志等级为info
logging:
level:
com.springboot2.controller: debug #指定包的日志等级
root: info #设置所有的日志的日志等级 为debug
file:
path: D://file3 # 设置日志的文件路径
name: mylog.log # 设置日志保存的文件名
可以通过上述的方式去配置日志,默认情况下日志还是会输出到控制台方便调试,当设置日志文件后,日志信息可以保存到文件中去
banner制作
banner的制作有两种方式:
1.直接在resources下新建一个banner.txt 将需要显示的内容放进去即可
生成网站
1.文字转文本 https://www.bootschool.net/ascii/
2.文字转文本 http://www.network-science.de/ascii/
3.图片转文本 https://www.degraeve.com/img2txt.php
4.文字转文本 http://patorjk.com/software/taag/
2.直接将图片放在resources目录下,然后在配置文件application.yml中去配置图片的路径
spring:
banner:
image:
location: classpath:banner.png
打包问题
一般情况下打包会打包成jar文件,可以在pom.xml中简单的配置一下
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
//指定主类
<mainClass>com.springboot2.Springboot2Application</mainClass>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
跨域配置
全局跨域配置
创建个跨域的配置类,配置跨域的过滤器
package com.springboot2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
CorsConfiguration configuration = new CorsConfiguration();
//设置允许的源 设置为* configuration.setAllowCredentials(true); 就不允许设置为true
// configuration.addAllowedOrigin("*");
//设置允许的源
configuration.addAllowedOriginPattern("*");
//配置是否允许携带cookie
configuration.setAllowCredentials(true);
//配置允许的请求头
configuration.addAllowedHeader("*");
//允许的请求方式 可以写* 也可以写具体的请求方式
configuration.addAllowedMethod("GET");
configuration.addAllowedMethod("POST");
configuration.addAllowedMethod("PUT");
configuration.addAllowedMethod("DELETE");
//设置允许的响应头 客户端可以获取
configuration.addExposedHeader("*");
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
//配置哪些路径需要跨域
corsConfigurationSource.registerCorsConfiguration("/**",configuration);
return new CorsFilter(corsConfigurationSource);
}
}
通过实现接口来进行配置
package com.springboot2.config;
import com.springboot2.convert.DateConvert;
import com.springboot2.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)// 允许携带cookie
.allowedHeaders("*") // 允许的请求头
.exposedHeaders("*") //允许的响应头
.allowedMethods("GET","POST") //允许的请求方式
.allowedOriginPatterns("*");//允许的源
}
}
上面两种跨域选择一种即可
局部跨域配置
使用注解@CrossOrigin ,这个注解可以添加在类上,也可以添加在方法上
@CrossOrigin(
originPatterns = "*",
methods = {RequestMethod.GET,RequestMethod.POST,RequestMethod.DELETE,RequestMethod.OPTIONS},
allowCredentials = "true",
exposedHeaders = "*",
allowedHeaders = "*"
)
这个注解添加在哪里,哪里就可以支持跨域
整合mybatis
创建一个项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>bootmybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>bootmybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件
server:
port: 80
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///
username:
password: root
hikari:
maximum-pool-size: 10
mybatis:
mapper-locations: classpath:mapper/**.xml
logging:
level:
com:
zlt:
bootmybatis:
dao: debug
启动类
package com.bootmybatis;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.bootmybatis.dao") // 整合mybatis后的接口扫描
public class BootmybatisApplication {
public static void main(String[] args) {
SpringApplication.run(BootmybatisApplication.class, args);
}
}