Spring Boot中的MVC支持
Spring Boot 的 MVC 支持主要来介绍实际项目中最常用的几个注解,包括 @RestController、 @RequestMapping、@PathVariable、@RequestParam 以及 @RequestBody。主要介绍这几个注解常用的使用方式和特点
1.@RestController
@RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解
其实是需要返回到 user.html 页面的,如果使用 @RestController 的话,会将 user 作为字符串返回的,所以这时候我们需要使用 @Controller 注解
2. @RequestMapping
@RequestMapping 是一个用来处理请求地址映射的注解,它可以用于类上,也可以用于方法上
该注解有6个属性,一般在项目中比较常用的有三个属性:
- value 属性:指定请求的实际地址,value 可以省略不写
- method 属性:指定请求的类型,主要有 GET、PUT、POST、DELETE,默认为 GET
- produces属性:指定返回内容类型,如 produces = “application/json; charset=UTF-8”
不同的请求方式可以使用不同的注解,效果一样
GET 方式请求可以直接使用 @GetMapping(“/get”) 注解
PUT 方式请求可以直接使用 @PutMapping 注解
POST 方式请求可以直接使用@PostMapping注解
DELETE 方式请求可以直接使用 DeleteMapping 注解
3. @PathVariable
@PathVariable 注解主要是用来获取 url 参数,Spring Boot 支持 restfull 风格的 url,比如一个 GET 请求携带一个参数 id 过来,我们将 id 作为参数接收,可以使用 @PathVariable 注解,@PathVariable 中的 value 属性来指定对应关系
4. @RequestParam
@RequestParam 注解顾名思义,也是获取请求参数的,上面我们介绍了 @PathValiable 注解也是获取请求参数的
那么 @RequestParam 和 @PathVariable 有什么不同呢?
主要区别在于:
@PathValiable 是从 url 模板中获取参数值, 即这种风格的 url:http://localhost:8080/user/{id}
@RequestParam 是从 request 里面获取参数值,即这种风格的 url:http://localhost:8080/user?id=1
除了 value 属性外,还有个两个属性比较常用:
- required 属性:true 表示该参数必须要传,否则就会报 404 错误,false 表示可有可无。
- defaultValue 属性:默认值,表示如果请求中没有同名参数时的默认值
5. @RequestBody
@RequestBody 注解用于接收前端传来的实体,接收参数也是对应的实体,比如前端通过 json 提交传来两个参数 username 和 password,此时我们需要在后端封装一个实体来接收。在传递的参数比较多的情况下,使用 @RequestBody 接收会非常方便
@RequestBody 注解用于 POST 请求上,接收 json 实体参数总结
本节课主要讲解了 Spring Boot 中对 MVC 的支持,分析了 @RestController、 @RequestMapping、@PathVariable、 @RequestParam 和 @RequestBody 四个注解的使用方式,由于 @RestController 中集成了 @ResponseBody 所以对返回 json 的注解不再赘述Spring Boot集成 Swagger2 展现在线接口文档
Swagger2 的 maven 依赖
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.2.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.2.2</version></dependency>
Swagger2 的配置
新建一个配置类,Swagger2 的配置类上除了添加必要的 @Configuration 注解外,还需要添加 @EnableSwagger2 注解 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
@author shengwu ni */ @Configuration @EnableSwagger2 public class SwaggerConfig {
@Bean public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)// 指定构建api文档的详细信息的方法:apiInfo().apiInfo(apiInfo()).select()// 指定要生成api接口的包路径,这里把controller作为包路径,生成controller中的所有接口.apis(RequestHandlerSelectors.basePackage("com.itcodai.course06.controller")).paths(PathSelectors.any()).build();
}
/**
- 构建api文档的详细信息
- @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
} } ``` 在该配置类中,已经使用注释详细解释了每个方法的作用了,在此不再赘述。到此为止,我们已经配置好了 Swagger2 了。现在我们可以测试一下配置有没有生效,启动项目,在浏览器中输入 localhost:8080/swagger-ui.html// 设置页面标题.title("Spring Boot集成Swagger2接口总览")// 设置接口描述.description("一起学Spring Boot")// 设置联系方式.contact("****")// 设置版本.version("1.0")// 构建.build();
实体类注解
主要介绍一下 Swagger2 中的 @ApiModel 和 @ApiModelProperty 注解
@ApiModel 注解用于实体类,表示对类进行说明,用于参数用实体类接收。
@ApiModelProperty 注解用于类中属性,表示对 model 属性的说明或者数据操作更改Controller 类中相关注解
学习一下 Controller 中和 Swagger2 相关的注解 ```java import com.itcodai.course06.entiy.JsonResult; import com.itcodai.course06.entiy.User; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping(“/swagger”) @Api(value = “Swagger2 在线接口文档”) public class TestController {
@GetMapping("/get/{id}")@ApiOperation(value = "根据用户唯一标识获取用户信息")public JsonResult<User> getUserInfo(@PathVariable @ApiParam(value = "用户唯一标识") Long id) {// 模拟数据库中根据id获取User信息User user = new User(id, "倪升武", "123456");return new JsonResult(user);}
}
@Api 注解用于类上,表示标识这个类是 swagger 的资源。<br />@ApiOperation 注解用于方法,表示一个 http 请求的操作。<br />@ApiParam 注解用于参数上,用来标明参数信息<a name="YWNwa"></a>## Spring Boot集成Thymeleaf模板引擎<a name="uilqH"></a>### 1. Thymeleaf 介绍Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎<a name="d4imk"></a>### 2. 依赖导入```xml<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
另外,在 html 页面上如果要使用 thymeleaf 模板,需要在页面标签中引入:
3. Thymeleaf相关配置
因为 Thymeleaf 中已经有默认的配置了,我们不需要再对其做过多的配置,有一个需要注意一下,Thymeleaf 默认是开启页面缓存的,所以在开发的时候,需要关闭这个页面缓存,配置如下
spring:thymeleaf:cache: false #关闭缓存
4. Thymeleaf 的使用
4.1 其他常用 thymeleaf 操作
| 标签 | 功能 | 例子 |
|---|---|---|
| th:value | 给属性赋值 | |
| th:style | 设置样式 | th:style=”‘display:’+@{(${sitrue}?’none’:’inline-block’)} + ‘’” |
| th:onclick | 点击事件 | th:onclick=”‘getInfo()’” |
| th:if | 条件判断 | |
| th:href | 超链接 | Login /> |
| th:unless | 条件判断和th:if相反 | Login |
| th:switch | 配合th:case | |
| th:case | 配合th:switch | administator |
| th:src | 地址引入 | |
| th:action | 表单提交的地址 |
Spring Boot中的切面AOP处理
1. 什么是AOP
2. Spring Boot 中的 AOP 处理
2.1 AOP 依赖
使用AOP,首先需要引入AOP的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2.2 实现 AOP 切面
Spring Boot 中使用 AOP 非常简单,假如我们要在项目中打印一些 log,在引入了上面的依赖之后,我们新建一个类 LogAspectHandler,用来定义切面和处理方法。只要在类上加个@Aspect注解即可。@Aspect 注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解让该类交给 Spring 来管理
@Aspect@Componentpublic class LogAspectHandler {}
这里主要介绍几个常用的注解及使用:
1.@Pointcut:定义一个切面,即上面所描述的关注的某件事入口。
2.@Before:在做某件事之前做的事。
3.@After:在做某件事之后做的事。
4.@AfterReturning:在做某件事之后,对其返回值做增强处理。
5.@AfterThrowing:在做某件事抛出异常时,处理。
2.3 注解
@Pointcut 注解:用来定义一个切面(切入点),即上文中所关注的某件事情的入口
@Aspect@Componentpublic class LogAspectHandler {/*** 定义一个切面,拦截com.itcodai.course09.controller包和子包下的所有方法*/@Pointcut("execution(* com.itcodai.course09.controller..*.*(..))")public void pointCut() {}}
@Before 注解指定的方法在切面切入目标方法之前执行,可以做一些 log 处理
@Aspect@Componentpublic class LogAspectHandler {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 在上面定义的切面方法之前执行该方法* @param joinPoint jointPoint*/@Before("pointCut()")public void doBefore(JoinPoint joinPoint) {logger.info("====doBefore方法进入了====");// 获取签名Signature signature = joinPoint.getSignature();// 获取切入的包名String declaringTypeName = signature.getDeclaringTypeName();// 获取即将执行的方法名String funcName = signature.getName();logger.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);// 也可以用来记录一些信息,比如获取请求的url和ipServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 获取请求urlString url = request.getRequestURL().toString();// 获取请求ipString ip = request.getRemoteAddr();logger.info("用户请求的url为:{},ip地址为:{}", url, ip);}}
JointPoint 对象很有用,可以用它来获取一个签名,然后利用签名可以获取请求的包名、方法名,包括参数(通过 joinPoint.getArgs() 获取)等等
@After 注解和 @Before 注解相对应,指定的方法在切面切入目标方法之后执行,也可以做一些完成某方法之后的 log 处理
Spring与SpringBoot
Spring的生态
覆盖了:
web开发
数据访问
安全控制
分布式
消息服务
移动开发
批处理
为什么用SpringBoot
能快速创建出生产级别的Spring应用
SpringBoot优点
- Create stand-alone Spring applications
- 创建独立Spring应用
- Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
- 内嵌web服务器
- Provide opinionated ‘starter’ dependencies to simplify your build configuration
- 自动starter依赖,简化构建配置
- Automatically configure Spring and 3rd party libraries whenever possible
- 自动配置Spring以及第三方功能
- Provide production-ready features such as metrics, health checks, and externalized configuration
- 提供生产级别的监控、健康检查及外部化配置
- Absolutely no code generation and no requirement for XML configuration
- 无代码生成、无需编写XML
SpringBoot是整合Spring技术栈的一站式框架 SpringBoot是简化Spring技术栈的快速开发脚手架
- 无代码生成、无需编写XML
SpringBoot缺点
- 人称版本帝,迭代快,需要时刻关注变化
-
了解自动配置原理
自动配置
自动配好Tomcat
- 引入Tomcat依赖。
- 配置Tomcat
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope></dependency>
自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(功能)
- 自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- 无需以前的包扫描配置
- 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“com.atguigu”)
- 或者@ComponentScan 指定扫描路径
@SpringBootApplication等同于@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.atguigu.boot")
- 或者@ComponentScan 指定扫描路径
各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
按需加载所有自动配置项
key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- ‘#’表示注释
字符串无需加引号,如果要加,’’与””表示字符串内容 会被 转义/不转义
数据类型
字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
对象:键值对的集合。map、hash、set、object
k:k1: v1k2: v2k3: v3
数组:一组按次序排列的值。array、list、queue
k:- v1- v2- v3
拦截器
HandlerInterceptor 接口
/*** 登录检查* 1、配置好拦截器要拦截哪些请求* 2、把这些配置放在容器中*/@Slf4jpublic class LoginInterceptor implements HandlerInterceptor {/*** 目标方法执行之前* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();log.info("preHandle拦截的请求路径是{}",requestURI);//登录检查逻辑HttpSession session = request.getSession();Object loginUser = session.getAttribute("loginUser");if(loginUser != null){//放行return true;}//拦截住。未登录。跳转到登录页request.setAttribute("msg","请先登录");// re.sendRedirect("/");request.getRequestDispatcher("/").forward(request,response);return false;}/*** 目标方法执行完成以后* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle执行{}",modelAndView);}/*** 页面渲染以后* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion执行异常{}",ex);}}
配置拦截器
/*** 1、编写一个拦截器实现HandlerInterceptor接口* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】*/@Configurationpublic class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //所有请求都被拦截包括静态资源.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求}}
拦截器原理
1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
- 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
文件上传
页面表单
<form method="post" action="/upload" enctype="multipart/form-data"><input type="file" name="file"><br><input type="submit" value="提交"></form>
文件上传代码
/**
* MultipartFile 自动封装上传过来的文件
* @param email
* @param username
* @param headerImg
* @param photos
* @return
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上传的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
自动配置原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
- 自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
- 原理步骤
- 1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
- 2、参数解析器来解析请求中的文件内容封装成MultipartFile
- 3、将request中文件信息封装为一个Map;MultiValueMap
FileCopyUtils。实现文件流的拷贝
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos)
整合MyBatis操作
依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
1、配置模式
- 全局配置文件
- SqlSessionFactory: 自动配置好了
- SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
- @Import(AutoConfiguredMapperScannerRegistrar.class);
- Mapper: 只要我们写的操作MyBatis的接口标准了 @Mapper 就会被自动扫描进来 ```java @EnableConfigurationProperties(MybatisProperties.class) : MyBatis配置项绑定类。 @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration{}
@ConfigurationProperties(prefix = “mybatis”) public class MybatisProperties
可以修改配置文件中 mybatis 开始的所有;
```yaml
# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置
Mapper接口--->绑定Xml
<?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.atguigu.admin.mapper.AccountMapper">
<!-- public Account getAcct(Long id); -->
<select id="getAcct" resultType="com.atguigu.admin.bean.Account">
select * from account_tbl where id=#{id}
</select>
</mapper>
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
# 配置mybatis规则
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
- 导入mybatis官方starter
- 编写mapper接口。标准@Mapper注解
- 编写sql映射文件并绑定mapper接口
在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration
2、注解模式
```java @Mapper public interface CityMapper {
@Select(“select * from city where id=#{id}”) public City getById(Long id);
public void insert(City city);
}
<a name="y1bCI"></a>
### 3、混合模式
```java
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
public void insert(City city);
}
最佳实战:
- 引入mybatis-starter
- 配置application.yaml中,指定mapper-location位置即可
- 编写Mapper接口并标注@Mapper注解
- 简单方法直接注解方式
- 复杂方法编写mapper.xml进行绑定映射
@MapperScan(“com.atguigu.admin.mapper”) 简化,其他的接口就可以不用标注@Mapper注解
整合 MyBatis-Plus
1、什么是MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生
2、整合MyBatis-Plus
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>自动配置
MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
- SqlSessionFactory 自动配置好。底层是容器中默认的数据源
- mapperLocations 自动配置好的。有默认值。classpath*:/mapper//.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下*
- 容器中也自动配置好了 SqlSessionTemplate
- @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行
优点:
只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
3、CRUD功能
```java @GetMapping(“/user/delete/{id}”) public String deleteUser(@PathVariable(“id”) Long id,
@RequestParam(value = "pn",defaultValue = "1")Integer pn, RedirectAttributes ra){ userService.removeById(id); ra.addAttribute("pn",pn); return "redirect:/dynamic_table";}
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
//表格内容的遍历
// response.sendError
// List
//构造分页参数
Page<User> page = new Page<>(pn, 2);
//调用page进行分页
Page<User> userPage = userService.page(page, null);
// userPage.getRecords() // userPage.getCurrent() // userPage.getPages()
model.addAttribute("users",userPage);
return "table/dynamic_table";
}
<a name="pi0Pp"></a>
## NoSQL
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、**缓存**和消息中间件。 它支持多种类型的数据结构,如 [字符串(strings)](http://www.redis.cn/topics/data-types-intro.html#strings), [散列(hashes)](http://www.redis.cn/topics/data-types-intro.html#hashes), [列表(lists)](http://www.redis.cn/topics/data-types-intro.html#lists), [集合(sets)](http://www.redis.cn/topics/data-types-intro.html#sets), [有序集合(sorted sets)](http://www.redis.cn/topics/data-types-intro.html#sorted-sets) 与范围查询, [bitmaps](http://www.redis.cn/topics/data-types-intro.html#bitmaps), [hyperloglogs](http://www.redis.cn/topics/data-types-intro.html#hyperloglogs) 和 [地理空间(geospatial)](http://www.redis.cn/commands/geoadd.html) 索引半径查询。 Redis 内置了 [复制(replication)](http://www.redis.cn/topics/replication.html),[LUA脚本(Lua scripting)](http://www.redis.cn/commands/eval.html), [LRU驱动事件(LRU eviction)](http://www.redis.cn/topics/lru-cache.html),[事务(transactions)](http://www.redis.cn/topics/transactions.html) 和不同级别的 [磁盘持久化(persistence)](http://www.redis.cn/topics/persistence.html), 并通过 [Redis哨兵(Sentinel)](http://www.redis.cn/topics/sentinel.html)和自动 [分区(Cluster)](http://www.redis.cn/topics/cluster-tutorial.html)提供高可用性(high availability)
<a name="efftd"></a>
### 1、Redis配置
```xml
<!--redis缓存依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
自动配置:
- RedisAutoConfiguration 自动配置类。RedisProperties 属性类 —> spring.redis.xxx是对redis的配置
- 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
- 自动注入了RedisTemplate<Object, Object> : xxxTemplate;
- 自动注入了StringRedisTemplate;k:v都是String
- key:value
- 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
2、Redis序列化
```java package com.example.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; 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.cache.RedisCacheWriter; 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;
/**
Redis缓存序列化类 */ @Configuration public class RedisConfig extends CachingConfigurerSupport { @Autowired private RedisConnectionFactory redisConnectionFactory;
@Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate;}
@Bean public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())); return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);}
}
<a name="EMITL"></a>
### 3、配置文件Redis配置
```yaml
server:
#配置Redis数据源,连接redis数据库
redis:
host: localhost #连接地址ID
port: 6379 #端口号
timeout: 1000 #超时时间(毫秒)
jedis:
pool:
min-idle: 0 #最小空闲连接数
max-active: 10 #最大连接数
max-idle: 8 #最大空闲连接数
max-wait: -1ms #最大阻塞等待时间(使用负值表示没有限制)
4、实例
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.demo.dao.SysUserDao;
import com.example.demo.entity.SysUser;
import com.example.demo.service.SysUserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* (SysUser)表服务实现类
*
* @author makejava
* @since 2021-09-09 16:20:18
*/
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserDao sysUserDao;
/**
* 通过ID查询单条数据
*
* @param usrId 主键
* @return 实例对象
*/
@Override
/**
* 查询时自动缓存
* #result == null #result 代表的是返回值 为空,则不缓存
* 如果 #result 为空,则不缓存,反之缓存
* unless 结果为 true 不缓存 ,反之缓存
*/
@Cacheable(cacheNames = "uset", key = "#root.methodName+'['+#usrId+']'", unless = "#result == null ")
public SysUser queryById(Long usrId) {
return this.sysUserDao.queryById(usrId);
}
@Override
@Cacheable(cacheNames = "uset", unless = "#result == null ")
public List<SysUser> selectList(String usrName) {
return sysUserDao.selectList(usrName);
}
/**
* 查询多条数据
*
* @param offset 查询起始位置
* @param limit 查询条数
* @return 对象列表
*/
@Override
public List<SysUser> queryAllByLimit(int offset, int limit) {
return this.sysUserDao.queryAllByLimit(offset, limit);
}
/**
* 新增数据
*
* @param sysUser 实例对象
* @return 实例对象
*/
@Override
//删除缓存,allEntries 设置为 true 删除全部缓存数据,重新查询时添加数据
// 如果出现异常,不会删除缓存数据
@CacheEvict(value = "uset", allEntries = true)
public SysUser insert(SysUser sysUser) {
this.sysUserDao.insert(sysUser);
return sysUser;
}
/**
* 修改数据
*
* @param sysUser 实例对象
* @return 实例对象
*/
@Override
// 更新数据库数据时缓存同步更新
@CacheEvict(value = "uset", allEntries = true)
public SysUser update(SysUser sysUser) {
this.sysUserDao.update(sysUser);
return this.queryById(sysUser.getUsrId());
}
/**
* 通过主键删除数据
*
* @param usrId 主键
* @return 是否成功
*/
@Override
// 缓存删除,key指定删除的数据
// 如果出现异常,不会删除缓存数据
@CacheEvict(value = "uset", allEntries = true)
public boolean deleteById(Long usrId) {
return this.sysUserDao.deleteById(usrId) > 0;
}
@Override
@CacheEvict(value = "uset", allEntries = true)
public boolean deleteByRole(Long roleId) {
return sysUserDao.deleteByRole(roleId) > 0;
}
@Override
@Cacheable(cacheNames = "uset", key = "#root.methodName+'['+#sysUser.usrName+']'", unless = "#result == null ")
public List<SysUser> queryAll(SysUser sysUser) {
return sysUserDao.queryAll(sysUser);
}
@Override
public IPage<SysUser> selectPage(IPage<SysUser> var1, Wrapper<SysUser> var2) {
return sysUserDao.selectPage(var1, var2);
}
}
