spring boot全面复习

Spring boot学习总结

学习笔记根据此demo示例撰写
https://github.com/liushuijinger/springboot

第一天

03 helloworld

标题为spring boot实战项目的章节名

这一章为springboot入门案例,了解到了springboot基础架子的搭建,

  • pom

    • 需要导入Spring boot starter parent,可以统一spring的version,避免了spring由于版本不一致的冲突
    • 还要导入spring-boot-starter-web,此依赖为web编程的起步依赖
    • test类用spring-boot-starter-test依赖;
  • 配置属性

    • 可以通过 servlet.context-path来映射整个工程的父路径
    • 同时集成mysql、redis等属性配置也可在yml文件配置
    • 两个文件:
      • bootstrap.yml:在此文件配置的属性要优先于application.yml
      • application.yml:用于配置属性;
  • 编码

    • 启动类

      • 需要加上@SpringBootApplication,如果要排除依赖,可以在此通过exclude属性排除
      • 通过SpringApplication.run()方法来启动;
    • controller:主要涉及到几个controller层的注解

      • @RestController:标识此类是一个controller类,并将此类的返回结果返回为json格式;跟进此注解,会看到此注解类同时添加了@Controller@ResponseBody注解;

        • @Controller
          • 作用:标识此类是一个controller类,并把此类对象添加进IOC容器;
          • 理解:跟@Component注解类似;handleMapping会扫描有@Controller的类,如果带有@RequestMapping注解,分发处理器会根据资源路径来进行分发到对应的方法上;
        • @ResponseBody:此注解能将返回值转为json格式;
      • 延伸:@RestController@Controller区别,应用场景?

        • @RestController也是基于@Controller进行增强的,结合了@ResponseBody注解;如果想要返回视图资源就用@Controller注解,如果在前后端分离的情况下,可以直接用@RestController注解;
      • @GetMapping

        • 作用:可以设置映射路径接受对应get请求;

        • 理解:此注解是一个组合注解,通过在注解里设置属性达到只接收get请求;@PostMapping等同理

          @RequestMapping(method = {RequestMethod.GET}
          
      • @RequestParam

        • 作用:用于接收请求路径携带的参数;
        • 理解:请求路径中的属性名要与方法参数的属性同名,如果不同名可以通过属性值设置;与@RequestBody注解类似,都是获取参数,不过获取的范围不一样;
      • 总结:

        • 一个基础的Controller控制类,首先需要在类上加上@RestController或@Controller注解,标识此类为一个Controller;
        • 其次根据需求写对应的映射方法,方法上也要加上@RequestMapping等注解来映射路径;
        • 如果根据参数所在位置来使用对应获取参数的注解;如:
          • 路径携带的参数:/stu/delete/{id},这种用@ PathVariable
          • get请求用@RequestParam
          • post请求用@RequestBody
    • 测试类:主要用了通知类注解来测试,通过观察console打印结果可以看出执行顺序

      • @BeforeClass:在类加载之前就出现,一个类中只运行一次,就算抛异常也一定会执行;且必须声明为public static;
      • @Before:在标注了@Test注解方法前执行,可以有多个方法标注@Before,执行顺序不确定,必须声明为public并且非static;
      • @AfterClass:类加载完毕后执行,只执行一次,一定会执行;
      • @After:在标有@Test方法执行之后执行;
    • 测试:

      • 为了验证@BeforeClass是否一定会执行:
      • 环境:以上四个注解都存在,并且在@BeforeClass注解里手动制造除零异常
      • 测试结果:@BeforeClass@AfterClass都会执行,其余注解方法不执行;
  • 总结:

    • 此章主要讲了spring boot基础架子的搭建,以及通知方法的引入;
    • 才看demo时,看起来很简单,当深入去对每个知识点去理解,又会延伸出新的知识,越挖越深。这种学习方式有利于在我的脑海里建立一个思维树,以及问题的联想;

07 Swagger

1. Swagger 概述
  • 概述:是一款接口调试工具,用于生成、描述、可视化Restful风格的web服务;
  • 作用:
    • 便于前后端分离开发,团队协作更方便;
    • 可以在线生成接口文档,方便调试;
    • 接口测试
  • 常用注解:
    • @Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口
    • @ApiParam:单个参数的描述信息
    • @ApiModel:用对象来接收参数
    • @ApiModelProperty:用对象接收参数时,描述对象的一个字段
    • @ApiResponse:HTTP响应其中1个描述
    • @ApiResponses:HTTP响应整体描述
    • @ApiIgnore:使用该注解忽略这个API
    • @ApiError :发生错误返回的信息
    • @ApiImplicitParam:一个请求参数
    • @ApiImplicitParams:多个请求参数的描述信息
    • @ApiImplicitParam属性:
属性 取值 作用
paramType 查询参数类型
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
form 以form表单的形式提交 仅支持POST
dataType 参数的数据类型 只作为标志说明,并没有实际验证
Long
String
name 接收参数名
value 接收参数的意义描述
required 参数是否必填
true 必填
false 非必填
defaultValue 默认值
2. 测试:springboot集成Swagger、mysql进行测试
  • pom

<parent>
    <artifactId>spring-boot-starter-parentartifactId>
    <groupId>org.springframework.bootgroupId>
    <version>2.2.5.RELEASEversion>
parent>
<properties>
    <java.version>1.8java.version>
properties>

<dependencies>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>2.1.0version>
    dependency>
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.1.1version>
    dependency>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.47version>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintagegroupId>
                <artifactId>junit-vintage-engineartifactId>
            exclusion>
        exclusions>
    dependency>
    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger-uiartifactId>
        <version>2.8.0version>
    dependency>
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger2artifactId>
        <version>2.8.0version>
    dependency>
dependencies>
  • 配置属性
server:
  port: 8081
spring:
#  数据库连接
  datasource:
    url: jdbc:mysql://192.168.5.128/db1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
# mybatis
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration: #sql打印日志输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.example.pojo
  • 编码

    • 启动类
    • config
    /**
     * @author scx
     */
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        /**
         * 创建API应用
         * createApiInfo() 增加API相关信息
         * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现
         * 采用指定扫描的包路径来定义指定要建立API的目录
         * @return
         */
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                            .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                            .paths(PathSelectors.any())
                            .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Spring Boot集成Swagger")
                    .description("Spring Boot实战的RESTFul接口文档说明")
                    .version("1.0")
                    .build();
        }
    }
    
    • pojo
    /**
     * 学生实体类
     *
     * @author 沈晨曦
     * @version 2021/4/8 10:10:43
     */
    @Data
    public class Stu {
        @ApiModelProperty("stu主键ID")
        @TableId(type = IdType.AUTO)
        private Integer id;
        @ApiModelProperty("学生姓名")
        private String name;
        @ApiModelProperty("学生年龄")
        private Integer age;
    }
    
    • service

      public interface StuService {
      
          /**
           * @return 学生集合
           */
          List<Stu> selectAllStu();
      }
      
      • impl
      /**
       * @author 沈晨曦
       * @version 2021/4/8 10:22:21
       */
      @Service
      public class StuServiceImpl implements StuService {
          @Autowired
          private StuMapper stuMapper;
          @Override
          public List<Stu> selectAllStu() {
      		
              List<Stu> stus = stuMapper.selectAllStu();
              // 异常判断
              if (stus==null||stus.size()==0){
                  return null;
              }
              return stus;
          }
      }
      
    • mapper

    /**
     * stu的dao层
     *
     * @author 沈晨曦
     * @version 2021/4/8 10:06:55
     */
    @Mapper
    public interface StuMapper {
        /**
         * @return 学生信息列表
         */
        @Select("select id,name,age from stu")
        List<Stu> selectAllStu();
    }
    
    
    • Controller
    /**
     * 学生表Controller
     *
     * @author scx
     * @version 2021/4/8 10:01:44
     */
    @Api(value = "学生信息",tags = "stu",description = "学生信息api")
    @RestController
    @RequestMapping("/springboot")
    public class StuController {
    
        @Autowired
        private StuService stuService;
        @ApiOperation(value = "查询所有学生信息",notes = "查询所有学生信息")
        @GetMapping("/selectAll")
        public List<Stu> selectAllStu(){
            List<Stu> stus = stuService.selectAllStu();
            return stus;
        }
    }
    
    
    

    打开浏览器访问:
    默认地址:http://localhost:8083/swagger-ui.html#/
    由于我在配置文件配了api/v1
    http://localhost:8083/api/v1/swagger-ui.html#/
    spring boot全面复习_第1张图片

