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 依赖

    1. <dependency>
    2. <groupId>io.springfox</groupId>
    3. <artifactId>springfox-swagger2</artifactId>
    4. <version>2.2.2</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>io.springfox</groupId>
    8. <artifactId>springfox-swagger-ui</artifactId>
    9. <version>2.2.2</version>
    10. </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() {

    1. return new Docket(DocumentationType.SWAGGER_2)
    2. // 指定构建api文档的详细信息的方法:apiInfo()
    3. .apiInfo(apiInfo())
    4. .select()
    5. // 指定要生成api接口的包路径,这里把controller作为包路径,生成controller中的所有接口
    6. .apis(RequestHandlerSelectors.basePackage("com.itcodai.course06.controller"))
    7. .paths(PathSelectors.any())
    8. .build();

    }

    /**

    • 构建api文档的详细信息
    • @return */ private ApiInfo apiInfo() { return new ApiInfoBuilder()
      1. // 设置页面标题
      2. .title("Spring Boot集成Swagger2接口总览")
      3. // 设置接口描述
      4. .description("一起学Spring Boot")
      5. // 设置联系方式
      6. .contact("****")
      7. // 设置版本
      8. .version("1.0")
      9. // 构建
      10. .build();
      } } ``` 在该配置类中,已经使用注释详细解释了每个方法的作用了,在此不再赘述。到此为止,我们已经配置好了 Swagger2 了。现在我们可以测试一下配置有没有生效,启动项目,在浏览器中输入 localhost:8080/swagger-ui.html

      实体类注解

      主要介绍一下 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 {

  1. @GetMapping("/get/{id}")
  2. @ApiOperation(value = "根据用户唯一标识获取用户信息")
  3. public JsonResult<User> getUserInfo(@PathVariable @ApiParam(value = "用户唯一标识") Long id) {
  4. // 模拟数据库中根据id获取User信息
  5. User user = new User(id, "倪升武", "123456");
  6. return new JsonResult(user);
  7. }

}

  1. @Api 注解用于类上,表示标识这个类是 swagger 的资源。<br />@ApiOperation 注解用于方法,表示一个 http 请求的操作。<br />@ApiParam 注解用于参数上,用来标明参数信息
  2. <a name="YWNwa"></a>
  3. ## Spring Boot集成Thymeleaf模板引擎
  4. <a name="uilqH"></a>
  5. ### 1. Thymeleaf 介绍
  6. Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎
  7. <a name="d4imk"></a>
  8. ### 2. 依赖导入
  9. ```xml
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  13. </dependency>

另外,在 html 页面上如果要使用 thymeleaf 模板,需要在页面标签中引入:

3. Thymeleaf相关配置

因为 Thymeleaf 中已经有默认的配置了,我们不需要再对其做过多的配置,有一个需要注意一下,Thymeleaf 默认是开启页面缓存的,所以在开发的时候,需要关闭这个页面缓存,配置如下

  1. spring:
  2. thymeleaf:
  3. 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 地址引入 csdn logo
th:action 表单提交的地址

Spring Boot中的切面AOP处理

1. 什么是AOP

意为:面向切面编程。面向切面编程的目标就是分离关注点

2. Spring Boot 中的 AOP 处理

2.1 AOP 依赖

使用AOP,首先需要引入AOP的依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>

2.2 实现 AOP 切面

Spring Boot 中使用 AOP 非常简单,假如我们要在项目中打印一些 log,在引入了上面的依赖之后,我们新建一个类 LogAspectHandler,用来定义切面和处理方法。只要在类上加个@Aspect注解即可。@Aspect 注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解让该类交给 Spring 来管理

  1. @Aspect
  2. @Component
  3. public class LogAspectHandler {
  4. }

这里主要介绍几个常用的注解及使用:
1.@Pointcut:定义一个切面,即上面所描述的关注的某件事入口。
2.@Before:在做某件事之前做的事。
3.@After:在做某件事之后做的事。
4.@AfterReturning:在做某件事之后,对其返回值做增强处理。
5.@AfterThrowing:在做某件事抛出异常时,处理。

2.3 注解

@Pointcut 注解:用来定义一个切面(切入点),即上文中所关注的某件事情的入口

  1. @Aspect
  2. @Component
  3. public class LogAspectHandler {
  4. /**
  5. * 定义一个切面,拦截com.itcodai.course09.controller包和子包下的所有方法
  6. */
  7. @Pointcut("execution(* com.itcodai.course09.controller..*.*(..))")
  8. public void pointCut() {}
  9. }

@Before 注解指定的方法在切面切入目标方法之前执行,可以做一些 log 处理

  1. @Aspect
  2. @Component
  3. public class LogAspectHandler {
  4. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  5. /**
  6. * 在上面定义的切面方法之前执行该方法
  7. * @param joinPoint jointPoint
  8. */
  9. @Before("pointCut()")
  10. public void doBefore(JoinPoint joinPoint) {
  11. logger.info("====doBefore方法进入了====");
  12. // 获取签名
  13. Signature signature = joinPoint.getSignature();
  14. // 获取切入的包名
  15. String declaringTypeName = signature.getDeclaringTypeName();
  16. // 获取即将执行的方法名
  17. String funcName = signature.getName();
  18. logger.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);
  19. // 也可以用来记录一些信息,比如获取请求的url和ip
  20. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  21. HttpServletRequest request = attributes.getRequest();
  22. // 获取请求url
  23. String url = request.getRequestURL().toString();
  24. // 获取请求ip
  25. String ip = request.getRemoteAddr();
  26. logger.info("用户请求的url为:{},ip地址为:{}", url, ip);
  27. }
  28. }

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技术栈的快速开发脚手架

