学习笔记根据此demo示例撰写
https://github.com/liushuijinger/springboot
标题为spring boot实战项目的章节名
这一章为springboot入门案例,了解到了springboot基础架子的搭建,
pom
配置属性
编码
启动类
controller:主要涉及到几个controller层的注解
@RestController
:标识此类是一个controller类,并将此类的返回结果返回为json格式;跟进此注解,会看到此注解类同时添加了@Controller
和@ResponseBody
注解;
@Controller
:
@Component
注解类似;handleMapping会扫描有@Controller的类,如果带有@RequestMapping
注解,分发处理器会根据资源路径来进行分发到对应的方法上;@ResponseBody
:此注解能将返回值转为json格式;延伸:@RestController
和@Controller
区别,应用场景?
@GetMapping
:
作用:可以设置映射路径接受对应get请求;
理解:此注解是一个组合注解,通过在注解里设置属性达到只接收get请求;@PostMapping
等同理
@RequestMapping(method = {RequestMethod.GET}
@RequestParam
:
@RequestBody
注解类似,都是获取参数,不过获取的范围不一样;总结:
@ PathVariable
测试类:主要用了通知类注解来测试,通过观察console打印结果可以看出执行顺序
@BeforeClass
:在类加载之前就出现,一个类中只运行一次,就算抛异常也一定会执行;且必须声明为public static;@Before
:在标注了@Test注解方法前执行,可以有多个方法标注@Before,执行顺序不确定,必须声明为public并且非static;@AfterClass
:类加载完毕后执行,只执行一次,一定会执行;@After
:在标有@Test方法执行之后执行;测试:
@BeforeClass
注解里手动制造除零异常@BeforeClass
和@AfterClass
都会执行,其余注解方法不执行;总结:
@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 | 默认值 |
<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
编码
/**
* @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();
}
}
/**
* 学生实体类
*
* @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();
}
/**
* @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
*
* @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#/
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实现类
这一章主要讲了单元测试
spring:
Junit
和spring - test
两个坐标@Runwith(SpringRunner.class)
获取spring的执行器即可import org.junit.Test;
spring boot:
spring-boot-starter-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
解决:
<properties>
<java.version>1.8java.version>
properties>
Ctrl+shift+F9
对编译保存@BeforeClass
:针对所有测试,只执行一次,且必须为static void
@Before
:初始化方法,执行当前测试类的每个测试方法前执行。
@Test
:测试方法,在这里可以测试期望异常和超时时间
@After
:释放资源,执行当前测试类的每个测试方法后执行
@AfterClass
:针对所有测试,只执行一次,且必须为static void
@Ignore
:忽略的测试方法(只在测试类的时候生效,单独执行该测试方法无效)
@RunWith
:可以更改测试运行器 ,缺省值 org.junit.runner.Runner
@BeforeClass
(必定执行) –> @Before
–> @Test
–> @After
–> @AfterClass
这些参数在只导入了spring-boot-starter-test
下,无法使用,因为此依赖的test所属包为:
import org.junit.jupiter.api.Test;
timeout
:单位毫秒值,超时测试,当一个测试方法超出设置时间则抛出异常org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds
expected
:异常测试,属性值为异常类,测试方法如果抛出预设的异常则会测试成功;@Slf4j
:
private final Logger logger = LoggerFactory.getLogger(LoggerTest.class);
@Test
public void loggerTest(){
logger.debug("debug");//默认日志级别为info
logger.info("info");
logger.error("error");
logger.warn("warn");
}
@Test
public void test2(){
log.debug("debug");//默认日志级别为info
log.info("info");
log.error("error");
log.warn("warn");
}
@Log4j
是具体的日志实现,而@Slf4j
是一个抽象的,它允许程序使用任意一个日志类库,使程序更独立;
@Slf4j
可以使用占位符{}
,减少代码中字符串连接次数,在运行时会被提供的实际字符串被替换;
好处:
使用对比:
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
如何集成slf4j
<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 : 查询学生列表成功
一个请求由:协议、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)
@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格式/**
* @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;
}
@ApiOperation(value = "新增一条学生信息")
@PostMapping("/insert")
public String insert(@RequestBody @Valid Stu stu) {
boolean b = stuService.insert(stu);
if (b){
return "成功";
}
return "失败";
}
"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()方法每次请求都会执行;
看似简单的注解,里面的东西不少,大部分好用的注解都是基于最原始的注解进行增强优化,今天学习到比较有意思的就是自定义校验注解,以后在项目中可以自己封装一些工具类对参数进行校验;