3. 异常
APPLICATION FAILED TO START
***************************

Description:

Field stuService in com.example.controller.StuController required a bean of type 'com.example.service.StuService' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.example.service.StuService' in your configuration.

原因:@Service注解加在了service接口类上

解决:把@Servlice加在service实现类

4. 延伸
  • 接口调试工具:
    • postman:接口调试工具,功能较为单一,但是方便好用;
    • Swagger:
    • Knife4j:
      • 是Swagger的增强,功能更丰富,
      • 文档说明:根据Swagger规范,详细列出接口文档的说明:接口地址、类型、请求示例、请求参数、响应示例、响应参数、响应码等;
      • 在线调试:在线接口联调。自动解析当前接口参数:表单验证、调用参数可返回接口相应内容、headers、Curl请求命令;
      • 个性化配置:可自定义UI的相关显示信息
      • 离线文档:根据标准规范,生成在线markdown离线接口文档;
      • 接口排序:如一个注册功能包含多个步骤,可以根据Swagger提供的接口排序规则实现接口排序。step化接口操作,方便其他开发者进行接口对接;

08 unittest

这一章主要讲了单元测试

1. 测试:创建一个spring工程、一个spring boot工程,观察两者的不同
  • spring:

    • pom:Junitspring - test两个坐标
    • test类:加入@Runwith(SpringRunner.class)获取spring的执行器即可
    import org.junit.Test;
    
    
  • spring boot:

    • pom:spring-boot-starter-test即可
    • test类:
      • @SpringBootTest注解,指定启动类,
      • @Runwith获取执行器
      • spring-boot-starter-test依赖下的test与spring test所属包不同
    import org.junit.jupiter.api.Test;
    
    

