SpringBoot入门
配置文件
在Spring Boot中多环境配置文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识,比如:
application-dev.properties:开发环境
application-test.properties:测试环境
application-prod.properties:生产环境
具体加载的配置文件,需要在通用配置application.properties中设置spring.profiles.active属性来设置,其值对应{profile}值。
也可通过在启动类中设置
SpringApplication app = new SpringApplication(Application.class); //使开发环境的配置文件生效 app.setAdditionalProfiles("dev"); //使测试环境的配置文件生效 //app.setAdditionalProfiles("test"); ConfigurableApplicationContext context = app.run(args);
通过@Profile注解可控制bean在哪个配置文件生效的时候被装载。比如@Profile("dev")就是application-dev.properties生效时加载注解下的bean。
@Autowired
当一个Interface有多个实现类时,可通过@Service("名称")给这个实现类bean命名。不进行命名时,名称默认为首字母小写的类名。
@Autowired这个接口时,可通过Map<String, 接口名>的形式,将这些实现类都注入进来。其中String就是你自己给这个实现类取的名字。通过Map根据key获取你所需要的实现类即可。
(这里可以构造一个工厂,就是用一个Context类把上面所说的封装起来,这个工厂专门用于传入key获取实现类)
当然你也可以不用Map,使用List<接口名>,但这样获取具体实现类时会比较麻烦。
当然你也可以使用@Qualifier("名称")指定自己所需要装载的实现类。
当然你也可以在实现类上加上@Primary告诉SpringBoot默认装载哪个实现类。
@ConditionalOnProperty
这个注解可用于有选择性地装载bean,比如
@ConditionalOnProperty(prefix = "sms",name = "provider",havingValue = "tencent")
就是当配置文件中 sms.provider这个属性值为tencent时才装载下面的注解下的bean
@ConfigurationProperties
这个注解可用于自动给需要装载的bean配置属性,它会调用bean中的set方法。比如:
public class TencentConfig { private String key1; private String key2; public String getKey1() { return key1; } public String getKey2() { return key2; } public void setKey1(String key1) { this.key1 = key1; } public void setKey2(String key2) { this.key2 = key2; } }
配置文件中
sms.tencent.key1 = 1
sms.tencent.key2 = 2
那么
@Bean @ConditionalOnProperty(name = "sms.provider",havingValue = "tencent") @ConfigurationProperties(prefix = "sms.tencent") public TencentConfig tencentConfig(){ return new TencentConfig(); }
返回的TencentConfig这个bean中,属性key1值被设为1,属性key2值被设为2。
@EnableConfigurationProperties
@EnableConfigurationProperties 注解使ConfigurationProperties注解生效
也可以指定Bean生效 @EnableConfigurationProperties({ConfigBean.class})
除了上述方式之外,还可以在@ConfigurationProperties注解的类上加@Component注解,使@ConfigurationProperties生效。
注:@Component注解表明一个类会作为组件类,然后spring会为该类创建bean。
@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。
类上添加Java Doc
具体方法可以百度一下
这里给个我自己用的模板,创建类的时候自动就生成了。
/**
*TODO
*
*@author ${USER}
*@date ${DATE} ${TIME}
*@version 1.0
*/
author的${USER}可在vmoptions中修改,加一行
-Duser.name=要修改的名字
方法上添加Java Doc直接在写完方法后小键盘依次按 / * * 回车
@RequestBody @RequestParam
@RequestBody 用于接收body中的 json 数据,映射到实体类中。它调用的是实体类的无参构造方法构造对象,然后通过set方法给属性赋值。
当 json 中有某个属性而实体类中无时,不会报错
当 json 中无某个属性值而实体类中有时,实体类中该属性如果是对象类型则值为null,基本类型值为0
实体类可配合使用JSR303校验
@RequestParam 用于接收key-value数据,该注解的属性 required 的值默认为true,即必须传这个字段,即使没有值。修改为false可不传这个字段。
其实不加@RequestParam也可以,加了只是可以实现更多定制化操作。
接口参数为一个实体类对象,前不加@RequestBody注解,接受的也是key-value数据
@Controller
@Api(tags = "Controller名") @RestController @Slf4j @RequestMapping("/api/controller") public class TestController { @ApiOperation(value = "调用方法", notes = "调用方法", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
// 给swagger看的
@ApiResponse({@ApiResponse(code = 404, message = "所请求的页面不存在"),
@ApiResponse(code = 500, message = "其他异常")})
@RequestMapping(path = "/{methodId}", method = RequestMethod.POST)
public void method(@ApiParam("请求参数") @RequestBody @Valid InfoDO info, //body里json
@ApiParam("额外参数") @RequestParam("extra") String extra, //url
@ApiParam("方法id") @PathVariable Integer methodId) { //url
}
}
Date & TimeStamp
Date 对应 mysql 中的 date 类型,默认存储格式是 yyyy-MM-dd
TimeStamp 对应 mysql 中的 datetime 类型,默认存储格式是 yyyy-MM-dd HH:mm:ss
实际上 Date 也可以存入 datetime 中,因为 Date 本身含有时分秒
String 可以直接存入 date 中,需要格式正确,mysql会自动转换
全局异常处理
<!-- Spring Boot Web 依赖 核心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Test 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
@ControllerAdvice
使用该注解可捕获全局异常 该注解标注在类上,在该类的方法上标注@ExceptionHandler表示对每一种异常的处理
与@Controller相同,@ControllerAdvice也有Rest风格:@RestControllerAdvice,自己定义一下返回dto
@ExceptionHandler({BindException.class}) public ResponseMsg handleException(BindException e) { try { log.warn("参数绑定错误", e); FieldError fieldError = e.getBindingResult().getFieldError(); return ResponseMsg.createError(fieldError.getDefaultMessage()); } catch (Exception ex) { log.error("系统错误", ex); return ResponseMsg.createError(MyException.SYSTEM_EXCEPTION); } }
@Valid & @Validated
1、@Validated写在类上,只能作用于类中方法参数,且参数只能是java的原生类型及其对应的引用类,如Integer、String、int等。无法作用到参数类型之内。
@Validated @RestController @Slf4j @RequestMapping("/api/controller") public class TestController { @RequestMapping(path = "/id", method = RequestMethod.POST) public void method(@NotBlank(message = "id不能为空") String id){ } }
2、@Valid和@Validate,都可以直接写在Dto之前,对Dto内部的参数进行校验。
@RestController @Slf4j @RequestMapping("/api/controller") public class TestController { @RequestMapping(path = "/id", method = RequestMethod.POST) public void method(@Valid/@Validated Dto dto){ } }
3、对与Dto内部的另外一个Dto内部的参数校验,即嵌套校验,需要在Dto内部的这个参数前加@Valid。
@Data public class FirstDto { @NotBlank(message = "username不能为空") private String username; @Valid //检验secondDto内部
@NotNull(message = "secondDto不能为null")//单检验secondDto本身 private SecondDto secondDto; @Valid //检验每个ThirdDto内部
@NotEmpty(message = "thirdDtos不能为空")//单检验thirdDtos本身 private List<ThirdDto> thirdDtos; }
4、@Validated 分组校验
//定义接口作为分组 public interface Select { } public interface Insert { }
//定义校验顺序,如果Select组失败,则Update组不会再校验
@GroupSequence({Select.class, Update.class})
public interface group {
{ //数据层 @Data public class Person { @NotNull(message = "id不能为空",groups = Select.class) private Integer id; @NotEmpty(message = "name不能为空",groups = Insert.class) private String name; } //Controller层 @GetMapping("/select") public void select(@Validated({Select.class}) Person person) { return person; } @GetMapping("/insert") public void insert(@Validated(Insert.class) Person person) { return person; }
@Transactional
Spring Aop事务管理,在进入方法前开启事务,方法结束后视执行情况提交或者回滚事务。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。如IOException、SQLException等以及用户自定义的Exception异常。对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过。
当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
@Transactional(readOnly = true, rollbackFor = Exception.class, transactionManager = "mysqlTransactionManager")
readOnly 只读事务 不允许写操作。
rollbackFor 遇见某个异常就回滚,这里注意有些异常是throwable类型的,无法回滚
transactionManager 指定事务管理器
@Transactional(propagation=Propagation.REQUIRED) //如果有事务, 那么加入事务, 没有的话新建一个(默认) @Transactional(propagation=Propagation.REQUIRES_NEW) //不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
当调用者存在@Transactional,被调用者也存在@Transactional,且处于共同事务时,被调用者回滚后,调用者提交事务会报错异常
Transaction rolledback because it has been marked as rollback-only
根本原因是被调用方法抛出异常后该共同事务TransactionStatus被RollbackOnly设置为true,表示该事务只能被回滚,不能够提交。
因此,一般来说,被调用者不需要添加事务,直接使用调用者的事务即可。
spring事务的7种传播行为MySQL中事务详解