SpringBoot第二部分
Mybatis框架集成
Mybatis是作用于持久层的的框架,主要与数据库进行交互,可以简化程序编写时与数据库的交互
配置与实现
配置依赖项
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency>
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.13</version> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency>
|
配置application.yml
spring: datasource: type: com.mchange.v2.c3p0.ComboPooledDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/DBName?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 username: root password: root
mybatis: mapper-locations: classpath:/mappers/*.xml type-aliases-package: com.xxxk.springframework.po configuration: map-underscore-to-camel-case: true
pagehelper: helper-dialect: mysql
|
Mybatis通过XML文件配置与接口的映射执行sql语句,在resources/mappers目录下添加xml配置,id属性与接口方法名对应
<?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.xxxx.springboot.dao.UserMapper"> <select id="queryUserByUserName" parameterType="string" resultType="com.example.User"> select id,user_name,user_pwd from tb_user where user_name=#{userName} </select> </mapper>
|
创建接口
public interface UserMapper { User queryUserByUserName(String userName); }
|
Service层注入接口调用方法
@Service public class UserService {
@Resource private UserMapper userMapper;
public User queryUserByUserName(String userName) { return userMapper.queryUserByUserName(userName); } }
|
最后的启动类需要@MapperScan扫描指定包中创建的接口
对于分页查询,将查询的参数封装在一个类中,
public class UserQuery {
private Integer pageNum = 1; private Integer pageSize = 10; private String userName; }
|
定义接口
public interface UserMapper { public List<User> selectByParams(UserQuery userQuery); }
|
配置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.xxxx.springboot.dao.UserMapper"> <select id="selectByParams" parameterType="com.xxxx.springboot.query.UserQuery" resultType="com.xxxx.springboot.po.User"> select * from tb_user <where> <if test="null != userName and userName !=''"> and user_name like concat('%',#{userName}, '%') </if> </where> </select> </mapper>
|
Service层进行对应实现
@Service public class UserService {
@Resource private UserMapper userMapper;
public PageInfo<User> queryUserByParams(UserQuery userQuery){ PageHelper.startPage(userQuery.getPageNum(), userQuery.getPageSize()); return new PageInfo<User>(userMapper.selectByParams(userQuery)); } }
|
Swagger2文档构建
在 Spring Boot 多终端项目中,通常使用一套 API 接口为不同终端提供服务。为方便联调测试,后端需提供接口文档,可通过 Swagger2 自动生成,免去手动编写文档的繁琐
环境配置
配置依赖项
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
|
添加配置类
@Configuration @EnableSwagger2 public class Swagger2 { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.xxxx.springboot.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("用户管理接口API文档") .version("1.0") .build(); } }
|
常用注解
@Api
用在请求的类上,具有tag属性,说明该类的作用
@Api(tags="APP用户注册Controller")
|
@ApiOperation
用在请求的方法上,说明方法的作用,value属性说明方法的作用,notes属性为方法的备注说明
@ApiOperation(value="用户注册", notes="手机号、密码都是必填项,年龄是选填项,但必须是数字")
|
@ApiImplicitParams
用在请求的方法上,包含一组参数说明,@ApiImplicitParam用在 @ApiImplicitParams 注解中,指定一个请求参数的配置信息
| 属性名 |
说明 |
示例值 |
| name |
参数名 |
"userId" |
| value |
参数的汉字说明、解释 |
"用户ID" |
| required |
参数是否必须传 |
true / false |
| paramType |
参数放在哪个地方 |
"header" / "query" / "path" / "body" / "form" |
| 对应注解 |
请求参数获取方式 |
@RequestHeader / @RequestParam / @PathVariable |
| datatype |
参数类型,默认为String |
"Integer" / "String" / "Boolean" |
| defaultValue |
参数的默认值 |
"0" / "default" |
paramType 对应关系
| paramType 值 |
对应注解 |
使用场景 |
header |
@RequestHeader |
从请求头获取参数 |
query |
@RequestParam |
从URL查询参数获取 |
path |
@PathVariable |
从URL路径获取(RESTful接口) |
body |
@RequestBody |
从请求体获取(不常用) |
form |
- |
表单提交(不常用) |
示例如下
@ApiImplicitParams({ @ApiImplicitParam(name="mobile", value="手机号", required=true, paramType="form"), @ApiImplicitParam(name="password", value="密码", required=true, paramType="form"), @ApiImplicitParam(name="age", value="年龄", required=true, paramType="form", dataType="Integer") })
|
@ApiResponses
用于请求的方法上,表示一组响应,@ApiResponse用在 @ApiResponses 中,一般用于表达一个错误的响应信息
| 属性名 |
说明 |
示例值 |
| code |
HTTP 状态码 |
400, 404, 500 |
| message |
响应信息描述 |
"请求参数没填好", "用户不存在" |
| response |
抛出的异常类(可选) |
IllegalArgumentException.class, UserNotFoundException.class |
示例如下
@ApiOperation(value = "select请求", notes = "多个参数,多种的查询参数类型") @ApiResponses({ @ApiResponse(code = 400, message = "请求参数没填好"), @ApiResponse(code = 404, message = "请求路径没有或页面跳转路径不对") })
|
@ApiModel
用于响应类上,表示一个返回响应数据的信息(这种一般用在post创建的时候,使用@RequestBody这样的场景,请求参数无法使用@ApiImplicitParam注解进行描述的时候),@ApiModelProperty用在属性上,描述响应类的属性
@ApiModel(description = "返回响应数据") public class RestMessage implements Serializable { @ApiModelProperty(value = "是否成功") private boolean success = true; @ApiModelProperty(value = "返回对象") private Object data; @ApiModelProperty(value = "错误编号") private Integer errCode; @ApiModelProperty(value = "错误信息") private String message; }
|
启动项目,访问项目页面/swagger_ui.html即可查看接口文档,例如http://localhost:8080/swagger_ui.html
应用热部署
热部署是指在不重启应用的情况下,实时更新代码、修复缺陷或增加功能,使修改立即生效
配置与使用
添加DevTools依赖项
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
|
同时在 plugin 中添加 devtools 生效标志
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin>
|
全局文件配配置,将 spring.devtools.restart.enabled 设为 false,则重启功能会被禁用,但类加载器仍然会初始化
spring: devtools: restart: enabled: true additional-paths: src/main/java poll-interval: 3000 quiet-period: 1000
|
IDEA配置自动编译,File->Strrings->Compiler->Build Project automatically
Help -> ind Action ->搜索Registry->勾选compiler.automake.allow.when.app.running
单元测试
做过 web 项目开发的对于单元测试都不陌生。SpringBoot 框架对单元测试也提供了良好的支持,可以快速、恰当地测试业务代码功能
配置
添加依赖项
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency>
|
Service业务方法测试
使用@Runwith注解指定特定的类进行单元测试,在Spring环境下的单元测试使用SpringRunner.class,使用@SpringBootTest注解指定入口类,通常是 @SpringBootApplication 注解的主类
使用@Before注解设定在测试方法执行前的行为,@After注解设定在测试方法执行后的行为,@Test注解标记要测试的方法
控制层接口方法测试
视图层代码使用 MockMvc 进行测试,其实现了对http请求的模拟,不需依赖网路环境,使用@AutoConfigureMvc注解进行使用
@RunWith(SpringRunner.class) @SpringBootTest(classes = {Starter.class}) @AutoConfigureMockMvc public class TestUserController { private Logger log = LoggerFactory.getLogger(TestUserController.class);
@Autowired private MockMvc mockMvc;
@Test public void apiTest01() throws Exception { MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/user/list") .contentType("text/html") .accept(MediaType.APPLICATION_JSON);
ResultActions perform = mockMvc.perform(request);
perform.andExpect(MockMvcResultMatchers.status().isOk());
MvcResult mvcResult = perform.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
log.info("响应状态: {}", response.getStatus()); log.info("响应内容: {}", response.getContentAsString()); }
}
|
分布式缓存Ehcache集成
EhCache 是一个成熟的 Java 缓存框架,最初从 Hibernate 发展而来。它是进程内缓存系统,提供了多种灵活的缓存管理方案,包括内存存储、磁盘文件存储和分布式存储方式,具有快速、简单的特点
相关注解
@CacheConfig 注解
标注在类上,用于配置该类中所有缓存方法的公共属性,例如设置缓存名称
@CacheConfig(cacheNames = "users") public interface UserService { ... }
|
@Cacheable 注解
应用到读取数据的方法上,即可缓存的方法,如查找方法。它首先从缓存中读取数据,如果没有命中,再调用相应方法获取数据,然后把数据添加到缓存中
| 参数 |
作用 |
| value |
指定缓存存储的集合名 |
| cacheNames |
value 的别名(Spring 4 新增) |
| key |
缓存对象在 Map 中的 key 值 |
| condition |
缓存条件(调用前判断) |
| unless |
缓存条件(调用后判断) |
| keyGenerator |
指定自定义 key 生成器 |
| cacheResolver |
指定缓存解析器 |
| cacheManager |
指定缓存管理器 |
@CachePut 注解
应用到写数据的方法上,如新增、修改方法。调用方法时会自动把相应的数据放入缓存。@CachePut 的参数与 @Cacheable 类似
@CachePut(value = "user", key = "#user.id") public User save(User user) { users.add(user); return user; }
|
@CacheEvict 注解
应用到移除数据的方法上,如删除方法。调用方法时会从缓存中移除相应的数据
@CacheEvict(value = "user", key = "#id") void delete(final Integer id);
|
除了同 @Cacheable 一样的参数之外,@CacheEvict 还有下面两个参数:
allEntries非必需,默认为 false。当为 true 时,会移除所有数据
beforeInvocation非必需,默认为 false,会在调用方法之后移除数据。当为 true 时,会在调用方法之前移除数据
@Caching注解
用于组合多个 Cache 注解使用
@Caching( put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.age") } )
|
环境配置
添加依赖项
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
|
resources目录下新建ehcache.xml,用于相关配置
进行全局配置
spring: cache: ehcache: config: classpath:ehcache.xml
|
启动类启用缓存,添加@EnableCaching注解
@SpringBootApplication @EnableCaching public class Starter {
public static void main(String[] args) { SpringApplication.run(Starter.class, args); } }
|
此外,bean对象均需实现序列化接口用于存储对象
定时调度Quartz集成
配置依赖项
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
|
定义定时任务类,实现Job接口,在execute()方法中实现任务逻辑
public class MyFirstJob implements Job {
private Logger log = LoggerFactory.getLogger(MyFirstJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.info(sdf.format(new Date()) + "-->" + "Hello Spring Boot Quartz..."); } }
|
创建配置类,实例化JobDetail并定义Trigger注册到scheduler
@Configuration public class QuartzConfig {
@Bean public JobDetail jobDetail1() { return JobBuilder.newJob(MyFirstJob.class).storeDurably().build(); }
@Bean public Trigger trigger1() { SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1) .repeatForever(); return TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .withSchedule(scheduleBuilder) .forJob(jobDetail1()) .build(); } }
|
事务控制与全局异常
事务控制
使用@Transactional注解进行事务控制,在遇到异常时进行回滚,确保数据同步
全局异常
SpringMVC 中对异常统一处理提供了相应的方式,推荐使用实现 HandlerExceptionResolver 接口的方式,对代码侵入性较小
@ControllerAdvice注解
该注解组合了 @Component 注解功能,最常用的就是作为全局异常处理的切面类。同时通过该注解可以指定包扫描的范围。@ControllerAdvice 约定了几种可行的返回值,如果是直接返回 model 类的话,需要使用 @ResponseBody 进行 JSON 转换
@ExceptionHandler注解
该注解在 Spring 3.X 版本引入,在处理异常时标注在方法级别,代表当前方法处理的异常类型有哪些。具体应用以 Restful 接口为例,测试保存用户接口
定义全局异常处理类
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) @ResponseBody public ResultInfo exceptionHandler(Exception e) { ResultInfo resultInfo = new ResultInfo(); resultInfo.setCode(300); resultInfo.setMsg("操作失败!"); if(e instanceof ParamsException) { ParamsException pe = (ParamsException) e; resultInfo.setMsg(pe.getMsg()); resultInfo.setCode(pe.getCode()); } return resultInfo; } }
|
数据校验Validation集成
在日常项目开发中,对于前端提交的表单,后台接口接收到表单数据后,为了程序的严谨性,通常后端会加入业务参数的合法校验操作来避免程序的非技术性 bug。这里对于客户端提交的数据校验,Spring Boot 通过 spring-boot-starter-validation 模块包含了数据校验的工作
JSR303:JSR303 是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如 @Null、@NotNull、@Pattern,位于 javax.validation.constraints 包下。JSR-349 是其升级版本,添加了一些新特性
Hibernate Validation:Hibernate Validation 是对这个规范的实现,并增加了一些其他校验注解,如 @Email、@Length、@Range 等等
Spring Validation:Spring Validation 对 Hibernate Validation 进行了二次封装,在 Spring MVC 模块中添加了自动校验,并将校验信息封装进了特定的类中
相关注解
| 注解 |
功能描述 |
| @AssertFalse |
可以为 null,如果不为 null 则必须为 false |
| @AssertTrue |
可以为 null,如果不为 null 则必须为 true |
| @DecimalMax |
设置不能超过的最大值 |
| @DecimalMin |
设置不能小于的最小值 |
| @Digits |
必须是数字,且整数部分和小数部分的位数在指定范围内 |
| @Future |
日期必须在当前日期的未来 |
| @Past |
日期必须在当前日期的过去 |
| @Max |
值不得超过指定的最大值 |
| @Min |
值不得小于指定的最小值 |
| @NotNull |
不能为 null(但可以是空字符串、空集合等) |
| @Pattern |
必须满足指定的正则表达式 |
| @Size |
集合、数组、Map 等的 size() 值必须在指定范围内 |
| @Email |
必须是合法的电子邮件格式 |
| @Length |
字符串长度必须在指定范围内 |
| @NotBlank |
字符串不能为 null,且 trim() 后不能等于空字符串 |
| @NotEmpty |
不能为 null,集合、数组、Map 等 size() 不能为 0;字符串 trim() 后可以等于空字符串 |
| @Range |
值必须在指定的范围内 |
| @URL |
必须是一个合法的 URL |
对字段属性值进行校验
public class User implements Serializable { private Integer id; @NotBlank(message = "用户名不能为空!") private String userName;
@NotBlank(message = "用户密码不能为空!") @Length(min = 6, max = 10, message = "密码长度至少6位但不超过10位!") private String userPwd; @Email private String email; }
|
对接口方法的形参进行校验
@PostMapping("user") @ApiOperation(value = "用户添加") @ApiImplicitParam(name = "user", value = "用户实体类", dataType = "User") public ResultInfo saveUser(@Valid User user) { ResultInfo resultInfo = new ResultInfo(); return resultInfo; }
|
不满足数据校验的情况会抛出Bind Exception异常,可以结合全局异常对其进行处理