探索到这里发现@SpringRunner底层继承自@SpringJunit4JClassRunner,且并未做增强扩展;源码:

public final class SpringRunner extends SpringJUnit4ClassRunner {
    public SpringRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }
}

异常:
  • 异常环境:搭建好一个maven工程,不导入任何依赖,创建一个类,sout(1)

  • 异常:需要发行版jdk1.8

  • 解决:

    • 检查所有需要jdk的地方:Project Settings->project、Modules、SDKs
    • Modules是用的1.5,修正后还是报此异常
    • 在pom文件加入jdk版本,运行正常
    <properties>
        <java.version>1.8java.version>
    properties>
    
    
    • 又把这段代码去掉,运行也正常,推测应该在修正jdk版本后按下Ctrl+shift+F9对编译保存
2. Junit常用注解

@BeforeClass:针对所有测试,只执行一次,且必须为static void

@Before:初始化方法,执行当前测试类的每个测试方法前执行。

@Test:测试方法,在这里可以测试期望异常和超时时间

@After:释放资源,执行当前测试类的每个测试方法后执行

@AfterClass:针对所有测试,只执行一次,且必须为static void

@Ignore:忽略的测试方法(只在测试类的时候生效,单独执行该测试方法无效)

@RunWith:可以更改测试运行器 ,缺省值 org.junit.runner.Runner

  • 一个单元测试类执行顺序为:

@BeforeClass(必定执行) –> @Before –> @Test –> @After –> @AfterClass

3. test参数

这些参数在只导入了spring-boot-starter-test下,无法使用,因为此依赖的test所属包为:

import org.junit.jupiter.api.Test;

  • timeout:单位毫秒值,超时测试,当一个测试方法超出设置时间则抛出异常
org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds

  • expected:异常测试,属性值为异常类,测试方法如果抛出预设的异常则会测试成功;

09-springmvc

1. 日志输出
  • @Slf4j

    • 日志输出
    • 好处,可以替换自定义的logger
    private final Logger logger = LoggerFactory.getLogger(LoggerTest.class);
    
    
    • 需要导入lombok依赖才能使用;
    • logger传统方式实现日志
    @Test
    public void loggerTest(){
        logger.debug("debug");//默认日志级别为info
        logger.info("info");
        logger.error("error");
        logger.warn("warn");
    }
    
    
    • @slf4j方式
    @Test
    public void test2(){
        log.debug("debug");//默认日志级别为info
        log.info("info");
        log.error("error");
        log.warn("warn");
    }
    
    
1.1 @slf4j和log4j对比
  • @Log4j是具体的日志实现,而@Slf4j是一个抽象的,它允许程序使用任意一个日志类库,使程序更独立;

  • @Slf4j可以使用占位符{},减少代码中字符串连接次数,在运行时会被提供的实际字符串被替换;

  • 好处:

    • 降低了字符串连接次数;
    • 节省新建String对象;由于Sting不可变,如果有不常用的字符串,就造成内存消耗;
    • 使用@Slf4j,可以在运行时延迟字符串的建立,只有需要String对象才被建立;
  • 使用对比:

    • log4j
    if (logger.isDebugEnabled()) {
        logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
    }
    
    
    • slf4j
    logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
    
    
  • 如何集成slf4j

    • pom
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
    </dependency>
    
    
    • 在要使用日志输入的地方加上@Slf4j,使用示例:
    @Slf4j
    @Service
    public class StuServiceImpl implements StuService {
        @Autowired
        private StuMapper stuMapper;
        @Override
        public List<Stu> selectAllStu() {
    
            List<Stu> stus = stuMapper.selectAllStu();
            if (stus==null||stus.size()==0){
                log.info("学生表里无数据");
                return null;
            }
            log.info("查询学生列表成功");
            return stus;
        }
    }
    
    
    • 控制台结果
    2021-04-08 15:09:33.306  INFO 3424 --- [nio-8081-exec-7] com.example.service.impl.StuServiceImpl  : 查询学生列表成功
    
    
