Spring Boot -- Spring Boot进阶
Posted 大奥特曼打小怪兽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot -- Spring Boot进阶相关的知识,希望对你有一定的参考价值。
这一节将在上一节的基础上,继续深入学习Spring Boot相关知识,其中主要包括@Async异步调用,@Value自定义参数、Mybatis、事务管理等。
本节所使用的代码是在上一节项目代码中,继续追加的,因此需要先学习上一节内容。
一、使用@Async实现异步调用
要在springboot中使用异步调用方法,只要在被调用的方法上面加上@Async
就可以了;
1.1.准备工作
准备一个Spring Boot项目,在App类上加上@EnableAsync
注解开启异步:
package com.goldwind;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @Author: zy
* @Description: 启动代码
* @Date: 2020-2-2
*/
@SpringBootApplication
@EnableAsync
public class App {
public static void main(String[] args){
//整个程序入口 启动Spring Boot项目
SpringApplication.run(App.class,args);
}
}
这个注解如果不加,@Async注解失效。
1.2、controller
在包com.goldwind.controller下,创建文件HelloController.java:
package com.goldwind.controller;
import com.goldwind.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
/**
* @Author: zy
* @Description: 用于演示@Async异步调用
* @Date: 2020-2-4
*/
@RestController
@RequestMapping("/hello")
public class HelloController {
@Autowired
private HelloService helloService;
/**
* 同步方法
* @return
*/
@RequestMapping("/sync")
public String getSyncHello(){
long n = Instant.now().toEpochMilli();
//异步
String s = helloService.syncSayHello();
long f = Instant.now().toEpochMilli();
return s + " 时间: " + (f-n);
}
/**
* 异步方法
* @return
*/
@RequestMapping("/async")
public String getAsyncHello(){
long n = Instant.now().toEpochMilli();
//异步
String s = helloService.asyncSayHello();
long f = Instant.now().toEpochMilli();
return s + "时间:" + (f-n);
}
}
1.3、Service
新建包com.goldwind.service,并在包下创建HelloService.java文件:
package com.goldwind.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: zy
* @Description: 用于演示@Async异步调用
* @Date: 2020-2-4
*/
@Service
@Slf4j
public class HelloService {
@Autowired
private SleepService sleepService;
/**
* 同步方法
* @return
*/
public String syncSayHello() {
try {
sleepService.syncSleep();
return "hello world,这是同步方法";
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
return "error";
}
}
/**
* 异步方法
* @return
*/
public String asyncSayHello() {
try {
log.info("主线程 " + Thread.currentThread().getName());
sleepService.asyncSleep();
return "hello world,这是异步方法";
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
return "error";
}
}
}
这里为了模拟应用场景,将耗时的方法放在另一个service里面,就叫SleepService,两个方法都是休眠3秒,aysncSleep方法上面有一个@Async
注解。
package com.goldwind.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @Author: zy
* @Description: 用于演示@Async异步调用
* @Date: 2020-2-4
*/
@Service
@Slf4j
public class SleepService {
/**
* 同步方法
* @throws InterruptedException
*/
public void syncSleep() throws InterruptedException {
log.info("线程名: " +Thread.currentThread().getName());
log.info("开始同步休眠3秒");
Thread.sleep(3000);
log.info("同步休眠结束");
}
/**
* 异步方法
* @throws InterruptedException
*/
@Async
public void asyncSleep() throws InterruptedException {
log.info("次线程 "+Thread.currentThread().getName());
log.info("开始异步休眠3秒");
Thread.sleep(3000);
log.info("异步休眠休眠结束");
}
}
1.4、测试
同步:访问 http://localhost:8080/hello/sync,需要3秒的时间才能收到响应;
异步:访问 http://localhost:8080/hello/asyn,可见主线程和次线程打印出来的线程名不一样,也就是Spring Boot帮我们开启了一个线程去处理。
注意事项
- 必须要加@EnableAsync注解;
- 不能在同一类下调用@Async注解的方法,比如A类下有a和b方法,b方法有@Async注解,不能直接这样a调用b,要把b放到其他类中;
- @Async也可以打在类上,这样类下面的所有方法都是异步的(被其他类调用的时候);
二、@Value自定义参数
@Value在Spring中,功能非常强大,可以注入一个配置项,可以引用容器中的Bean(调用其方法),也可以做一些简单的运算。下面通过@Value引用一个配置项。在配置文件application.properties中配置如下内容:
name=www.goldwind.com
修改HelloController.java文件,添加如下代码:
//初始化的时候加载
@Value("${name}")
private String name;
@RequestMapping("/getName")
public String getName(){
return name;
}
此时访问http://localhost:8080/hello/getName,将会返回配置文件中name属性的值。
此外@configurationproperties也具有类似的作用,具体可以查看博客springboot @value和@configurationproperties注解的区别。
三、多环境配置
软件开发中经常有开发环境、测试环境、预发布环境、生产环境,而且一般这些环境配置会各不相同,手动改配置麻烦且容易出错,如何管理不同环境的配置参数呢?Spring Boot + maven可以解决不同环境独立配置不同参数的问题。
不同环境的配置yml(或者properties,yml比properties配置文件更加节约、简约)文件名不一样:
- application-dev.yml(开发环境);
- application-test.yml(测试环境);
- application-uat.yml(预发布);
- application-pro.yml(生产环境);
eg:
application-dev.yml配置示例:
info:
build:
name: ${project.artifactId}
groupId: ${project.groupId}
artifactId: ${project.artifactId}
version: ${project.version}
server:
port: 8081
endpoints:
enabled: true
sensitive: false
data:
test:
envName: dev
envconfig: 127.0.0.1:8081
注意属性名: 属性值,在:后有一个空格;
application.properties文件追加:
spring.profiles.active=dev
如果要切换不同环境,只需要修改spring.profiles.active即可。
如果想读取开发环境中的配置信息,可以通过如下方式:
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "data.test")
@Service
public class DataConfig {
private String envName;
private String envconfig;
}
验证环境参数:
@Api("home controller")
@RestController
public class HomeController {
@Autowired
private DataConfig dataConfig;
@RequestMapping("/env")
@ApiOperation("env")
public Object testEnv() {
return dataConfig;
}
}
四、Mybatis
4.1、创建数据表
这里测试采用的是mysql数据库,首先创建goldwind数据库:
--创建数据库
DROP DATABASE IF EXISTS GLODWIND;
CREATE DATABASE IF NOT EXISTS GOLDWIND;
--选择数据库
USE GOLDWIND;
创建student数据表、并插入数据:
--创建表
CREATE TABLE student(
ID INT PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR(20),
AGE SMALLINT,
SEX BIT,
CLASS VARCHAR(50),
ADDRESS VARCHAR(100)) default charset=utf8;
--插入
insert into student(name,age,sex,class,address) values(\'郑洋\',\'24\',1,\'计算机(1)班\',\'江苏省无锡市滨湖区\');
--查询
select * from student;
4.2、pom文件导入依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
4.3、配置mybatis
修改appliction.properties,追加sql配置信息:
#mybati sql配置
#MySQL的JDBC URL编写方式:jdbc:mysql://主机名称:连接端口/数据库的名称 默认使用goldwind数据库
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/goldwind?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456aa
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
4.4、代码
在包com.goldwind.entity下新建StudentEntity.java文件:
package com.goldwind.entity;
import lombok.Data;
/**
* @Author: zy
* @Description: student实体类
* @Date: 2020-2-4
*/
@Data
public class StudentEntity {
/**
* id 主键、自增
*/
private Integer id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private Boolean sex;
/**
* 班级
*/
private String className;
/**
* 地址
*/
private String address;
}
新建包com.goldwind.mapper,并在包下新建StudentMapper.java文件:
package com.goldwind.mapper;
import com.goldwind.entity.StudentEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Service;
/**
* @Author: zy
* @Description: student Mapper类
* @Date: 2020-2-4
*/
@Service
public interface StudentMapper {
//查询语句
@Select("SELECT id,name,age,sex,`class` as className,address FROM STUDENT WHERE ID = #{id}")
StudentEntity getStudentById(@Param("id") Integer id);
//新增语句
@Insert("INSERT INTO STUDENT(name,age,sex) VALUES(#{name}, #{age},#{sex})")
int insertStudent(@Param("name") String name, @Param("age") Integer age,@Param("sex") Boolean sex);
}
在包com.goldwind.service下新建StudentService.java文件:
package com.goldwind.service;
import com.goldwind.mapper.StudentMapper;
import com.goldwind.entity.StudentEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: zy
* @Description: student业务逻辑
* @Date: 2020-2-4
*/
@Service
@Slf4j
public class StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 新增学生
* @param name:姓名
* @param age:年龄
* @param sex:性别
* @return:返回受影响的行数
*/
public int insertStudent(String name,Integer age,Boolean sex){
int ret = studentMapper.insertStudent(name,age,sex);
log.info("#####新增学生###########",ret);
return ret;
}
/**
* 根据id获取学生信息
* @param id:学生id
* @return:返回学生信息
*/
public StudentEntity getStudentById(Integer id){
log.info("#####获取学生信息###########",id);
return studentMapper.getStudentById(id);
}
}
在包com.goldwind.controller下新建StudentController.java文件:
package com.goldwind.controller;
import com.goldwind.entity.StudentEntity;
import com.goldwind.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: zy
* @Description: student控制器 测试mybatis
* @Date: 2020-2-4$
*/
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
/**
* 新增学生 这里没有对参数进行校验 127.0.0.1:8081/insertStudent?name=李艳&age=25&sex=1
* @param name:姓名
* @param age:年龄
* @param sex:性别
* @return:返回受影响的行数
*/
@RequestMapping("/insertStudent")
public int insertStudent(@RequestParam String name,@RequestParam Integer age,@RequestParam Boolean sex){
return studentService.insertStudent(name,age,sex);
}
/**
* 根据id获取学生信息
* @param id:学生id
* @return:返回学生信息
*/
@RequestMapping("/getStudent")
public StudentEntity getStudentById(@RequestParam Integer id){
return studentService.getStudentById(id);
}
}
修改App.java文件,加入mapper扫包范围:
package com.goldwind;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @Author: zy
* @Description: 启动代码
* mybatis启动方式有两种:
* 1、在mapper层添加@Mapper注解
* 2、在启动类上加@MapperScan指定扫包范围
* @Date: 2020-2-2
*/
@SpringBootApplication
@MapperScan(basePackages = {"com.goldwind.mapper"})
@EnableAsync
public class App {
public static void main(String[] args){
//整个程序入口 启动Spring Boot项目
SpringApplication.run(App.class,args);
}
}
启动程序,访问http://127.0.0.1:8081/getStudent?id=1,输出结果如下:
4.5、mybatis整合分页插件
(1)、pageHelper
PageHelper 是一款好用的开源免费的 Mybatis 第三方物理分页插件,它具有以下特点:
- 物理分页;
- 支持常见的 12 种数据库,Oracle,MySql,MariaDB,SQLite,DB2,PostgreSQL,SqlServer 等;
- 支持多种分页方式;
- 支持常见的 RowBounds(PageRowBounds),PageHelper.startPage 方法调用,Mapper 接口参数调用;
(2)、新增依赖
<!-- springboot 整合 pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
(3)、新增配置信息
在application.properties文件中追加如下配置:
logging.level.com.example.demo.dao=DEBUG
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
pagehelper.page-size-zero=true
(4)、代码
修改StudentMapper.java,添加查询全部学生信息的方法:
package com.goldwind.mapper;
import com.goldwind.entity.StudentEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: zy
* @Description: student Mapper类
* @Date: 2020-2-4
*/
@Service
public interface StudentMapper {
//查询语句
@Select("SELECT id,name,age,sex,`class` as className,address FROM STUDENT WHERE ID = #{id}")
StudentEntity getStudentById(@Param("id") Integer id);
//查询全部
@Select("SELECT id,name,age,sex,`class` as className,address FROM STUDENT")
List<StudentEntity> getStudentList();
//新增语句
@Insert("INSERT INTO STUDENT(name,age,sex) VALUES(#{name}, #{age},#{sex})")
int insertStudent(@Param("name") String name, @Param("age") Integer age, @Param("sex") Boolean sex);
}
修改StudentService.java:
package com.goldwind.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.goldwind.mapper.StudentMapper;
import com.goldwind.entity.StudentEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @Author: zy
* @Description: student业务逻辑
* @Date: 2020-2-4
*/
@Slf4j
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 新增学生
* @param name:姓名
* @param age:年龄
* @param sex:性别
* @return:返回受影响的行数
*/
@Transactional
public int insertStudent(String name,Integer age,Boolean sex){
int ret = studentMapper.insertStudent(name,age,sex);
int i = 1/age;
log.info("#####新增学生###########",ret);
return ret;
}
/**
* 根据id获取学生信息
* @param id:学生id
* @return:返回学生信息
*/
public StudentEntity getStudentById(Integer id){
log.info("#####获取学生信息###########",id);
return studentMapper.getStudentById(id);
}
/**
* 分页查询 获取学生信息
* @param page: 当前页
* @param pageSize: 每页记录数目
* @return: 返回学生信息
*/
public PageInfo<StudentEntity> getStudentList(int page,int pageSize){
//mysql查询 limit
//pageHelper 帮我们生成分页语句 底层实现原理:利用AOP、改写sql语句
PageHelper.startPage(page,pageSize);
List<StudentEntity> listStudent = studentMapper.getStudentList();
//返回给客户端展示
PageInfo<StudentEntity> pageInfoStudent = new PageInfo<StudentEntity>(listStudent);
return pageInfoStudent;
}
}
修改StudentController.java:
package com.goldwind.controller;
import com.github.pagehelper.PageInfo;
import com.goldwind.entity.StudentEntity;
import com.goldwind.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: zy
* @Description: student控制器 测试mybatis
* @Date: 2020-2-4$
*/
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
/**
* 新增学生 这里没有对参数进行校验 127.0.0.1:8081/insertStudent?name=李艳&age=25&sex=1
* @param name:姓名
* @param age:年龄
* @param sex:性别
* @return:返回受影响的行数
*/
@RequestMapping("/insertStudent")
public int insertStudent(@RequestParam String name,@RequestParam Integer age,@RequestParam Boolean sex){
return studentService.insertStudent(name,age,sex);
}
/**
* 根据id获取学生信息 127.0.0.1:8081/getStudent?id=1
* @param id:学生id
* @return:返回学生信息
*/
@RequestMapping("/getStudent")
public StudentEntity getStudentById(@RequestParam Integer id){
return studentService.getStudentById(id);
}
/**
* 分页查询 获取学生信息 127.0.0.1:8081/student?page=1&pageSize=2
* @param page: 当前页
* @param pageSize: 每页记录数目
* @return: 返回学生信息
*/
@RequestMapping("/student")
public PageInfo<StudentEntity> getStudentList(@RequestParam int page,@RequestParam int pageSize){
return studentService.getStudentList(page,pageSize);
}
}
运行程序,访问http://127.0.0.1:8081/student?page=1&pageSize=2,输出如下:
五、Spring事务管理
按是否通过编程分为声明式事务和编程式事务:
- 声明式事务:通过XML配置或者注解实现;
- 编程式事务:通过编程代码在业务逻辑时需要时自行实现,
以上是关于Spring Boot -- Spring Boot进阶的主要内容,如果未能解决你的问题,请参考以下文章
spring boo的简单搭建(eclipse+springboot + redis + mysql + thymeleaf)