目录
2、整合mybatis(spring_04_mybatis)
一、入门案例
1、创建的控制器操作
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById(){
System.out.println("springboot getById");
return "springboot getById";
}
}
2、在Idea中隐藏指定文件/文件夹
因为我们所创建的工程是使用别人的模板,有很多很多个包,有些包不需要使用,我们就可以将文件夹隐藏,从而更加简洁
. Idea中隐藏指定文件或指定类型文件
1. 【Files】→【Settings】
2. 【Editor】→【File Types】→【Ignored Files and Folders】
3. 输入要隐藏的名称,支持*号通配符
4. 回车确认添加
3、SpringBoot基础配置
1.在工作空间赋值对应的工程,并修改工程名称。
2.只留下src 喝pox.xml文件
3.使用的时候,直接复制模板,将pox.xml文件打开将artifactId与新工程名字相同
4.在idea当中将工程导入进去,刷新maven
二、基础配置
SpringBoot默认配置文件是application.properties
1、修改配置
直接name=value的形式
例子::: #服务器的端口配置
server.port=8081
#设置日志相关
logging.level.root=debug
2、springboot的三种配置文件格式
- properties格式(默认配置文件格式)
- yml格式(主流)
- yaml格式
- application.properties(properties格式)
```properties
server.port=80
```
- application.yml(yml格式)
```YML
server:
port: 81
```
- application.yaml(yaml格式)
```yaml
server:
port: 82
```
3、配置文件的优先级
application.properties > application.yml > application.yaml
**总结**
1. 配置文件间的加载优先级 properties(最高)> yml > yaml(最低)
2. 不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
4、 yml语法规则
*大小写敏感
*属性层级关系使用多行描述,每行结尾使用冒号结束
*使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
*属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
* #号 表示注释
2. 注意属性名冒号后面与数据之间有一个**空格**
3. 字面值、对象数据格式、数组数据格式
下面是yml数据格式
5、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;
6、yaml文件中的数据引用
使用的也是${}
```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
```
7、将整个数据封装为Environment
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";
}
8、读取ymal当中引用型数据
@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";
}
三、整合第三方技术
1、整合junit
这个就是我们测试类,如果测试类不在我们启动类的包或者子包当中,我们就添加classes来指定配置类
```JAVA
//在注解@SpringBootTest中添加classes属性指定配置类
@SpringBootTest(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
//注入你要测试的对象
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
//执行要测试的对象对应的方法
bookDao.save();
System.out.println("two...");
}
}
```
2、整合mybatis(spring_04_mybatis)
(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);
}
}
3、整合mybatis-plus
(1)添加依赖,在将module添加mysql包创建出来后,我们将mybatis-plus的jar依赖导入进去
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
(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<Book> ,指定表的类型,然后就会帮我们将数据库的一些增删改查的方法写出来
//如果是整合mybatis-plus的话,我们这里的方法不需要写具体的方法操作了
//mybatis-plus帮助我们写了
@Mapper
public interface BookDao extends BaseMapper<Book> {
}
(5)测试方法
@SpringBootTest
class Spring05MybatisPlusApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
System.out.println(bookDao.selectById(3));
}
}
四、springboot整合ssmp的案例
1.导入的jar包依赖
model哪里选择spring web ,mysql Driver
手动导入的有::前面不加mybatis是因为我们使用的mybatis-plus,所以不需要添加
<!--mybatis-plus的jar包依赖-->这个永华简化Mapper操作
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--druid的jar依赖文件-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
<!--lombok-->这个给是方便我们实例化类
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2、配置application.yml文件
(1)开启日志文件
3.创建pojo类
使用lombok的话,我们直接添加@Data属性
4.创建dao映射接口
//这个我们使用的是mybatis-plus,可以简化Mapper文件,
//只需要继承BaseMapper<这里写我们pojo类>
@Mapper
public interface BookDao extends BaseMapper<Book> {
}
5、测试我们的映射dao接口的方法
(1)分页查询::
分页查询获取查询数据,直接用创建的 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;
}
}
(2)按条件查询
这个记住一点,LambdaQueryWrapper是用来封装条件的对象,
QueryWrapper的话可能我们查询的条件name会打错,所以我们一般使用第一种情况
第一个是按条件分页查询,第二个是我们普通的按条件查询所有
//按条件查询
@Test
void testGetBy(){
String name = "spring";
//这个是要传入值的对象,可以封装我们的条件
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
//因为我们给条件传值的时候为null会将null作为值传入进去,所有LambdaQueryWrapper对象提供了这个值name!=null
//将这个值传入进去就,如果为为空,我们就不将条件拼接进去
lqw.like(name!=null,Book::getName,name);
bookDao.selectList(lqw);
}
(3)其他方法
@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);
}
6.业务层service接口
一般service接口的名称定义为业务名称,与dao当中的名称有所区分
public interface BookService {
Boolean save(Book book);
Boolean delete(Integer id);
Boolean update(Book book);
Book getById(Integer id);
List<Book> getAll();
//分页查询,两个参数,当前页码,每页显示数目
IPage<Book> getPage(int currentPage,int pageSize);
}
7.业务层service接口的实现类
@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<Book> getAll() {
return bookDao.selectList(null);
}
//分页查询
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage,pageSize);
return bookDao.selectPage(page,null);
}
}
8.业务层快速开发
直接写我们的业务层接口继承IService<T>,然后接口实现类继承业务通用实现类。
注意::要记住的就是ServiceImpl<M,T>一般就是ServiceImpl<BookDao,Book> ,M就是dao层的业务接口
service接口
public interface IBookService extends IService<Book> {
boolean saveBook(Book book);
boolean modify(Book book);
boolean delete(Integer id);
//分页
IPage<Book> getPage(int currentPage, int pageSize);
//分页加条件查询
IPage<Book> getPage(int currentPage, int pageSize,Book book);
}
service接口的实现
@Service//把这个纳入ioc容器管理
public class IBookServiceImpl extends ServiceImpl<BookDao,Book> implements IBookService {
//要记住的就是ServiceImpl<M,T>一般就是ServiceImpl<BookDao,Book> ,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<Book> getPage(int currentPage, int pageSize) {
//如果只是分页查询这样就够了
IPage<Book> page = new Page<Book>(currentPage,pageSize);
return bookDao.selectPage(page,null);
}
//分页查询+条件查询
@Override
public IPage<Book> getPage(int currentPage, int pageSize,Book book) {
IPage<Book> page = new Page<Book>(currentPage,pageSize);
//这个是要传入值的对象,可以封装我们的条件
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
//讲条件添加进去
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;
}
}
9、控制表现层的开发 controller
但是我们一般使用的是统一格式的方法,不然前端拿到的数据比较乱
//@RestController
@RequestMapping("/books")
public class BookController2 {//表现层,这里的表现层数据没有统一格式
//@Autowired
private IBookService iBookService;
@GetMapping
public List<Book> 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<Book> 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<Book> 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");
}
}
10.前端页面的实现
这里,我直接导入老师的前端资源,然后跟着配套写。
写的话先找到页面模块在哪里,跟着点击事件设置的下一步操作去写
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>基于SpringBoot整合SSM案例</title>
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>图书管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="图书类别" v-model="pagination.type" style="width: 200px;" class="filter-item"></el-input>
<el-input placeholder="图书名称" v-model="pagination.name" style="width: 200px;" class="filter-item"></el-input>
<el-input placeholder="图书描述" v-model="pagination.description" style="width: 200px;" class="filter-item"></el-input>
<el-button @click="getAll()" class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel()">取消</el-button>
<el-button type="primary" @click="handleAdd()">确定</el-button>
</div>
</el-dialog>
</div>
<!-- 编辑标签弹层 -->
<div class="add-form">
<el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">
<el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="updateCancel()">取消</el-button>
<el-button type="primary" @click="handleEdit()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//当前页要展示的列表数据
dialogFormVisible: false,//添加表单是否可见
dialogFormVisible4Edit:false,//编辑表单是否可见
formData: {},//表单数据
rules: {//校验规则
type: [{ required: true, message: '图书类别为必填项', trigger: 'blur' }],
name: [{ required: true, message: '图书名称为必填项', trigger: 'blur' }]
},
pagination: {//分页相关模型数据
currentPage: 1,//当前页码
pageSize:10,//每页显示的记录数
total:0, //总记录数
type:"", //条件查询数据
name:"",
description:""
}
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
//调用查询全部数据
this.getAll();
},
methods: {
//列表
/* getAll() {
//发送异步请求
axios.get("/books").then((res)=>{
this.dataList=res.data.data; //第一个data是拿到我们后端R表现层数据一致性处理的数据,第二个data是拿到r里面的数据
})
},*/
getAll(){
//条件查询数据,因为是get请求,我们需要做字符串的拼接
param = "?query";
param += "&type=" + this.pagination.type;
param += "&name=" + this.pagination.name;
param += "&description=" + this.pagination.description;
//分页查询
axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize + param).then((res)=>{
this.dataList=res.data.data.records; //将查询到的数据给到表单数据存储当中
//下面的是分页查询的页数,多少条,一共多少条
this.pagination.currentPage=res.data.data.current;
this.pagination.pageSize=res.data.data.size;
this.pagination.total=res.data.data.total;
})
},
//切换页码
handleCurrentChange(currentPage) {
//修改页码值为当前选中的页码值
this.pagination.currentPage=currentPage;
//修改页码后执行查询
this.getAll();
},
//弹出添加窗口
handleCreate() {
//因为我们将添加表单设置为不可见,所以这里设置点击新建我们将弹窗改为true,让它弹出来
this.dialogFormVisible=true;
//一般清空数据设置在打开弹窗最为妥当
this.resetForm();
},
//重置表单
resetForm() {
//将添加弹窗数据清空
this.formData={};
},
//添加
handleAdd () {
//这个this.formData就是将我们弹出的添加窗口当中的数据添加打包传参
//如果不添加操作失败,我们就不关闭弹窗
axios.post("/books",this.formData).then((res)=>{
if(res.data.flag){
//1.操作成功,将弹窗关闭
this.dialogFormVisible=false;
this.$message.success(res.data.msg);
}else {
this.$message.error(res.data.msg);
}
}).finally(()=>{
//2.添加了数据,重新刷新页面
this.getAll();
})
},
//添加弹窗取消
cancel(){
this.dialogFormVisible=false;//关闭添加创库弹窗
this.$message.info("您取消了当前操作")
},
// 删除
handleDelete(row) {
//删除操作我们得设置一个提醒,万一用户手抖点错了就恢复不了,所以设置一个弹窗
this.$confirm("此操作将会删除当前数据,是否继续","删除弹窗提示",{type:"info"}).then(()=>{
//弹窗点击确定
axios.delete("/books/"+row.id).then((res)=>{
//判断是否删除成功
if(res.data.flag){
this.$message.success("删除成功")
}else {
this.$message.error("删除失败")
}
}).finally(()=>{
//不管成功与否,都重新刷新页面,调用查询所有
this.getAll();
});
}).catch(()=>{
//弹窗点击取消
this.$message.info("您取消了删除操作");
});
},
//弹出编辑窗口
handleUpdate(row) {
//将我们编辑的哪一个数据查询编辑窗口表单当中
axios.get("/books/"+row.id).then((res)=>{
//出现1异常的情况
if(res.data.flag && res.data.data!=null){
this.formData=res.data.data
//将编辑窗口改为true
this.dialogFormVisible4Edit=true;
}else{
this.$message.error("数据同步失败,自动刷新");
}
}).finally(()=>{
this.getAll();
})
},
//修改
handleEdit() {
axios.put("/books",this.formData).then((res)=>{
//判断是否操作成功
if(res.data.flag){
this.$message.success("操作成功");
this.dialogFormVisible4Edit=false;
}else{
this.$message.error("操作失败");
}
}).finally(()=>{
this.getAll();
})
},
//编辑弹窗取消
updateCancel(){
this.dialogFormVisible4Edit=false;//关闭添加创库弹窗
this.$message.info("您取消了当前操作")
},
}
})
</script>
</html>
五、运维实用篇
1、SpringBoot程序的打包与运行
运行项目:java后面那个就是打包后的文件名字
下面的是我们检查端口的操作::
2、配置临时属性
3.配置文件4级分类
(1)一级::在我们编程的resources当中的:application.xml是给我们程序员用的
(2)二级:: 在编程的resources当中创建config包下的application.yml是给项目经理用的,而皮质等级高于前面
(3)三级::打包后的jar文件同级目录下的application.yml,
(4)四级::打包后的jar文件同级目录下的config,config包里面的application
等级越高,优先级越高,相同配置会覆盖前面的配置,不同的配置,会联合一起使用
4、自定义配置文件
这个是根据名字,也可以根据路径 --spring.config.location=classpath:/ebank.yml
5、多环境开发
(1)配置
yml文件当中,不同的环境用 --- 分隔开
#应用环境,当前在使用的环境
spring:
profiles:
active: dev
#设置环境
---
#生产环境
spring:
profiles: pro
server:
port: 80
---
#开发环境
spring:
profiles: dev
server:
port: 81
---
#测试环境
spring:
profiles: test
server:
port: 82
第二种方式
(2)多环境开发分组管理
include的话,我们的主配置文件会在最后加载,而group的话就是先加载主配置文件,再加载指定的附属配置文件
(3)多环境的开发控制
pox.xml文件当中的配置
**maven中设置多环境(使用属性方式区分环境)**
```xml
<profiles>
<profile>
<id>env_dev</id>
<properties>
<profile.active>dev</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault> <!--默认启动环境-->
</activation>
</profile>
<profile>
<id>env_pro</id>
<properties>
<profile.active>pro</profile.active>
</properties>
</profile>
</profiles>
```
**SpringBoot中读取maven设置值**
```yaml
spring:
profiles:
active: @profile.active@
```
注意:如果pom.xml文件当中设置更改了另一个环境为true,但是运行的时候没有更改成功,这个问题是因为idea缓存的原因,你直接再meven当中手动compile一下就可以,所以一般都是在你更新pom文件的时候就compile
6、日志文件
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快速创建日志对象
、、、添加依赖
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
、、、给类加上@Slf4j注解,就可以直接使用了
@Slf4j
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String toIndex(){
log.info("info...");
log.warn("warn...");
log.error("error...");
return "log日志文件";
}
}
7、日志输出格式与控制
```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)
1、绑定第三方的bean
2、松散绑定
注意::松散绑定就是你在配置文件yml当中,可以使用很多种命名方式,我们最常用的就是烤肉串模式,前面使用@configurationProperties获取数据直接全小写就可以了
3、常量计量单位
、、、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类的时候给他加上单位
4、使用属性校验
<!--1.导入JSR303规范-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!--使用hibernate框架提供的校验器做实现-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
这里不用配置版本的原因是我们的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)
1、加载测试临时属性
@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);
}
}
2、加载测试专用属性
、、、配置类
@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);
}
}
3、使用测试类启动web环境
、、、控制器
@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);
}
4、测试匹配信息
(1)匹配响应状态
//这个就是配置虚拟端口,后面的表明端口是随机的
@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);
}
}
(2)匹配响应体
- 响应体匹配(非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);
}
```
(3)响应头信息匹配
- 响应头信息匹配
```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);
}
```
5、业务层测试事务回滚
只要注解@Transactional出现的位置存在注解@SpringBootTest,
springboot就会认为这是一个测试程序,无需提交事务,所以也就可以避免事务的提交。
6、测试用例设置随机数
九、数据层解决方案
1、内置数据源
- 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 //这个配置其他的配置
2、jdbcTemplate持久化技术
首先需要导入依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency
```
**步骤④**:使用JdbcTemplate实现查询操作(实体类封装数据的查询操作)
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
String sql = "select * from tbl_book";
//这个是封装数据模型,不封装模型的话,我们使用jdbcTemplate拿到的数据是map数组
//而封装后我们拿到的就是book类型的数据
RowMapper<Book> rm = new RowMapper<Book>() {
@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<Book> list = jdbcTemplate.query(sql, rm);
System.out.println(list);
}
如果想对JdbcTemplate对象进行相关配置,可以在yml文件中进行设定,具体如下:
```yaml
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数
3、内置数据库 h2
导入依赖(工程为web工程)
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
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即可。
(2) 数据库redis,springbot整合redis
windows启动redis::redis-server.exe redis.windows.conf
、、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
、、、yml配置文件
spring:
redis:
host: localhost
port: 6379
(3)springboot整合mogodb
这个数据库一般都是数据变化很快,我们一般使用的是robo3T来操作文档库
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
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<Book> books = mongoTemplate.findAll(Book.class);
System.out.println(books);
}
}
十、缓存
springboot_15_cache(依赖有lombok,springboot-web,mybatispuls,druid,mysql,test)
1、springboot内置缓存技术
、、、xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
第二步:再我们的启动程序哪里启动缓存
重点:: 使用缓存技术,一定不能忘记再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);
}
2、使用内置缓存技术实现验证码
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);
}
}
3、将实现验证码改换成ehcache技术
第一步:再原有的依赖文件上我们的
<!--ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
第二步:将我们ehcache的配置文件导入进来(ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="D:\sofo\ehcache" /> //这个是缓存存放的位置
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果,可用于记录时效性数据,例如验证码-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
<!--这里面的name就是我们使用缓存设置的那个value,大的环境的名字,这个可以给它进行配置-->
<cache
name="SMSphone"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
第三步:再环境当中将默认的cache该化成ehcache(yml文件当中)
spring:
cache:
type: ehcache
ehcache:
config: classpath:ehcache.xml //记住这里,直接导入文件位置找不到的话,就要使用classpath导入
再就可以直接使用了,springboot好的点在于,之前使用的默认的cache,再将ehcache技术导入进来后,我们可以直接使用
4、使用redis 整合redis
**步骤①**:导入redis的坐标
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
如果需要对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 缓存时间
```
5、整合Memcached技术
使用这个技术需要注意的是和redis一样,需要将程序启动,而且暂时springboot内部还没有memcached,所以需要我们导入版本号
导入依赖
<!--memcached-->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
将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);
}
}
我们可以创建一个pojo类,来存放我们需要配置的属性
然后直接在我们给memcached配置文件里面引用就可以了
6、使用jetcacha
目前jetcache支持的缓存方案本地缓存支持两种,远程缓存支持两种,分别如下
- 本地缓存(Local)
- LinkedHashMap
- Caffeine
- 远程缓存(Remote)
- Redis
- Tair
(1)使用纯远程redis缓存
///1.添加依赖
<!--jetcache-->
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
、、、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<String,String> 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
(2)本地缓存
、、、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<String ,String> 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);
}
}
7、j2cache缓存框架
十一、定时
(1)springboot整合Quartz定时任务
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
创建我们需要定时发送的工作,记住,需要让它继承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();
}
}
(2)整合task
这个是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...");
}
}
(3)springboot整合javamail发送邮件
导入springboot整合javamail的starter
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
```
第二步,yaml配置邮箱登录信息
我们的密码写的是开启smtp服务时候的给的编号
第三步:编写程序,添加发送邮件需要的信息
```java
@Service
public class SendMailServiceImpl implements SendMailService {
//发送人
private String from = "test@qq.com";
//接收人
private String to = "test@126.com";
//标题
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 = "test@qq.com";
//接收人
private String to = "test@126.com";
//标题
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的初始化过程,
通过配置的形式加载信息简化开发过程
十二、mq产品
目前企业级开发中广泛使用的消息处理技术共三大类,具体如下:
- JMS
- AMQP
- MQTT
(1)购物订单案例,消息发送
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<String> msgList = new ArrayList<String>();
@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
(2)SpringBoot整合ActiveMQ
导入依赖坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
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属性可以在点对点模型和发布订阅模型间切换消息模型
(3)SpringBoot整合RocketMQ
我们先启动命名服务器,后期的broker
第一步:导入依赖坐标
<!--rocketmq-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
第二步: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<String> {//设置有消息自动消费
@Override
public void onMessage(String s) {
System.out.println("已经完成消息的发送:id:"+s);
}
}
**总结**
1. springboot整合RocketMQ使用RocketMQTemplate对象作为客户端操作消息队列
2. 操作RocketMQ需要配置RocketMQ服务器地址,默认端口9876
3. 企业开发时通常使用监听器来处理消息队列中的消息,设置监听器使用注解@RocketMQMessageListener