目录
一、入门案例
1、创建的控制器操作
2、在Idea中隐藏指定文件/文件夹
3、SpringBoot基础配置
二、基础配置
1、修改配置
2、springboot的三种配置文件格式
3、配置文件的优先级
4、 yml语法规则
5、yml数据读取
6、yaml文件中的数据引用
7、将整个数据封装为Environment
8、读取ymal当中引用型数据
三、整合第三方技术
1、整合junit
2、整合mybatis(spring_04_mybatis)
3、整合mybatis-plus
四、springboot整合ssmp的案例
1.导入的jar包依赖
2、配置application.yml文件
3.创建pojo类
4.创建dao映射接口
5、测试我们的映射dao接口的方法
(1)分页查询::
(2)按条件查询
(3)其他方法
6.业务层service接口
7.业务层service接口的实现类
8.业务层快速开发
9、控制表现层的开发 controller
10.前端页面的实现
五、运维实用篇
1、SpringBoot程序的打包与运行
2、配置临时属性
3.配置文件4级分类
4、自定义配置文件
5、多环境开发
(1)配置
(2)多环境开发分组管理
(3)多环境的开发控制
6、日志文件
7、日志输出格式与控制
六、热部署功能
七、配置高级
1、绑定第三方的bean
2、松散绑定
3、常量计量单位
4、使用属性校验
八、测试专题
1、加载测试临时属性
2、加载测试专用属性
3、使用测试类启动web环境
4、测试匹配信息
(1)匹配响应状态
(2)匹配响应体
(3)响应头信息匹配
5、业务层测试事务回滚
6、测试用例设置随机数
九、数据层解决方案
1、内置数据源
2、jdbcTemplate持久化技术
3、内置数据库 h2
(2) 数据库redis,springbot整合redis
(3)springboot整合mogodb
十、缓存
1、springboot内置缓存技术
2、使用内置缓存技术实现验证码
3、将实现验证码改换成ehcache技术
4、使用redis 整合redis
5、整合Memcached技术
6、使用jetcacha
(1)使用纯远程redis缓存
(2)本地缓存
7、j2cache缓存框架
十一、定时
(1)springboot整合Quartz定时任务
(2)整合task
(3)springboot整合javamail发送邮件
十二、mq产品
(1)购物订单案例,消息发送
(2)SpringBoot整合ActiveMQ
(3)SpringBoot整合RocketMQ
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById(){
System.out.println("springboot getById");
return "springboot getById";
}
}
因为我们所创建的工程是使用别人的模板,有很多很多个包,有些包不需要使用,我们就可以将文件夹隐藏,从而更加简洁
. Idea中隐藏指定文件或指定类型文件
1. 【Files】→【Settings】
2. 【Editor】→【File Types】→【Ignored Files and Folders】
3. 输入要隐藏的名称,支持*号通配符
4. 回车确认添加
1.在工作空间赋值对应的工程,并修改工程名称。
2.只留下src 喝pox.xml文件
3.使用的时候,直接复制模板,将pox.xml文件打开将artifactId与新工程名字相同
4.在idea当中将工程导入进去,刷新maven
SpringBoot默认配置文件是application.properties
直接name=value的形式
例子::: #服务器的端口配置
server.port=8081
#设置日志相关
logging.level.root=debug
- properties格式(默认配置文件格式)
- yml格式(主流)
- yaml格式
- application.properties(properties格式)
```properties
server.port=80
```
- application.yml(yml格式)
```YML
server:
port: 81
```
- application.yaml(yaml格式)
```yaml
server:
port: 82
```
application.properties > application.yml > application.yaml
**总结**
1. 配置文件间的加载优先级 properties(最高)> yml > yaml(最低)
2. 不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
*大小写敏感
*属性层级关系使用多行描述,每行结尾使用冒号结束
*使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
*属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
* #号 表示注释
2. 注意属性名冒号后面与数据之间有一个**空格**
3. 字面值、对象数据格式、数组数据格式
下面是yml数据格式
、、、application.yml
users:
- name: Tom
age: 4
- name: Jerry
age: 5
country: china
、、、java
//读取单一数据
@Value("${country}")
private String country1;
//读取对象数据
@Value("${users[1].name}")
private String name;
@Value("${users[1].age}")
private Integer age;
使用的也是${}
```YAML
center:
dataDir: D:/usr/local/fire/data
tmpDir: D:/usr/local/fire/tmp
logDir: D:/usr/local/fire/log
msgDir: D:/usr/local/fire/msgDir
```
如果你在书写yaml数据时,经常出现如下现象,比如很多个文件都具有相同的目录前缀
这个时候你可以使用引用格式来定义数据,其实就是搞了个变量名,然后引用变量了,格式如下:
```YAML
baseDir: /usr/local/fire
center:
dataDir: ${baseDir}/data
tmpDir: ${baseDir}/tmp
logDir: ${baseDir}/log
msgDir: ${baseDir}/msgDir
```
yml页面当中的数据可以看 5、yml数据读取
//如果一个一个获取数据太麻烦,可以直接将所有数据存储到Environment,然后通过它使用get方法拿数据,记得加上注释@Autowired(自动装配)
、、、、java
@Autowired
private Environment environment;
@GetMapping
public String getById(){
System.out.println("age==>>"+ environment.getProperty("user[1].age"));
return "springboot base configuration";
}
@ConfigurationProperties(prefix = "datasource")
指定我们提取的是那一组数据
、、、yml
#创建类,用于封装下面的数据
#由spring帮我们去加载数据到对象当中,一定要告诉spring加载这组信息
#使用时候从spring当中直接获取
datasource:
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/springboot
username: root
password: Xx20011128
、、、创建一个pojo类
//1.定义数据模型封装yaml文件当中对应的数据
//2.定义为spring管控的Bean
@Component
//3.指定需要加载的数据,将我们yml当中定义的名字放在这里提取
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
、、、控制器当中
@Autowired
private MyDataSource myDataSource;
@GetMapping
public String getById(){
System.out.println(myDataSource);
return "springboot base configuration";
}
这个就是我们测试类,如果测试类不在我们启动类的包或者子包当中,我们就添加classes来指定配置类
```JAVA
//在注解@SpringBootTest中添加classes属性指定配置类
@SpringBootTest(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
//注入你要测试的对象
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
//执行要测试的对象对应的方法
bookDao.save();
System.out.println("two...");
}
}
```
(1).将我们需要使用的jar加上
(2)yml配置文件
重点:这里面有一个问题,玩的端口号不设置称3306启动就会报错
```yaml
#2.配置相关信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db
username: root
password: root
```
(3)pojo类
public class Book {
private Integer id;
private String type;
private String name;
private String description;
(4)映射dao接口
这里面使用的mybatis注解配置,
@Mapper
public interface BookDao {
@Select("select * from tbl_book where id = #{id} ")
Book selectById(Integer id);
}
(5)测试程序
@SpringBootTest
class Spring04MybatisApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
Book book = bookDao.selectById(1);
System.out.println(book);
}
}
(1)添加依赖,在将module添加mysql包创建出来后,我们将mybatis-plus的jar依赖导入进去
com.baomidou
mybatis-plus-boot-starter
3.5.2
(2)yml配置文件
#2.配置相关信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_tb?serverTimezone=UTC
username: root
password: Xx20011128
#设置所有表的通用前缀名称为tbl_,然后继承了BaseMapper的dao接口类,
#就会再查询数据的时候加上前缀,因为我们数据库设置的是tbl_book
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
(3)创建pojo类
public class Book {
private Integer id;
private String type;
private String name;
private String description;
(4)映射dao接口
最重要的就是这里 我们继承 BaseMapper
//如果是整合mybatis-plus的话,我们这里的方法不需要写具体的方法操作了
//mybatis-plus帮助我们写了
@Mapper
public interface BookDao extends BaseMapper {
}
(5)测试方法
@SpringBootTest
class Spring05MybatisPlusApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
System.out.println(bookDao.selectById(3));
}
}
model哪里选择spring web ,mysql Driver
手动导入的有::前面不加mybatis是因为我们使用的mybatis-plus,所以不需要添加
这个永华简化Mapper操作
com.baomidou
mybatis-plus-boot-starter
3.5.2
com.alibaba
druid-spring-boot-starter
1.2.18
这个给是方便我们实例化类
org.projectlombok
lombok
(1)开启日志文件
使用lombok的话,我们直接添加@Data属性
//这个我们使用的是mybatis-plus,可以简化Mapper文件,
//只需要继承BaseMapper<这里写我们pojo类>
@Mapper
public interface BookDao extends BaseMapper {
}
分页查询获取查询数据,直接用创建的 IPage 对象使用get方法获取
测试包BookDaoTest下
//分页查询,分页查询的话,它不能直接使用,我们需要在创捷一个MybatisPlus的拦截器,
@Test
void testGetPage(){
// 其中selectPage方法需要传入一个封装分页数据的对象,
//可以通过new的形式创建这个对象,当然这个对象也是MyBatisPlus提供的,别选错包了。
//创建此对象时需要指定两个分页的基本数据
IPage page = new Page(1, 5);
bookDao.selectPage(page,null);
//分页方法返回的就是page,我们要想获取查询数据,直接page。get的获取
System.out.println(page.getCurrent());
}
拦截器,我们创建config文件,专门存放配置类信息,在里面创建MP的配置配置类
//这个是我们mybatisPlus的配置文件
@Configuration//添加注解,让它成为配置文件
public class MPConfig {
因为这个是配置文件,整体上还是spring的程序,
要将对象创建出来交给spring的ioc管理
配置拦截器,这个拦截器就是帮助我们动态的拼接limit语句,帮助我们完成分页查询
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
这个记住一点,LambdaQueryWrapper是用来封装条件的对象,
QueryWrapper的话可能我们查询的条件name会打错,所以我们一般使用第一种情况
第一个是按条件分页查询,第二个是我们普通的按条件查询所有
//按条件查询
@Test
void testGetBy(){
String name = "spring";
//这个是要传入值的对象,可以封装我们的条件
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//因为我们给条件传值的时候为null会将null作为值传入进去,所有LambdaQueryWrapper对象提供了这个值name!=null
//将这个值传入进去就,如果为为空,我们就不将条件拼接进去
lqw.like(name!=null,Book::getName,name);
bookDao.selectList(lqw);
}
@SpringBootTest
public class BookDaoTest {//测试我们dao下面的方法
@Autowired
private BookDao bookDao;
@Test
public void testSelectById(){
System.out.println(bookDao.selectById(2));
}
@Test
public void testSelectAll(){
System.out.println(bookDao.selectList(null));
}
@Test
public void testSave(){
Book book = new Book();
book.setName("测试1");
book.setType("测试2");
book.setDescription("测试3");
bookDao.insert(book);
}
@Test
void testDeleteById(){
bookDao.deleteById(2);
}
一般service接口的名称定义为业务名称,与dao当中的名称有所区分
public interface BookService {
Boolean save(Book book);
Boolean delete(Integer id);
Boolean update(Book book);
Book getById(Integer id);
List getAll();
//分页查询,两个参数,当前页码,每页显示数目
IPage getPage(int currentPage,int pageSize);
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public Boolean save(Book book) {
int count = bookDao.insert(book);
return count > 0;
}
@Override
public Boolean delete(Integer id) {
int count = bookDao.deleteById(id);
return count > 0;
}
@Override
public Boolean update(Book book) {
int count = bookDao.updateById(book);
return count > 0;
}
@Override
public Book getById(Integer id) {
return bookDao.selectById(id);
}
@Override
public List getAll() {
return bookDao.selectList(null);
}
//分页查询
@Override
public IPage getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage,pageSize);
return bookDao.selectPage(page,null);
}
}
直接写我们的业务层接口继承IService
注意::要记住的就是ServiceImpl
service接口
public interface IBookService extends IService {
boolean saveBook(Book book);
boolean modify(Book book);
boolean delete(Integer id);
//分页
IPage getPage(int currentPage, int pageSize);
//分页加条件查询
IPage getPage(int currentPage, int pageSize,Book book);
}
service接口的实现
@Service//把这个纳入ioc容器管理
public class IBookServiceImpl extends ServiceImpl implements IBookService {
//要记住的就是ServiceImpl一般就是ServiceImpl ,M就是dao层的业务接口
@Autowired
private BookDao bookDao;
@Override
public boolean saveBook(Book book) {
return bookDao.insert(book)>0;
}
@Override
public boolean modify(Book book) {
int count = bookDao.updateById(book);
return count >0;
}
@Override
public boolean delete(Integer id) {
int count = bookDao.deleteById(id);
return count>0;
}
@Override
public IPage getPage(int currentPage, int pageSize) {
//如果只是分页查询这样就够了
IPage page = new Page(currentPage,pageSize);
return bookDao.selectPage(page,null);
}
//分页查询+条件查询
@Override
public IPage getPage(int currentPage, int pageSize,Book book) {
IPage page = new Page(currentPage,pageSize);
//这个是要传入值的对象,可以封装我们的条件
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//讲条件添加进去
lqw.like(book.getType()!=null,Book::getType,book.getType());
lqw.like(book.getName()!=null,Book::getName,book.getName());
lqw.like(book.getDescription()!=null,Book::getDescription,book.getDescription());
bookDao.selectPage(page,lqw);
return page;
}
}
但是我们一般使用的是统一格式的方法,不然前端拿到的数据比较乱
//@RestController
@RequestMapping("/books")
public class BookController2 {//表现层,这里的表现层数据没有统一格式
//@Autowired
private IBookService iBookService;
@GetMapping
public List GetAll(){
return iBookService.list();
}
@GetMapping("/{id}")
public Book GetById(@PathVariable("id") Integer id){
return iBookService.getById(id);
}
@PostMapping
public Boolean save(@RequestBody Book book){
return iBookService.save(book);
}
@DeleteMapping("/{id}")
public Boolean delete(@PathVariable("id") Integer id){
return iBookService.removeById(id);
}
@PutMapping
public Boolean update(@RequestBody Book book){
return iBookService.modify(book);
}
//分页查询
@GetMapping("/{current}/{size}")
public IPage getPage(@PathVariable("current") int current,@PathVariable("size")int size){
return iBookService.getPage(current,size,null);
}
}
10、控制表形层统一数据
使用这种格式 flag表示操作成功,false表示出现异常,date用来存储数据
在controller当中创建工具类utils,存放我们统一数据的类
@Data//使用lombok构建实体类方法
public class R {//表现层数据一致性处理
private Boolean flag;
private Object data;
private String msg;
public R(){}
public R(Boolean flag){
this.flag=flag;
}
public R(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}
public R(Boolean flag, Object data, String msg) {
this.flag = flag;
this.data = data;
this.msg = msg;
}
}
控制器业务层代码,使用统一格式的方法
@RestController
@RequestMapping("/books")
public class BookController {//表现层,这里的数据统一了格式
@Autowired
private IBookService iBookService;
@GetMapping
public R GetAll(){
return new R(true,iBookService.list());
}
@GetMapping("/{id}")
public R GetById(@PathVariable("id") Integer id){
return new R(true,iBookService.getById(id));
}
@PostMapping
public R save(@RequestBody Book book) throws IOException {
//return new R(iBookService.saveBook(book));
//这里模拟一个异常操作,当添加的数据name=123的时候报错
if(book.getName().equals("123")){
throw new IOException();
}
boolean flag = iBookService.saveBook(book);
return new R(flag,null,flag ? "添加成功o.O":"添加失败O.o");
}
@DeleteMapping("/{id}")
public R delete(@PathVariable("id") Integer id){
return new R(iBookService.delete(id));
}
@PutMapping
public R update(@RequestBody Book book){
return new R(iBookService.modify(book));
}
//分页查询,这里将book作为提交查询数据一起传输过去
@GetMapping("/{currentPage}/{pageSize}")
public R getPage(@PathVariable("currentPage") int currentPage,@PathVariable("pageSize")int pageSize,Book book){
IPage page = iBookService.getPage(currentPage, pageSize,book);
//解决分页功能bug
//如果当前的页码值大于最大页码值,那么就应该把最大页码之赋值给最当前页码值
if(currentPage>=page.getPages())
page=iBookService.getPage((int) page.getPages(), pageSize,book);
return new R(true,page);
}
}
uils包下创建处理异常的类ProjectException,如果出现异常的话,直接返回数据,msg里面存储的状态为数据出错
@RestControllerAdvice
//springmvc的异常处理器
public class ProjectException {
@ExceptionHandler(Exception.class)
public R doOrderException(Exception ex){
//出现异常可以写记录日志啊,发消息给运维啊,发邮件给开发人员啊等等
ex.printStackTrace();
return new R(false,null,"系统错误o.O");
}
}
这里,我直接导入老师的前端资源,然后跟着配套写。
写的话先找到页面模块在哪里,跟着点击事件设置的下一步操作去写
基于SpringBoot整合SSM案例
图书管理
查询
新建
编辑
删除
运行项目:java后面那个就是打包后的文件名字
下面的是我们检查端口的操作::
(1)一级::在我们编程的resources当中的:application.xml是给我们程序员用的
(2)二级:: 在编程的resources当中创建config包下的application.yml是给项目经理用的,而皮质等级高于前面
(3)三级::打包后的jar文件同级目录下的application.yml,
(4)四级::打包后的jar文件同级目录下的config,config包里面的application
等级越高,优先级越高,相同配置会覆盖前面的配置,不同的配置,会联合一起使用
这个是根据名字,也可以根据路径 --spring.config.location=classpath:/ebank.yml
yml文件当中,不同的环境用 --- 分隔开
#应用环境,当前在使用的环境
spring:
profiles:
active: dev
#设置环境
---
#生产环境
spring:
profiles: pro
server:
port: 80
---
#开发环境
spring:
profiles: dev
server:
port: 81
---
#测试环境
spring:
profiles: test
server:
port: 82
第二种方式
include的话,我们的主配置文件会在最后加载,而group的话就是先加载主配置文件,再加载指定的附属配置文件
pox.xml文件当中的配置
**maven中设置多环境(使用属性方式区分环境)**
```xml
env_dev
dev
true
env_pro
pro
```
**SpringBoot中读取maven设置值**
```yaml
spring:
profiles:
active: @profile.active@
```
注意:如果pom.xml文件当中设置更改了另一个环境为true,但是运行的时候没有更改成功,这个问题是因为idea缓存的原因,你直接再meven当中手动compile一下就可以,所以一般都是在你更新pom文件的时候就compile
private static final Logger log= LoggerFactory.getLogger(BookController.class);
@GetMapping
public String toIndex(){
log.info("info...");
log.warn("warn...");
log.error("error...");
return "log日志文件";
}
- TRACE:运行堆栈信息,使用率低
- DEBUG:程序员调试代码使用
- INFO:记录运维过程数据
- WARN:记录运维过程报警数据
- ERROR:记录错误堆栈信息
- FATAL:灾难信息,合并计入ERROR
yml配置文件当中更改日志级别
使用lombok快速创建日志对象
、、、添加依赖
org.projectlombok
lombok
、、、给类加上@Slf4j注解,就可以直接使用了
@Slf4j
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String toIndex(){
log.info("info...");
log.warn("warn...");
log.error("error...");
return "log日志文件";
}
}
```yaml
logging:
pattern:
console: "%d %clr(%p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"
```
springboot_09_hot_deploy
1、手动启动热部署
(2)、热部署的范围
```yaml
spring:
devtools:
restart:
# 设置不参与热部署的文件或文件夹
exclude: static/**,public/**,config/application.yml
```
(springboot_10_configuration)
注意::松散绑定就是你在配置文件yml当中,可以使用很多种命名方式,我们最常用的就是烤肉串模式,前面使用@configurationProperties获取数据直接全小写就可以了
、、、java
@Data
@Component
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
private Duration serverTime;//这个没有绑定单位,所以我们再yml当中的数据带上单位
@DataSizeUnit(DataUnit.MEGABYTES)//再这里绑定单位就不用再yml当中带单位了
private DataSize dataSize;
}
、、、yaml
servers:
serverTime: 10m
data-size: 100
时间的话再yml配置文件当中默认是ms,所以不好写,datasize是容量大小,再配置文件当中是单位是by,不方便使用,我们可以再定义pojo类的时候给他加上单位
javax.validation
validation-api
org.hibernate.validator
hibernate-validator
这里不用配置版本的原因是我们的springboot里面有
@Data//lombokchuachua快速创建pojo类
@Component//注册bean
@ConfigurationProperties(prefix = "servers")//这个是yml当中提取数据
//开启校验功能
@Validated
public class ServerConfig {
private String ip;
@Max(value = 400,message = "最大值不能超过400")//给我们属性添加范围最大值
private int port;
}
总结:开启Bean属性校验功能一共3步:
导入JSR303与Hibernate校验框架坐标、
使用@Validated注解启用校验功能、
使用具体校验规则规范数据校验格式
(springboot_11_text)
@SpringBootTest(properties = {"test.post=testValue1"})//使用临时参数测试程序
@SpringBootTest(args = {"--test.post=testValue2"})
//使用args来,这个的话需要加--,和我们之前运输jar的时候设置临时参数一样
class Springboot11TextApplicationTests {
@Value("${test.post}")
private String msg;
@Test
void contextLoads() {
System.out.println(msg);
}
}
、、、配置类
@Configuration
public class MsgConfig {
@Bean
public String msg(){
return "MsgConfig";
}
}
、、、测试里面导入并且使用
@SpringBootTest
@Import(MsgConfig.class)
public class MsgConfigTest {
@Autowired
private String msg;
@Test
void testConfiguration(){
System.out.println(msg);
}
}
、、、控制器
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String toIndex(){
return "books";
}
}
、、、测试类
//1.这个就是配置虚拟端口,后面的表明端口是随机的
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//2.开启虚拟mvc调用
@AutoConfigureMockMvc
public class WebTest {
@Test
//3。注入虚拟mvc调用对象
void indexTest(@Autowired MockMvc mvc) throws Exception {
//4.创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
//5.执行请求
ResultActions actions = mvc.perform(builder);
}
//这个就是配置虚拟端口,后面的表明端口是随机的
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟mvc调用
@AutoConfigureMockMvc
public class WebTest {
@Test
void testStatus(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
//执行请求
ResultActions actions = mvc.perform(builder);
//定义本次调用的预期值
StatusResultMatchers status = MockMvcResultMatchers.status();
//预计本次调用时成功的状态200
ResultMatcher ok = status.isOk();
//添加预计值到本次调用过程中进行匹配
actions.andExpect(ok);
}
}
- 响应体匹配(非json数据格式)
```JAVA
@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
//设定预期值 与真实值进行比较,成功测试通过,失败测试失败
//定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.string("springboot2");
//添加预计值到本次调用过程中进行匹配
action.andExpect(result);
}
```
- 响应体匹配(json数据格式,开发中的主流使用方式)
```JAVA
@Test
void testJson(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
//设定预期值 与真实值进行比较,成功测试通过,失败测试失败
//定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot2\",\"type\":\"springboot\"}");
//添加预计值到本次调用过程中进行匹配
action.andExpect(result);
}
```
- 响应头信息匹配
```JAVA
@Test
void testContentType(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
//设定预期值 与真实值进行比较,成功测试通过,失败测试失败
//定义本次调用的预期值
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher contentType = header.string("Content-Type", "application/json");
//添加预计值到本次调用过程中进行匹配
action.andExpect(contentType);
}
```
只要注解@Transactional出现的位置存在注解@SpringBootTest,
springboot就会认为这是一个测试程序,无需提交事务,所以也就可以避免事务的提交。
- HikariCP(默认)
- Tomcat提供DataSource
- Commons DBCP
、、、使用druid
```YAML
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
```
、、、换成是默认的数据源HikariCP后
两种方案,第一,直接将druid方法的druid哪一行删除就可以
第二:
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
maximum-pool-size: 50 //这个配置其他的配置
首先需要导入依赖
```xml
org.springframework.boot
spring-boot-starter-jdbc
**步骤④**:使用JdbcTemplate实现查询操作(实体类封装数据的查询操作)
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
String sql = "select * from tbl_book";
//这个是封装数据模型,不封装模型的话,我们使用jdbcTemplate拿到的数据是map数组
//而封装后我们拿到的就是book类型的数据
RowMapper rm = new RowMapper() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book book = new Book();
book.setId(rs.getInt("id"));
book.setName(rs.getString("name"));
book.setType(rs.getString("type"));
book.setDescription(rs.getString("description"));
return book;
}
};
List list = jdbcTemplate.query(sql, rm);
System.out.println(list);
}
如果想对JdbcTemplate对象进行相关配置,可以在yml文件中进行设定,具体如下:
```yaml
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数
导入依赖(工程为web工程)
com.h2database
h2
org.springframework.boot
spring-boot-starter-data-jpa
yml当中的配置
spring:
h2:
console:
enabled: true
path: /h2
```
web端访问路径/h2,访问密码123456,如果访问失败,先配置下列数据源,
启动程序运行后再次访问/h2路径就可以正常访问了
```yaml
datasource:
url: jdbc:h2:~/test
hikari:
driver-class-name: org.h2.Driver
username: sa
password: 123456
注意::其实我们只是换了一个数据库而已,其他的东西都不受影响。一个重要提醒,别忘了,上线时,把内存级数据库关闭,采用M据持久化方ySQL数据库作为数案,关闭方式就是设置enabled属性为false即可。
windows启动redis::redis-server.exe redis.windows.conf
、、依赖
org.springframework.boot
spring-boot-starter-data-redis
、、、yml配置文件
spring:
redis:
host: localhost
port: 6379
这个数据库一般都是数据变化很快,我们一般使用的是robo3T来操作文档库
1.依赖
org.springframework.boot
spring-boot-starter-data-mongodb
2.使用
、、、yml页面当中,url和mysql数据库一样的格式
spring:
data:
mongodb:
uri: mongodb://localhost/itheima
、、、java
@SpringBootTest
class Springboot14MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void testMongo(){
Book book = new Book();
book.setId(2);
book.setName("stringboot");
book.setType("stringboot");
book.setDescription("stringboot");
mongoTemplate.save(book);
}
@Test
void testFind(){
List books = mongoTemplate.findAll(Book.class);
System.out.println(books);
}
}
springboot_15_cache(依赖有lombok,springboot-web,mybatispuls,druid,mysql,test)
、、、xml
org.springframework.boot
spring-boot-starter-cache
第二步:再我们的启动程序哪里启动缓存
重点:: 使用缓存技术,一定不能忘记再application哪里添加启用缓存
@SpringBootApplication
//启用缓存技术
@EnableCaching
public class Springboot15CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot15CacheApplication.class, args);
}
}
第四步,再service实现类当中使用缓存、
注意::@Cacheable这个注解存储数据可以拿,可以存,key就是我们设置的key,key对应的值就是我们这个方法的返回值。但是如果你要是那种60秒验证码变更的那种,就需要使用@Cacheput,这个只能往缓存里放数据
@Override
@Cacheable(value = "cacheSpring",key = "#id")//这样就对这个方法使用了缓存,
//value定义的是数据存储的一个大的key,最终的结果存在于key
public Book selectById(int id) {
return bookDao.selectById(id);
}
pojo类
@Data
public class SMSCode {
private String phone;
private String code;
}
工具类(获取验证码,和从缓存当中提取验证码)
@Component
public class CodeUtils {
private String[] patch = {"000000","00000","0000","000","00","0",""};
//这个是获取验证码的
public String generator(String smsPhone){
//加密获取6位验证码
int hash = smsPhone.hashCode();
int encryption = 20011128;
long result = hash ^ encryption;//第一次加密
long nowtime = System.currentTimeMillis();
result = result ^ nowtime;//第二次加密,获取系统时间
long code = result % 1000000; //获取后六位数字
code = code < 0 ? -code : code; //取出来的数据可能为负数,所以这里判断一下
String strCode = code + "";
int len = strCode.length();
return patch[len]+strCode; // 因为取出来的数字可能前面为0,而不是六位数,所以需要补零
}
//这个是从缓存当中拿验证码的,
//需要注意的是,@Cacheable这个注解的返回值就是我们的value,如果缓存当中有数据,就会返回缓存当中的,没有数据就返回方法的返回值
@Cacheable(value = "SMSphone",key = "#phone")
public String get(String phone){
return null;
}
}
service接口和实现类
、、、接口
public interface SMSCodeService {
public String getCaptcha(String phone);
public boolean ifCaptcha(SMSCode smsCode);
}
、、、实现类
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
//获取验证码
@Override
// @Cacheable(value = "SMSphone",key = "#phone")
@CachePut(value = "SMSphone",key = "#phone")//因为我们只希望向缓存当中存储验证码,不希望第二次请求的时候拿到验证码,所以使用这个注解可以只存不取
public String getCaptcha(String phone) {
String smsCode = codeUtils.generator(phone);
return smsCode;
}
//判断验证码
@Override
public boolean ifCaptcha(SMSCode smsCode) {
//这个是拿到用户输入的code
String code = smsCode.getCode();
//这个是调用工具类当中的方法,拿到缓存当中数据
String queryCode = codeUtils.get(smsCode.getPhone());
return code.equals(queryCode);
}
}
第一步:再原有的依赖文件上我们的
net.sf.ehcache
ehcache
第二步:将我们ehcache的配置文件导入进来(ehcache.xml)
//这个是缓存存放的位置
第三步:再环境当中将默认的cache该化成ehcache(yml文件当中)
spring:
cache:
type: ehcache
ehcache:
config: classpath:ehcache.xml //记住这里,直接导入文件位置找不到的话,就要使用classpath导入
再就可以直接使用了,springboot好的点在于,之前使用的默认的cache,再将ehcache技术导入进来后,我们可以直接使用
**步骤①**:导入redis的坐标
```xml
org.springframework.boot
spring-boot-starter-data-redis
```
如果需要对redis作为缓存进行配置,注意不是对原始的redis进行配置,而是配置redis作为缓存使用相关的配置,隶属于spring.cache.redis节点下,注意不要写错位置了。
```yaml
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
redis:
use-key-prefix: false 是否使用前缀名字
key-prefix: sms_ 我们自己给它加前缀名字
cache-null-values: false 是否应许存储空值
time-to-live: 10s 缓存时间
```
使用这个技术需要注意的是和redis一样,需要将程序启动,而且暂时springboot内部还没有memcached,所以需要我们导入版本号
导入依赖
com.googlecode.xmemcached
xmemcached
2.4.7
将memcached纳入ioc管理,配置Bean
@Configuration
public class XMemcachedConfig {//这个是我们的memcached缓存技术的配置文件
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
//因为MemcachedClient需要通过MemcachedClientBuilder的builder方法获取
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder("localhost:11211");
MemcachedClient memcachedClient = memcachedClientBuilder.build();
return memcachedClient;
}
}
使用方法
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils; (这个是工具类,用来获取我们的6位数字验证码的)
@Autowired
private MemcachedClient memcachedClient;
@Override
public String getCaptcha(String phone) {
String generator = codeUtils.generator(phone);
try {
//这三个参数,一个是我们的key,一个是效应时间,一个是value
memcachedClient.set(phone,10,generator);
} catch (Exception e) {
e.printStackTrace();
}
return generator;
}
@Override
public boolean ifCaptcha(SMSCode smsCode) {
String code = null;
try {
code = memcachedClient.get(smsCode.getPhone()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return smsCode.getCode().equals(code);
}
}
然后直接在我们给memcached配置文件里面引用就可以了
目前jetcache支持的缓存方案本地缓存支持两种,远程缓存支持两种,分别如下
- 本地缓存(Local)
- LinkedHashMap
- Caffeine
- 远程缓存(Remote)
- Redis
- Tair
///1.添加依赖
com.alicp.jetcache
jetcache-starter-redis
2.6.2
、、、2.yml当中配置
spring:
main:
allow-circular-references: true //将循环依赖关闭
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig: //这个必须要配置,不配置的话会报错
maxTotal: 50
、、、3.使用需要开启jetcache
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class Springboot16JetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot16JetcacheApplication.class, args);
}
}
、、、4.使用
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(name = "jetcache_",expire = 10,timeUnit = TimeUnit.SECONDS)
private Cache jetcache;
@Override
public String getCaptcha(String phone) {
String generator = codeUtils.generator(phone);
jetcache.put(phone,generator);
return generator;
}
@Override
public boolean ifCaptcha(SMSCode smsCode) {
String code = jetcache.get(smsCode.getPhone());
return smsCode.getCode().equals(code);
}
}
注意:: 因为现在的版本使用纯远程有一个依赖循环的bug,所以我们需要在yml配置文件当中将循环依赖关闭
spring:
main:
allow-circular-references: true
、、、yml配置
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
开启jetcache等都和前面一样,值得注意的是,我们在使用的时候可以指定使用哪一种
cacheType = CacheType.LOCAL这个是指定使用本地缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL)
private Cache jetCache;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
jetCache.put(tele,code);
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
导入依赖
org.springframework.boot
spring-boot-starter-quartz
创建我们需要定时发送的工作,记住,需要让它继承QuartzJobBean
public class MYQuartz extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("你好");
}
}
配置文件类
@Configuration
public class QuartzConfig {//触发器
@Bean
public JobDetail printJobDetail(){ //工作明细
//绑定具体的工作
return JobBuilder
.newJob(MYQuartz.class)
.storeDurably()
.build();
}
@Bean
public Trigger printJobTrigger(){
//这个是设置发送时间
ScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
//绑定对应的工作明细
return TriggerBuilder
.newTrigger()
.forJob(printJobDetail())
.withSchedule(schedBuilder)
.build();
}
}
这个是springboot自己推出的,更加简单方便
```java
@SpringBootApplication
//开启定时任务功能
@EnableScheduling
public class Springboot22TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot22TaskApplication.class, args);
}
}
```
**步骤②**:定义Bean,在对应要定时执行的操作上方,
使用注解@Scheduled定义执行的时间,执行时间的描述方式还是cron表达式
```java
@Component
public class MyBean {
@Scheduled(cron = "0/1 * * * * ?")
public void print(){
System.out.println(Thread.currentThread().getName()+" :spring task run...");
}
}
导入springboot整合javamail的starter
```xml
org.springframework.boot
spring-boot-starter-mail
```
第二步,yaml配置邮箱登录信息
我们的密码写的是开启smtp服务时候的给的编号
第三步:编写程序,添加发送邮件需要的信息
```java
@Service
public class SendMailServiceImpl implements SendMailService {
//发送人
private String from = "[email protected]";
//接收人
private String to = "[email protected]";
//标题
private String subject = "测试邮件";
//正文
private String context = "测试邮件正文内容";
@Autowired
private JavaMailSender javaMailSender;
@Override
public void sendMail() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from+"(小甜甜)"); //这个后面添加了名称,那么发送邮件显示名称就会是我们添加的名称
message.setTo(to);
message.setSubject(subject);
message.setText(context);
javaMailSender.send(message);
}
}
```
将发送邮件的必要信息(发件人、收件人、标题、正文)封装到SimpleMailMessage对象中
,可以根据规则设置发送人昵称等。
第四步,运行这个方法即可;
如果需要发送带有附件的邮件,那我们所创建的对象有所区别,但是我们的步骤相差不大
```JAVA
@Service
public class SendMailServiceImpl2 implements SendMailService {
@Autowired
private JavaMailSender javaMailSender;
//发送人
private String from = "[email protected]";
//接收人
private String to = "[email protected]";
//标题
private String subject = "测试邮件";
//正文
private String context = "测试邮件正文";
public void sendMail() {
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,true); //此处设置支持附件
helper.setFrom(to+"(小甜甜)");
helper.setTo(from);
helper.setSubject(subject);
helper.setText(context);
//添加附件
File f1 = new File("springboot_23_mail-0.0.1-SNAPSHOT.jar");
File f2 = new File("resources\\logo.png");
helper.addAttachment(f1.getName(),f1);
helper.addAttachment("最靠谱的培训结构.png",f2);
javaMailSender.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1. springboot整合javamail其实就是简化了发送邮件的客户端对象JavaMailSender的初始化过程,
通过配置的形式加载信息简化开发过程
目前企业级开发中广泛使用的消息处理技术共三大类,具体如下:
- JMS
- AMQP
- MQTT
server层接口和实现类
、、、、接口
public interface MessageService {
void sendMessage(String id);
String doMessage();
}
public interface OrderService {
void order(String id);
}
、、、实现类
@Service
public class MessageServiceImpl implements MessageService {
private ArrayList msgList = new ArrayList();
@Override
public void sendMessage(String id) {
msgList.add(id);
System.out.println("已经将订单纳入待处理"+id);
}
@Override
public String doMessage() {
String id = msgList.remove(0);
System.out.println(id+"已经处理完毕");
return id;
}
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private MessageService messageService;
@Override
public void order(String id) {
//这里写处理业务的操作
System.out.println("订单开始处理");
//短信消息处理
messageService.sendMessage(id);
System.out.println("订单处理完毕");
}
}
表现层::
@RequestMapping("/orders")
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
//发送消息
@PostMapping("/{id}")
public void order(@PathVariable("id")String id){
orderService.order(id);
}
}
------------------------
@RequestMapping("/mes")
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
//接受消息
@GetMapping
public String message(){
String id = messageService.doMessage();
return id;
}
}
yaml
导入依赖坐标
org.springframework.boot
spring-boot-starter-activemq
yml当中的配置
server:
port: 80
spring:
activemq:
broker-url: tcp://localhost:61616
jms:
template:
default-destination: xsh 这个是如果后面没有设置消息的存储位置,就用这个
业务层
这里我们在message当中设置的消息的发送和接受,order当中负责消息的发送,调用message当中的消息发送方法
public interface OrderService {
void order(String id);
}
@Service
public class OrderServiceActivemqImpl implements OrderService {
@Autowired
private MessageService messageServiceActivemq;
@Override
public void order(String id) {
//这里写处理业务的操作
System.out.println("订单开始处理");
//短信消息处理
messageServiceActivemq.sendMessage(id);
System.out.println("订单处理完毕");
}
}
public interface MessageService {
void sendMessage(String id);
String doMessage();
}
@Service
public class MessageServiceActivemqImpl implements MessageService {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Override
public void sendMessage(String id) {
System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
jmsMessagingTemplate.convertAndSend("order.queue.id",id);
//order.queue.id这个是指定它存放的位置
}
@Override
public String doMessage() {
String id =jmsMessagingTemplate.receiveAndConvert("order.queue.id",String.class);
//因为前面存储指定了位置,所以这里需要加入取数据的地址
System.out.println("已完成短信发送业务,id:"+id);
return id;
}
}
表现层:
@RequestMapping("/orders")
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
//发送消息
@PostMapping("/{id}")
public void order(@PathVariable("id")String id){
orderService.order(id);
}
}
----------
@RequestMapping("/mes")
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
//接受消息
@GetMapping
public String message(){
String id = messageService.doMessage();
return id;
}
}
**步骤④**:使用消息监听器在服务器启动后,监听指定位置,当消息出现后,立即消费消息
@Component
public class MessageListener {
@JmsListener(destination = "order.queue.id")//当这个里面有消息,就会立即执行下面的代码
@SendTo("order.other.queue.id") //这个会将返回值作为消息发送到括号里面的位置保存起来
public String receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
return "new:"+id;
}
}
步骤5:切换消息模型由点对点模型到发布订阅模型,修改jms配置即可
```yaml
spring:
activemq:
broker-url: tcp://localhost:61616
jms:
pub-sub-domain: true
**总结**
1. springboot整合ActiveMQ提供了JmsMessagingTemplate对象作为客户端操作消息队列
2. 操作ActiveMQ需要配置ActiveMQ服务器地址,默认端口61616
3. 企业开发时通常使用监听器来处理消息队列中的消息,设置监听器使用注解@JmsListener
4. 配置jms的pub-sub-domain属性可以在点对点模型和发布订阅模型间切换消息模型
第一步:导入依赖坐标
org.apache.rocketmq
rocketmq-spring-boot-starter
2.2.1
第二步:yml当中的配置
#配置RocketMQ的服务器地址
rocketmq:
name-server: localhost:9876
producer:
group: group_rocketmq #给一个组名
第三步:消息的发送
里面存储消息第一个填写的就是我们写的消息存储的位置
@Service
public class MessageServiceRocketmqImpl implements MessageService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public void sendMessage(String id) {
System.out.println("已经纳入待处理订单 id:"+id);
//这个是同步消息
// rocketMQTemplate.convertAndSend("order_rocketmq_id",id);
//这个是异步消息
SendCallback callback = new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("异步消息发送成功");
}
@Override
public void onException(Throwable throwable) {
System.out.println("异步消息发送失败");
}
};
rocketMQTemplate.asyncSend("order_rocketmq_id",id,callback);
}
@Override
public String doMessage() {
return null;
}
}
第四步: 设置监听器,当有消息时候,立即消费消息
@Component
@RocketMQMessageListener(topic = "order_rocketmq_id",consumerGroup = "group_rocketmq")
//这里将组名,还有我们的消息存储的位置
public class MessageListener implements RocketMQListener {//设置有消息自动消费
@Override
public void onMessage(String s) {
System.out.println("已经完成消息的发送:id:"+s);
}
}
**总结**
1. springboot整合RocketMQ使用RocketMQTemplate对象作为客户端操作消息队列
2. 操作RocketMQ需要配置RocketMQ服务器地址,默认端口9876
3. 企业开发时通常使用监听器来处理消息队列中的消息,设置监听器使用注解@RocketMQMessageListener