SpringBoot缺点

  • 人称版本帝,迭代快,需要时刻关注变化
  • 封装太深,内部原理复杂,不容易精通

    了解自动配置原理

    自动配置

  • 自动配好Tomcat

    • 引入Tomcat依赖。
    • 配置Tomcat
      1. <dependency>
      2. <groupId>org.springframework.boot</groupId>
      3. <artifactId>spring-boot-starter-tomcat</artifactId>
      4. <version>2.3.4.RELEASE</version>
      5. <scope>compile</scope>
      6. </dependency>
  • 自动配好SpringMVC

    • 引入SpringMVC全套组件
    • 自动配好SpringMVC常用组件(功能)
  • 自动配好Web常见功能,如:字符编码问题
    • SpringBoot帮我们配置好了所有web开发的常见场景
  • 默认的包结构

    • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
    • 无需以前的包扫描配置
    • 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“com.atguigu”)
      • 或者@ComponentScan 指定扫描路径
        1. @SpringBootApplication
        2. 等同于
        3. @SpringBootConfiguration
        4. @EnableAutoConfiguration
        5. @ComponentScan("com.atguigu.boot")
  • 各种配置拥有默认值

    • 默认配置最终都是映射到某个类上,如:MultipartProperties
    • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项

    • 非常多的starter
    • 引入了哪些场景这个场景的自动配置才会开启
    • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

      配置文件

      springBoot的配置文件类型

      1、properties
      2、yaml

      yaml

      基本语法

  • key: value;kv之间有空格

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • ‘#’表示注释
  • 字符串无需加引号,如果要加,’’与””表示字符串内容 会被 转义/不转义

    数据类型

  • 字面量:单个的、不可再分的值。date、boolean、string、number、null

    1. k: v
  • 对象:键值对的集合。map、hash、set、object

    1. k:
    2. k1: v1
    3. k2: v2
    4. k3: v3
  • 数组:一组按次序排列的值。array、list、queue

    1. k:
    2. - v1
    3. - v2
    4. - v3

    拦截器

    HandlerInterceptor 接口

    1. /**
    2. * 登录检查
    3. * 1、配置好拦截器要拦截哪些请求
    4. * 2、把这些配置放在容器中
    5. */
    6. @Slf4j
    7. public class LoginInterceptor implements HandlerInterceptor {
    8. /**
    9. * 目标方法执行之前
    10. * @param request
    11. * @param response
    12. * @param handler
    13. * @return
    14. * @throws Exception
    15. */
    16. @Override
    17. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    18. String requestURI = request.getRequestURI();
    19. log.info("preHandle拦截的请求路径是{}",requestURI);
    20. //登录检查逻辑
    21. HttpSession session = request.getSession();
    22. Object loginUser = session.getAttribute("loginUser");
    23. if(loginUser != null){
    24. //放行
    25. return true;
    26. }
    27. //拦截住。未登录。跳转到登录页
    28. request.setAttribute("msg","请先登录");
    29. // re.sendRedirect("/");
    30. request.getRequestDispatcher("/").forward(request,response);
    31. return false;
    32. }
    33. /**
    34. * 目标方法执行完成以后
    35. * @param request
    36. * @param response
    37. * @param handler
    38. * @param modelAndView
    39. * @throws Exception
    40. */
    41. @Override
    42. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    43. log.info("postHandle执行{}",modelAndView);
    44. }
    45. /**
    46. * 页面渲染以后
    47. * @param request
    48. * @param response
    49. * @param handler
    50. * @param ex
    51. * @throws Exception
    52. */
    53. @Override
    54. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    55. log.info("afterCompletion执行异常{}",ex);
    56. }
    57. }

    配置拦截器

    1. /**
    2. * 1、编写一个拦截器实现HandlerInterceptor接口
    3. * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
    4. * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
    5. */
    6. @Configuration
    7. public class AdminWebConfig implements WebMvcConfigurer {
    8. @Override
    9. public void addInterceptors(InterceptorRegistry registry) {
    10. registry.addInterceptor(new LoginInterceptor())
    11. .addPathPatterns("/**") //所有请求都被拦截包括静态资源
    12. .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
    13. }
    14. }

    拦截器原理

    1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
    2、先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle

  • 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;

3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion

文件上传

页面表单

  1. <form method="post" action="/upload" enctype="multipart/form-data">
  2. <input type="file" name="file"><br>
  3. <input type="submit" value="提交">
  4. </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 users = Arrays.asList(new User(“zhangsan”, “123456”), // new User(“lisi”, “123444”), // new User(“haha”, “aaaaa”), // new User(“hehe “, “aaddd”)); // model.addAttribute(“users”,users); // // if(users.size()>3){ // throw new UserTooManyException(); // } //从数据库中查出user表中的用户进行展示

    //构造分页参数
    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);
    }
}