2. spring mvc
2.1 接收参数的注解

一个请求由:协议、uri、queryParam、header、cookie、body组成

  • 如果不加注解,如这样,有参数则封装进去,无参数则不封
// http://localhost:8080/springboot/noannotation?id=1&name=dd&age=22
public User noAnnotation( User user)

  • @RequestParam:接收**(queryParam)**请求路径上的参数,如果参数名与方法上的参数名不匹配可以在此注解里指定name
// http://localhost:8080/springboot/requestparam?name=scx&age=22
public User RequestParam(@RequestParam String name, @RequestParam int age)

  • @PathVariable:接收uri上的参数;restful风格,通常用于拼接id,
// http://localhost:8080/springboot/pathvariable/scx/22
public User PathVariable(@PathVariable String name,@PathVariable int age)

  • @RequestBody:接收请求体(body)里的参数,可以通过开发者模式观察到请求的请求体请求头等信息;
@PostMapping("/requestbody")
public User RequestBody(@RequestBody @Valid User user)

2.2校验参数的注解
  • @Valid:用于验证实体类上的对应注解是否符合要求,不符合要求会报错,并返回我们给出的message错误提示信息;
@PostMapping("/requestbody")
public User RequestBody(@RequestBody @Valid User user)

public class User {
    private String id;  
 
    @NotBlank(message = "密码不能为空")
    private String password;
}

  • @Null:限制只能为null
  • @NotNull:限制必须不为null
  • @AssertFalse:限制必须为false
  • @AssertTrue:限制必须为true
  • @DecimalMax(value):限制必须为一个不大于指定值的数字
  • @DecimalMin(value):限制必须为一个不小于指定值的数字
  • @Digits(integer,fraction):限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction;
  • @Future:限制必须是一个将来的日期
  • @Max(value:限制必须为一个不大于指定值的数字
  • @Min(value):限制必须为一个不小于指定值的数字
  • @Past:限制必须是一个过去的日期
  • @Pattern(value):限制必须符合指定的正则表达式
  • @Size(max,min):限制字符长度必须在min到max之间
  • @Past:验证注解的元素值(日期类型)比当前时间早
  • @NotEmpty: 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
  • @NotBlank:验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty
  • @NotBlank:只应用于字符串且在比较时会去除字符串的空格
  • @Email:验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
2.3 自定义校验注解
  • 定义注解类
/**
 * @author scx
 */
@Constraint(validatedBy = {MyConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno4Valid {
    String message();
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

  • 定义校验类
@Slf4j
public class MyConstraintValidator implements ConstraintValidator<MyAnno4Valid, Object> {
    @Autowired
    private StuService stuService;

    @Override
    public void initialize(MyAnno4Valid constraintAnnotation) {
        System.out.println("初始化");
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        System.out.println("校验开始");
//        自定义校验规则
        if ((int) value < 0) {
            log.error("年龄小于0");
            return false;
        }
        return true;
    }
}

  • 实体类加上自定义的校验注解
@Data
public class Stu {
    @ApiModelProperty("stu主键ID")
    @TableId(type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("学生姓名")
    private String name;
    @ApiModelProperty("学生年龄")
    @MyAnno4Valid(message = "年龄不能小于0")
    private Integer age;
}

  • Controller编写一个方法接收参数并校验
@ApiOperation(value = "新增一条学生信息")
@PostMapping("/insert")
public String insert(@RequestBody @Valid Stu stu) {
    boolean b = stuService.insert(stu);
     if (b){
         return "成功";
     }
    return "失败";
}

  • 测试的时候把age写为-1,测试结果,以下是Swagger返回的错误码片段
"defaultMessage": "年龄不能小于0",
      "objectName": "stu",
      "field": "age",
      "rejectedValue": -1,
      "bindingFailure": false,
      "code": "MyAnno4Valid"

  • 控制台日志打印
2021-04-08 16:36:34.001 ERROR 17544 --- [nio-8081-exec-2] c.example.myAnno.MyConstraintValidator   : 年龄小于0

观察控制台,可以看出initialize()只执行一次,而isValid()方法每次请求都会执行;

3. 总结

看似简单的注解,里面的东西不少,大部分好用的注解都是基于最原始的注解进行增强优化,今天学习到比较有意思的就是自定义校验注解,以后在项目中可以自己封装一些工具类对参数进行校验;

你可能感兴趣的:(spring,boot,java,spring,boot)