SpringBoot
Spring Boot 概念&特点
框架概念
随着动态语言流行(Ruby,Scala,NodeJs等),Java 开发变得相对笨重,配置繁琐,开发效率低下,部署流程复杂,以及第三方集成难度也相对较大,针对该环境,Spring Boot被开发出来,其使用“习惯大于配置目标”,借助Spring Boot 能够让项目快速运行起来,同时借助Spring Boot可以快速创建web 应用并独立进行部署(jar包 war 包方式,内嵌servlet 容器),同时借助Spring Boot 在开发应用时可以不用或很少去进行相关xml环境配置,简化了开发,大大提高项目开发效率。
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,让Spring Boot在蓬勃发展的快速应用开发领域(rapid application development)成为领导者.
框架特点
创建独立Spring应用程序、嵌入式Tomcat,Jetty容器、无需部署WAR包、简化Maven及Gradle配置、尽可能自动化配置Spring、直接植入产品环境下的实用功能,比如度量指标、健康检查及扩展配置、无需代码生成及XML配置等,同时Spring Boot不仅对web应用程序做了简化,还提供一系列的依赖包来把其它一些工作做成开箱即用。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Spring Boot的项目必须要将parent设置为Spring Boot的parent,该parent包含了大量默认的配置,简化程序的开发。
导入Spring Boot的web坐标与相关插件
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
添加源代码
@Controller
public class HelloController {
@RequestMapping("hello")
@ResponseBody
public String hello(){
return "Hello Spring Boot";
}
}
创建启动程序
在HelloController.java 所在包下创建StarterApplication.java
@SpringBootApplication
public class StarterApplication
{
public static void main(String[] args) {
SpringApplication.run(Starter.class);
}
}
Spring Boot 配置文件
Spring Boot 默认会读取全局配置文件,配置文件名固定为:application.properties或application.yml,放置在src/main/resources资源目录下,使用配置文件来修改SpringBoot自动配置的默认值;
在resources 资源目录下添加application.properties 文件,配置信息如下:
## 项目启动端口号配置
server.port=8989
## 项目访问上下文路径
server.servlet-path=/mvc
## 数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/hr?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
或者application.yml 文件
## 端口号 上下文路径
server:
port: 8989
servlet:
context-path: /mvc
## 数据源配置
spring:
datasource:
type: com.mchange.v2.c3p0.ComboPooledDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/hr
username: root
password: root
Starter 坐标 & 自动化配置
Starter坐标配置
Spring Boot 引入了全新的Starter坐标体系,简化企业项目开发大部分场景的Starter pom,应用程序引入指定场景的Start pom 相关配置就可以消除 ,通过Spring Boot就可以得到自动配置的Bean。
Web starter
使用Spring MVC来构建RESTful Web应用,并使用Tomcat作为默认内嵌容器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Freemarker Starter & Thymeleaf starter
集成视图技术,引入Freemarker Starter ,Thymeleaf Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
JavaMail邮件发送 Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
引入AOP环境
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 相关starter系列坐标参考 | 名称 | 描述 | | :—- | :—-: | | spring-boot-starter | 核心Spring Boot starter,包括自动配置支持,日志和YAML | | spring-boot-starter-actuator | 生产准备的特性,用于帮我们监控和管理应用 | | spring-boot-starter-amqp | 对”高级消息队列协议”的支持,通过spring-rabbit实现 | | spring-boot-starter-aop | 对面向切面编程的支持,包括spring-aop和AspectJ | | spring-boot-starter-batch | 对Spring Batch的支持,包括HSQLDB数据库 | | spring-boot-starter-cloud-connectors | 对Spring Cloud Connectors的支持,简化在云平台下(例如,Cloud Foundry 和Heroku)服务的连接 | | spring-boot-starter-data-elasticsearch | 对Elasticsearch搜索和分析引擎的支持,包括spring-data-elasticsearch | | spring-boot-starter-data-gemfire | 对GemFire分布式数据存储的支持,包括spring-data-gemfire | | spring-boot-starter-data-jpa | 对”Java持久化API”的支持,包括spring-data-jpa,spring-orm和Hibernate | | spring-boot-starter-data-mongodb | 对MongoDB NOSQL数据库的支持,包括spring-data-mongodb | | spring-boot-starter-data-rest | 对通过REST暴露Spring Data仓库的支持,通过spring-data-rest-webmvc实现 | | spring-boot-starter-data-solr | 对Apache Solr搜索平台的支持,包括spring-data-solr | | spring-boot-starter-freemarker | 对FreeMarker模板引擎的支持 | | spring-boot-starter-groovy-templates | 对Groovy模板引擎的支持 | | spring-boot-starter-hateoas | 对基于HATEOAS的RESTful服务的支持,通过spring-hateoas实现 | | spring-boot-starter-hornetq | 对”Java消息服务API”的支持,通过HornetQ实现 | | spring-boot-starter-integration | 对普通spring-integration模块的支持 | | spring-boot-starter-jdbc | 对JDBC数据库的支持 | | spring-boot-starter-jersey | 对Jersey RESTful Web服务框架的支持 | | spring-boot-starter-jta-atomikos | 对JTA分布式事务的支持,通过Atomikos实现 | | spring-boot-starter-jta-bitronix | 对JTA分布式事务的支持,通过Bitronix实现 | | spring-boot-starter-mail | 对javax.mail的支持 | | spring-boot-starter-mobile | 对spring-mobile的支持 | | spring-boot-starter-mustache | 对Mustache模板引擎的支持 | | spring-boot-starter-redis | 对REDIS键值数据存储的支持,包括spring-redis | | spring-boot-starter-security | 对spring-security的支持 | | spring-boot-starter-social-facebook | 对spring-social-facebook的支持 | | spring-boot-starter-social-linkedin | 对spring-social-linkedin的支持 | | spring-boot-starter-social-twitter | 对spring-social-twitter的支持 | | spring-boot-starter-test | 对常用测试依赖的支持,包括JUnit, Hamcrest和Mockito,还有spring-test模块 | | spring-boot-starter-thymeleaf | 对Thymeleaf模板引擎的支持,包括和Spring的集成 | | spring-boot-starter-velocity | 对Velocity模板引擎的支持 | | spring-boot-starter-web | 对全栈web开发的支持, 包括Tomcat和spring-webmvc | | spring-boot-starter-websocket | 对WebSocket开发的支持 | | spring-boot-starter-ws | 对Spring Web服务的支持 |
传统的maven坐标这里同样适用,如果引入传统maven坐标需要考虑相关配置类的编写
如果引入相关starter坐标这里不存在,可以到这里搜索。
自动化配置
SpringBoot Starter坐标版本查看
前面介绍了SpringBoot Starter相关坐标,引入Starter坐标来简化应用环境的配置。这里以环境搭建spring-boot-starter-web坐标来简单分析SpringBoot 自动化配置过程。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这里引入的web环境坐标不像传统的maven坐标那样包含坐标的版本号,项目中引入的starter 系列坐标对应的版本库统一由父工程坐标统一控制即项目中引入的parent标签。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--
父类项目统一对项目依赖版本统一控制!
-->
<version>2.2.2.RELEASE</version>
</parent>
这里spring-boot-starter-parent 继承spring-boot-dependencies 项目,在spring-boot-dependencies项目中定义了spring-boot-starter-web坐标的版本!
日志配置
在开发企业项目时,日志的输出对于系统bug 定位无疑是一种比较有效的方式,也是项目后续进入生产环境后快速发现错误解决错误的一种有效手段,所以日志的使用对于项目也是比较重要的一块功能。
Spring Boot默认使用LogBack日志系统,如果不需要更改为其他日志系统如Log4j2等,则无需多余的配置,LogBack默认将日志打印到控制台上。如果要使用LogBack,原则上是需要添加dependency依赖的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
因为新建的Spring Boot项目一般都会引用spring-boot-starter
或者spring-boot-starter-web
,而这两个起步依赖中都已经包含了对于spring-boot-starter-logging
的依赖,所以,无需额外添加依赖。
项目中日志信息输出
Starter 启动类中添加Log 日志类,控制台打印日志信息。
package com.xxxx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Starter {
private static Logger logger = LoggerFactory.getLogger(Starter.class);
public static void main(String[] args) {
logger.info("SpringBoot 应用开始启动...");
SpringApplication.run(Starter.class);
}
}
日志输出格式配置
修改application.yml文件添日志输出格式信息配置,可以修改application.yml文件来控制控制台日志输出格式,同时可以设置日志信息输出到外部文件。
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n"
level: debug
file:
path: "."
name: "springboot.log"
SpringBoot 静态资源访问
从入门项目中可以看到:对于Spring Mvc 请求拦截规则为‘/’,Spring Boot 默认静态资源路径如下:
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private final ResourceProperties.Chain chain;
private final ResourceProperties.Cache cache;
即:我们可以在resources 资源目录下存放web 应用静态资源文件。
在resources 目录下创建static 或者public 存放images、js、css等静态资源文件
Mybatis整合&数据访问
使用SpringBoot开发企业项目时,持久层数据访问是前端页面数据展示的基础,SpringBoot支持市面上常见的关系库产品(Oracle,Mysql,SqlServer,DB2等)对应的相关持久层框架,当然除了对于关系库访问的支持,也支持当下众多的非关系库(Redis,Solr,MongoDB等)数据访问操作,这里主要介绍SpringBoot集成Mybatis并实现持久层数据基本增删改查操作。
SpringBoot 整合Mybatis
环境整合配置
Idea 下创建Maven 普通工程 springboot_mybatis
pom.xml 添加核心依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--
mybatis 集成
-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- springboot分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- application.yml 整合配置
## 端口号
server:
port: 9999
## 数据源配置
spring:
datasource:
type: com.mchange.v2.c3p0.ComboPooledDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot_mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: root
## mybatis 配置
mybatis:
mapper-locations: classpath:/mappers/*.xml
type-aliases-package: com.xxxx.springboot.vo
configuration:
## 下划线转驼峰配置
map-underscore-to-camel-case: true
## pageHelper
pagehelper:
helper-dialect: mysql
#显示dao 执行sql语句
logging:
level:
com:
xxxx:
springboot:
dao: debug
源代码添加
- Dao层接口方法定义
com.xxxx.springboot.dao 包下创建UserDao.java 接口声明查询方法
package com.xxxx.springboot.dao;
import com.xxxx.springboot.vo.User;
public interface UserMapper {
// 根据用户名查询用户记录
User queryUserByUserName(String userName);
}
- SQL映射文件添加
resources/mappers 目录下添加UserMapper.xml 配置查询statetment
<?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.xxxx.springboot.vo.User">
select
id,user_name,user_pwd
from t_user
where user_name=#{userName}
</select>
</mapper>
- 添加service 、controller 对应代码
UserService.java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User queryUserByUserName(String userName){
return userMapper.queryUserByUserName(userName);
}
}
UserController.java
@RestController
public class UserController {
@Resource
private UserService userService;
@GetMapping("user/{userName}")
public User queryUserByUserName(@PathVariable String userName){
return userService.queryUserByUserName(userName);
}
}
- 添加应用启动入口
@SpringBootApplication
@MapperScan("com.xxxx.springboot.dao")
public class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class);
}
}
启动测试
运行Starter main方法,启动应用浏览器测试查询
后端日志打印效果:
SpringBoot数据访问操作
完成SpringBoot 与Mybatis 集成后,接下来以用户表为例实现一套用户模块基本数据维护。
接口方法 & Sql映射文件
- UserDao 接口方法定义
UserDao 接口添加数据访问基本方法
public interface UserMapper {
public User queryById(Integer id);
User queryUserByUserName(String userName);
public int save(User user);
public int update(User user);
public List<User> selectByParams(UserQuery userQuery);
}
- UserMapper.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="queryById" parameterType="int" resultType="com.xxxx.springboot.vo.User">
select *
from t_user
where id = #{id,jdbcType=INTEGER}
</select>
<select id="queryUserByUserName" parameterType="string" resultType="com.xxxx.springboot.vo.User">
select *
from t_user
where user_name=#{userName}
</select>
<insert id="save" parameterType="com.xxxx.springboot.vo.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user(id,user_name,user_pwd) values(#{id},#{userName},#{userPwd})
</insert>
<update id="update" parameterType="com.xxxx.springboot.vo.User">
update t_user set user_name =#{userName},user_pwd=#{userPwd}
where id = #{id}
</update>
<select id="selectByParams" parameterType="com.xxxx.springboot.query.UserQuery" resultType="com.xxxx.springboot.vo.User">
select *
from t_user
<where>
<if test="null !=userName and userName !=''">
and user_name like concat('%',#{userName},'%')
</if>
</where>
</select>
</mapper>
UserService.java方法实现
public User queryUserByUserName(String userName){
return userMapper.queryUserByUserName(userName);
}
public User queryUserByUserId(Integer userId){
return userMapper.queryById(userId);
}
public void saveUser(User user) {
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用户名不能为空!");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用户密码不能为空!");
User temp = userMapper.queryUserByUserName(user.getUserName());
AssertUtil.isTrue(null != temp, "该用户已存在!");
AssertUtil.isTrue(userMapper.save(user)<1,"用户记录添加失败!");
}
public void updateUser(User user) {
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用户名不能为空!");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用户密码不能为空!");
User temp = userMapper.queryUserByUserName(user.getUserName());
AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "该用户已存在!");
AssertUtil.isTrue(userMapper.update(user)<1,"用户记录添加失败!");
}
public void deleteUser(Integer id){
AssertUtil.isTrue(null == id || null ==userMapper.queryById(id),"待删除记录不存在!");
AssertUtil.isTrue(userMapper.delete(id)<1,"用户删除失败!");
}
public PageInfo<User> queryUserByParams(UserQuery userQuery){
PageHelper.startPage(userQuery.getPageNum(),userQuery.getPageSize());
return new PageInfo<User>(userMapper.selectByParams(userQuery));
}
UserController.java 接口方
@GetMapping("user/{userId}")
public User queryUserByUserId(@PathVariable Integer userId){
return userService.queryUserByUserId(userId);
}
@GetMapping("user/list")
public PageInfo<User> list(UserQuery userQuery){
return userService.queryUserByParams(userQuery);
}
@PutMapping("user")
public ResultInfo saveUser(User user){
ResultInfo resultInfo=new ResultInfo();
try {
userService.saveUser(user);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录添加失败!");
}
return resultInfo;
}
@PostMapping("user")
public ResultInfo updateUser(User user){
ResultInfo resultInfo=new ResultInfo();
try {
userService.updateUser(user);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录更新失败!");
}
return resultInfo;
}
@DeleteMapping("user/{userId}")
public ResultInfo deleteUser(@PathVariable Integer userId){
ResultInfo resultInfo=new ResultInfo();
try {
userService.deleteUser(userId);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录删除失败!");
}
return resultInfo;
}
API 文档构建工具-Swagger2
由于Spring Boot能够快速开发、便捷部署等特性,通常在使用Spring Boot构建Restful 接口应用时考虑到多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。对于不同的终端公用一套接口API时对于联调测试时就需要知道后端提供的接口Api 列表文档,对于服务端开发人员来说就需要编写接口文档,描述接口调用地址参数结果等,这里借助第三方构建工具Swagger2来实现Api文档生成功能。
环境整合配置
- pom.xml 依赖添加
<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();
}
}
Swagger2 常用注解说明
@Api
@Api:用在请求的类上,说明该类的作用
tags="说明该类的作用"
@Api(tags="APP用户注册Controller")
@ApiOperation
@ApiOperation:"用在请求的方法上,说明方法的作用"
value="说明方法的作用"
notes="方法的备注说明"
@ApiOperation(value="用户注册",notes="手机号、密码都是必输项,年龄随边填,但必须是数字")
@ApiImplicitParams
@ApiImplicitParams:用在请求的方法上,包含一组参数说明
@ApiImplicitParam:用在 @ApiImplicitParams 注解中,指定一个请求参数的配置信息
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
· header --> 请求参数的获取:@RequestHeader
· query --> 请求参数的获取:@RequestParam
· path(用于restful接口)--> 请求参数的获取:@PathVariable
· body(不常用)
· form(不常用)
dataType:参数类型,默认String,其它值dataType="Integer"
defaultValue:参数的默认值
@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
@ApiResponses:用于请求的方法上,表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiOperation(value = "select请求",notes = "多个参数,多种的查询参数类型")
@ApiResponses({
@ApiResponse(code=400,message="请求参数没填好"),
@ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")
})
@ApiModel
@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;
/* getter/setter */
}
用户模块注解配置
UserController.java 接口方法注解使用
@GetMapping("user/uname/{userName}")
@ApiOperation(value = "根据用户名查询用户记录")
@ApiImplicitParam(name = "userName",value = "查询参数",required = true,paramType = "path")
public User queryUserByUserName(@PathVariable String userName){
return userService.queryUserByUserName(userName);
}
@ApiOperation(value = "根据用户id查询用户记录")
@ApiImplicitParam(name = "userId",value = "查询参数",required = true,paramType = "path")
@GetMapping("user/{userId}")
public User queryUserByUserId(@PathVariable Integer userId, HttpServletRequest request){
return userService.queryUserByUserId(userId);
}
@GetMapping("user/list")
@ApiOperation(value = "多条件查询用户列表记录")
public PageInfo<User> list(UserQuery userQuery){
return userService.queryUserByParams(userQuery);
}
@PutMapping("user")
@ApiOperation(value = "用户添加")
@ApiImplicitParam(name = "user",value = "用户实体类",dataType = "User")
public ResultInfo saveUser(@RequestBody User user){
ResultInfo resultInfo=new ResultInfo();
try {
userService.saveUser(user);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录添加失败!");
}
return resultInfo;
}
@PostMapping("user")
@ApiOperation(value = "用户更新")
@ApiImplicitParam(name = "user",value = "用户实体类",dataType = "User")
public ResultInfo updateUser(@RequestBody User user){
ResultInfo resultInfo=new ResultInfo();
try {
userService.updateUser(user);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录更新失败!");
}
return resultInfo;
}
@PutMapping("user/{userId}")
@ApiOperation(value = "根据用户id删除用户记录")
@ApiImplicitParam(name = "userId",value = "查询参数",required = true,paramType = "path")
public ResultInfo deleteUser(@PathVariable Integer userId){
ResultInfo resultInfo=new ResultInfo();
try {
userService.deleteUser(userId);
} catch (ParamsException e) {
e.printStackTrace();
resultInfo.setCode(e.getCode());
resultInfo.setMsg(e.getMsg());
}catch (Exception e) {
e.printStackTrace();
resultInfo.setCode(300);
resultInfo.setMsg("记录删除失败!");
}
return resultInfo;
}
JavaBean 使用
- User.java
@ApiModel(description = "响应结果-用户信息")
public class User {
@ApiModelProperty(value = "用户id",example = "0")
private Integer id;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "用户密码")
private String userPwd;
/*
省略get|set
*/
}
- UserQuery.java
@ApiModel(description = "用户模块条件查询类")
public class UserQuery {
@ApiModelProperty(value = "分页页码",example = "1")
private Integer pageNum=1;
@ApiModelProperty(value = "每页大小",example = "10")
private Integer pageSize=10;
@ApiModelProperty(value = "用户名")
private String userName;
/*
省略get|set
*/
}
- ResultInfo.java
@ApiModel(description = "响应结果-Model信息")
public class ResultInfo {
@ApiModelProperty(value = "响应状态码",example = "200")
private Integer code=200;
@ApiModelProperty(value = "响应消息结果")
private String msg="success";
@ApiModelProperty(value = "响应具体结果信息")
private Object result;
/*
省略get|set
*/
}
Swagger2 接口文档访问
启动工程,浏览器访问:http://localhost:9999/swagger-ui.html
分布式缓存Ehcache整合
EhCache是一个比较成熟的Java缓存框架,最早从hibernate发展而来, 是进程中的缓存系统,它提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案,快速简单。
Spring Boot对Ehcache的使用提供支持,所以在Spring Boot中只需简单配置即可使用Ehcache实现数据缓存处理。
Spring Cache 相关注解说明
SpringBoot 内部使用SpringCache 来实现缓存控制,这里集成Ehcache实际上是对SpringCache 抽象的其中一种实现,这里在使用Ehcache实现缓存控制时相关注解说明如下
@CacheConfig
用于标注在类上,可以存放该类中所有缓存的公有属性,比如设置缓存的名字。
@CacheConfig(cacheNames = "users")
public class UserService {。。。}
这里也可以不使用该注解,直接使用@Cacheable配置缓存集的名字。
@Cacheable
应用到读取数据的方法上,即可缓存的方法,如查找方法,先从缓存中读取,如果没有再调用相应方法获取数据,然后把数据添加到缓存中。
该注解主要有下面几个参数:
- value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
- key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
- condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存。
- unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
- cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
@Cacheable(value = "user", key = "#id")
User selectUserById(final Integer id);
@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")
}
}
将id—>user;username—->user;age—->user进行缓存。
定时调度集成-Quartz
在日常项目运行中,我们总会有需求在某一时间段周期性的执行某个动作。比如每天在某个时间段导出报表,或者每隔多久统计一次现在在线的用户量等。
在Spring Boot中有Java自带的java.util.Timer类,SpringBoot自带的Scheduled来实现,也有强大的调度器Quartz。Scheduled 在Spring3.X 引入,默认SpringBoot自带该功能,使用起来也很简单,在启动类级别添加@EnableScheduling注解即可引入定时任务环境。但遗憾的是Scheduled 默认不支持分布式环境,这里主要讲解Quartz 时钟调度框架与Spring Boot 集成。
环境整合配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
源代码添加
定义job
com.xxxx.springboot下添加jobs包,定义待执行job任务
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");
TriggerKey triggerKey = context.getTrigger().getKey();
log.info("触发器:"+triggerKey.getName()+"-->所属组:"+triggerKey.getGroup()+"-->"+sdf.format(new Date())+"-->"+"hello Spring Boot Quartz...");
}
}
构建调度配置类
@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();
}
// 每两秒触发一次任务
@Bean
public Trigger trigger2(){
return TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *"))
.forJob(jobDetail1())
.build();
}
}
启动StarterApplication 查看控制台打印效果
全局异常与事物控制
Spring Boot事物支持
在使用Jdbc 作为数据库访问技术时,Spring Boot框架定义了基于jdbc 的PlatformTransactionManager 接口的实现DataSourceTransactionManager,并在Spring Boot 应用启动时自动进行配置。如果使用jpa 的话 Spring Boot 同样提供了对应实现。
这里Spring Boot 集成了Mybatis框架,Mybatis底层数据访问层实现基于jdbc 来实现,所以在Spring Boot 环境下对事物进行控制,事物实现由Spring Boot实现并自动配置,在使用时通过注解方式标注相关方法加入事物控制即可
- 声明式事物配置
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser(User user) {
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用户名不能为空!");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用户密码不能为空!");
User temp = userMapper.queryUserByUserName(user.getUserName());
AssertUtil.isTrue(null != temp, "该用户已存在!");
AssertUtil.isTrue(userMapper.save(user)<1,"用户记录添加失败!");
}
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user) {
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用户名不能为空!");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用户密码不能为空!");
User temp = userMapper.queryUserByUserName(user.getUserName());
AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "该用户已存在!");
AssertUtil.isTrue(userMapper.update(user)<1,"用户记录添加失败!");
}
@Transactional(propagation = Propagation.REQUIRED)
public void deleteUser(Integer id){
AssertUtil.isTrue(null == id || null ==userMapper.queryById(id),"待删除记录不存在!");
AssertUtil.isTrue(userMapper.delete(id)<1,"用户删除失败!");
}
Spring Boot 全局异常处理
SpringMvc 中对异常统一处理提供了相应处理方式,推荐大家使用的是实现接口HandlerExceptionResolver的方式,对代码侵入性较小。
在Spring Boot 应用中同样提供了对异常的全局性处理,相关注解如下:
@ControllerAdvice
该注解组合了@Component注解功能,最常用的就是作为全局异常处理的切面类,同时通过该注解可以指定包扫描的范围。@ControllerAdvice约定了几种可行的返回值,如果是直接返回model类的话,需要使用@ResponseBody进行json转换
@ExceptionHandler
该注解在Spring 3.X 版本引入,在处理异常时标注在方法级别,代表当前方法处理的异常类型有哪些 具体应用以Restful 接口为例,测试保存用户接口
全局异常应用
异常抛出与全局捕捉
- UserController 查询接口
@ApiOperation(value = "根据用户id查询用户记录")
@ApiImplicitParam(name = "userId",value = "查询参数",required = true,paramType = "path")
@GetMapping("user/{userId}")
public User queryUserByUserId(@PathVariable Integer userId){
return userService.queryUserByUserId(userId);
}
- UserService 查询业务方法,抛出ParamExceptions 异常
public User queryUserByUserId(Integer userId){
AssertUtil.isTrue(true,"异常测试...");
return userMapper.queryById(userId);
}
- 全局异常处理类GlobalExceptionHandler定义
@ControllerAdvice
public class GlobalExceptionHandler{
/**
* 全局异常处理 返回json
* @param e
* @return
*/
@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;
}
}
- Postman 执行测试效果
特定异常处理
通过@ExceptionHandler 标注方法可以处理特定异常,这里以用户未登录异常为例,通过全局异常进行统一处理
/**
* 用户未登录异常特殊处理 返回json
* @param authExceptions
* @return
*/
@ExceptionHandler(value = NoLoginException.class)
@ResponseBody
public ResultInfo userNotLoginHandler(NoLoginException authExceptions){
System.out.println("用户未登录异常处理。。。");
return new ResultInfo(authExceptions.getCode(),authExceptions.getMsg());
}
在用户添加接口中抛出未登录异常为例进行测试
@PutMapping("user")
@ApiOperation(value = "用户添加")
@ApiImplicitParam(name = "user",value = "用户实体类",dataType = "User")
public ResultInfo saveUser(@RequestBody User user){
if(1==1){
throw new NoLoginException();
}
ResultInfo resultInfo=new ResultInfo();
userService.saveUser(user);
return resultInfo;
}
SpringBoot 数据校验-Validation
日常项目开发中,对于前端提交的表单,后台接口接收到表单数据后,为了程序的严谨性,通常后端会加入业务参数的合法校验操作来避免程序的非技术性bug,这里对于客户端提交的数据校验,SpringBoot通过spring-boot-starter-validation 模块包含了数据校验的工作。
这里主要介绍Spring Boot中对请求数据进行校验,相关概念如下
- JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其升级版本,添加了一些新特性。
- Hibernate Validation:Hibernate Validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
- Spring Validation:Spring Validation对Hibernate Validation进行了二次封装,在Spring Mvc模块中添加了自动校验,并将校验信息封装进了特定的类中
环境配置
实现参数校验,程序必须引入spring-boot-starter-validation 依赖,只是在引入spring-boot-starter-web依赖时,该模块会自动依赖spring-boot-starter-validation,所以程序中引入spring-boot-starter-web 会一并依赖spring-boot-starter-validation到项目中。
校验相关注解
注解 | 功能 |
---|---|
@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,字符串trin()后也不能等于“” |
@NotEmpty | 不能为null,集合、数组、map等size()不能为0;字符串trin()后可以等于“” |
@Range | 值必须在指定范围内 |
@URL | 必须是一个URL |
参数校验注解使用
- User实体类参数校验注解
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;
private String email;
/*
省略get set 方法
*/
}
- 接口方法形参@Valid注解添加
@PostMapping("user02")
@ApiOperation(value = "用户添加")
@ApiImplicitParam(name = "user02",value = "用户实体类",dataType = "User")
public ResultInfo saveUser02(@Valid User user){
ResultInfo resultInfo=new ResultInfo();
//userService.saveUser(user);
return resultInfo;
}
- 全局异常错误信息捕捉
/**
* 全局异常处理 返回json
* @param e
* @return
*/
@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());
}else if(e instanceof BindException){
BindException be = (BindException) e;
resultInfo.setResult(be.getBindingResult().getFieldError().getDefaultMessage());
}
return resultInfo;
}
- PostMan 接口测试
总结
今天课程主要介绍了SpringBoot中各种环境的整合与测试工作,持久层框架Mybatis集成与数据访问基本操作,借助SpringBoot单元测试实现业务方法与控制器接口测试,同时集成了Swagger2 接口文件生成工具来快速生成接口文档的功能。在web项目开发中常见的项目热部署配置,这里集成DevTools工具来帮助web开发者在项目开发中不断手动启动服务器部署项目的繁琐流程,通过引入EhCache 缓存技术来加快应用程序数据的访问效率,然后对于项目中常见的定时任务的执行,这里集成了Quartz 任务调度框架来实现任务定时执行处理,最后提到了SpringBoot 应用中对象项目事物控制与异常统一处理,从而提高项目代码的健壮性与数据的一致性,借助SpringBoot 的Validation 实现后端数据参数的校验机制,结合全局异常来对校验结果进行输出操作,提高后端应参数处理的严谨性。