## 整合操作
Lombok
FreeMaker
logback
Thymeleaf
PageHelper
SpringDoc
DevTools热部署
mybatis plus
数据校验validation
典型的Spring Boot项目的树形目录结构:
```
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── demo
│ │ │ └── DemoApplication.java
│ │ └── resources
│ │ ├── static
│ │ │ └── css
│ │ │ └── style.css
│ │ ├── templates
│ │ │ └── index.html
│ │ ├── application.properties
│ │ └── application.yml
│ └── test
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ └── DemoApplicationTests.java
│ └── resources
│ └── test.properties
└── pom.xml
```
新建springboot项目
1. 新建一个简单的项目
2. 配置pom
```xml
org.springframework.boot
spring-boot-starter-parent
3.0.6
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
```
web项目
1. 添加web依赖
```xml
org.springframework.boot
spring-boot-starter-web
```
2. 创建控制类
```java
@Controller
public class DemoController {
@RequestMapping("/show")
@ResponseBody
public String show(){
return "hello springboot"; // 响应给浏览器一句话
}
}
```
```yaml
server:
port: 8888
servlet:
context-path: /hello
```
其他参考[文档](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server)
Lombok
```xml
org.projectlombok
lombok
true
```
整合MyBatis
1. 引入Mybatis 驱动 引入mysql 驱动
~~~xml
org.mybatis.spring.boot
mybatis-spring-boot-starter
3.0.2
com.mysql
mysql-connector-j
runtime
~~~
```yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/msbsys?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
```
mybatis支持注解开发和xml文件开发
整合Druid
~~~xml
com.alibaba
druid-spring-boot-starter
1.2.11
~~~
~~~yaml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource #底层使用druid连接池
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/msbsys?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
username: root
password: root
druid:
# 连接池的配置信息
initial-size: 5 # 初始化连接数大小
max-active: 30 # 最大连接数
min-idle: 5 # 最小连接数
# 配置获取连接等待超时的时间
max-wait: 60000 #如果连接都被占用,最大等待时间1min=60000ms
validation-query: SELECT 1 FROM DUAL #通过从续表查询来监控是否连接数据库,语句执行能连接,语句不执行没连接
# 配置一个连接在池中最小空闲的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
test-while-idle: true
# 开启SQL监控、防火墙监控、日志监控
filters: stat,wall,slf4j
# 配置DruidStatViewServlet
stat-view-servlet:
# 登录名
login-username: admin
# 登录密码
login-password: admin
url-pattern: /druid/*
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 192.167.10.1,127.0.0.1 # 本机和设置的ip可以访问后台管理界面
reset-enable: false # 不要重置
# 启用控制台-后台管理界面,必须启用,否则访问界面会404
enabled: true
~~~
FreeMaker
1. xml
~~~xml
org.springframework.boot
spring-boot-starter-freemarker
~~~
Freemarker 默认默认视图路径文 resources/templates 目录由自动化配置类FreemarkerProperties 类决定
该目录可以进行在application.yml 中进行修改:修改application.yml 添加freemarker 基本配置如下:
2. yml
```yml
spring:
freemarker:
suffix: .ftl # 修改后缀
content-type: text/html # 此为默认类型,不设置也没有关系
charset: UTF-8
template-loader-path: classpath:/views/ # 修改存放目录,不修改默认就是templates下
```
springboot 2.2.0版本以后,freemaker模版引擎默认的模版文件后缀名由**ftl**改变为**ftlh**。若使用ftl将无法正常映射与展示。ftlh,使freemarker默认以html内容转码输出,以利于模板输出的安全。
3. 创建Controller
编写FreemarkerController 控制器转发视图
~~~java
@Controller
public class MyController {
@RequestMapping("/test1")
public String test1(HttpServletRequest req){
req.setAttribute("msg","msb");
//返回模板文件名称
return "test1";
}
}
~~~
4. 创建Freemarker模板
在resources/views目录下创建test1.ftl文件
~~~java
hi test1.ftl
${msg}
~~~
logback
Spring Boot默认使用Logback组件作为日志管理(官方推出的)。Logback是由log4j创始人设计的一个开源日志组件。
在Spring Boot项目中我们不需要额外的添加Logback的依赖,因为在spring-boot-starter或者spring-boot-starter-web中已经包含了Logback的依赖。
使用案例:打印sql相关日志。在配置文件application.yml中设置:
```yml
# 不改变日志类型和日志规则 只开启mybatis中对应SQL的日志
logging:
level:
com.msb.mapper: trace
```
Thymeleaf
1. 在pom.xml中添加Thymeleaf启动器
~~~xml
org.springframework.boot
spring-boot-starter-thymeleaf
~~~
1. 在`resources/template`s文件夹下新建html页面。
```html
Title
hi test01.html
```
1. 新建控制器
此处方法返回值为页面名称。
~~~java
package com.msb.controller;
@Controller
public class MyController {
@RequestMapping("/show")
public String show(){
System.out.println("执行show方法");
return "test01";
}
}
~~~
源码
```java
public class ThymeleafProperties {
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
}
```
PageHelper
在Service中添加入主要的分页逻辑.
SpringBoot整合PageHelper完成分页,可直接利用上面整合代码进行测试。
PageHelper是一个Mybatis分页插件,官网:https://pagehelper.github.io/
现在SpringBoot整合PageHelper完成分页使用非常简单的(以前搞依赖,配置拦截器)
1. 先在pom.xml中导入启动器:**(SpringBoot3 必须整合 1.4.6以上版本,否则不好使)**
```xml
com.github.pagehelper
pagehelper-spring-boot-starter
1.4.6
```
1. 编写mapper层
接口:
```java
public interface UserMapper {
List selectAllUsers();
}
```
mapper.xml:
```xml
```
1. 编写业务层
接口:
```java
public interface UserService {
/**
*
* @param pageNum 页码
* @param pageSize 每页数据大小
* @return
*/
PageInfo findAllUsers(int pageNum, int pageSize);
}
```
实现类:
```java
@Service
public class UserServiceImpl implements UserService {
@Override
public PageInfo findAllUsers(int pageNum, int pageSize) {
PageHelper.startPage(pageNum,pageSize);
List users = userMapper.selectAllUsers();
// PageInfo是分页查询所有查询结果封装的类,所有的结果都从这个类取
// 使用PageInfo包装查询后的结果,只需要将PageInfo交给页面就行。
PageInfo info = new PageInfo<>(users);
return info;
}
}
```
4. 编写控制层
```java
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/list")
@ResponseBody
public PageInfo selectAll(int pageNum,int pageSize){
return userService.findAllUsers(pageNum,pageSize);
}
}
```
SpringDoc
SpringDoc是一个在Spring框架中用于生成API文档的工具,类似 Swagger
```xml
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.1.0
```
```java
@Configuration
public class SpringDocConfig {
@Bean
public OpenAPI restfulOpenAPI() {
Info info = new Info().title("SpringBoot项目")
.description("用户管理项目")
.version("1.0.0");
return new OpenAPI()
.info(info);
}
}
```
DevTools热部署
热部署原理
项目中修改代码后重启的目的:其实就是重新编译生成了新的 Class 文件,这个文件里记录着和代码等对应的各种信息,然后 Class 文件将被虚拟机的 ClassLoader 加载。
热部署在原理上是使用了两个 ClassLoader,一个 ClassLoader 加载那些不会改变的类(第三方 Jar 包),另一个ClassLoader 加载会更改的类,称为 restart ClassLoader。它监听到如果有 Class 文件改动了,原来的 restart ClassLoader 被丢弃,重新创建一个 restart ClassLoader,由于需要加载的类相比较少,不需要人工重启服务器,所以实现了较快的重启时间。
在Spring Boot项目中 通过配置 DevTools 工具(开发者工具)来达到热部署效果。
配置 DevTools 环境
```xml
org.springframework.boot
spring-boot-devtools
runtime
true
```
devtools 可以实现**页面热部署**(即页面修改后会立即生效,这个可以直接在 application.properties 文件中配置 spring.thymeleaf.cache=false 来实现),实现**类文件热部署**(类文件修改后不会立即生效),实现对**属性文件的热部署**。即 devtools 会监听 classpath 下的文件变动,并且会立即重启应用(发生在保存时机),注意:因为其采用的虚拟机机制,该项重启是很快的。配置了后在修改 java 文件后也就支持了热启动,不过这种方式是属于项目重启(速度比较快的项目重启),会清空 session 中的值,也就是如果有用户登陆的话,项目重启后需要重新登陆。
默认情况下,/META-INF/maven,/META-INF/resources,/resources,/static,/templates,/public 这些文件夹下的文件修改不会使应用重启,但是会重新加载( devtools 内嵌了一个 LiveReload server,当资源发生改变时,浏览器刷新)
在 application.yml 中配置 spring.devtools.restart.enabled=false,此时 restart 类加载器还会初始化,但不会监视文件更新。
```yml
spring:
## 热部署配置
devtools:
restart:
enabled: true # 开启热部署
# 设置热启动路径,这个路径下的内容修改后不需要重启,进行热部署。
additional-paths: src/main/java
# 设置的内容为不让热部署去管理,不需要加载 (其实指的就是编译过的文件,因为热部署只管理变化的内容,编译过的没有变化的内容不加载)
exclude: WEB-INF/**
```
IDEA 配置
当我们修改了 Java 类后,IDEA 默认是不自动编译的,而 spring-boot-devtools 又是监测 classpath 下的文件发生变化才会重启应用,所以需要设置 IDEA 的自动编译。
自动编译配置
File -> Settings -> Compiler -> Build Project automatically 高级设置- 属性修改-允许auto-make 允许程序在运行的时候自动编译
关闭浏览器缓存
不关闭缓存,浏览器可能还用上次的界面,不是新的内容,找到浏览器,开发者工具(f12)-网络-禁用缓存:
数据校验validation
```xml
org.springframework.boot
spring-boot-starter-validation
```
mybatis plus
```xml
com.baomidou
mybatis-plus-boot-starter
3.5.3.1
```
1. 构建实体类
```java
@NoArgsConstructor
@AllArgsConstructor
@Data
@TableName("t_user") // 对应的数据库中表为t_user,如果数据库中表和实体类名字一致,可以不指定
public class User {
@TableField(exist = false) // 如果数据库表中没有这个字段,这个字段与数据库表字段对不上,加了这个属性,就不会报错
private Integer a;
@TableField("uid") // 指定数据库表中字段名字,如果数据库表字段和属性名字一致,可以不指定
private Integer id;
private String uname;
private String pwd;
private String realname;
private Integer identity;
}
```
1. 构建mapper层
mapper接口:
```java
/**
* 继承BaseMapper接口,该接口中定义了很多基础的增删改查的方法,
* 所以UserMapper中就不需要定义基础的增删改查方法了
*/
public interface UserMapper extends BaseMapper {
}
```
mapper.xml可以不写了
1. 构建service层
service接口:
```java
/**
* 继承IService接口,该接口中定义了很多基础的增删改查的方法,
* * 所以UserService中就不需要定义基础的增删改查方法了
*/
public interface UserService extends IService {
}
```
service实现类:
```java
/**
* 如果用UserServiceImpl 直接implements UserService会报错
* 因为要实现全部的抽象方法
* 那还不如不继承了,太麻烦了呀
* mybatis-plus同样给出了解决方案,让UserServiceImpl继承ServiceImpl
* ServiceImpl有两个泛型,其中一个代表你要注入的mapper,另一个代表你这个实现类中操作的那个表对应的实体类
* 这样UserServiceImpl中一些基本的方法就不用去写了,除非一些特殊的逻辑底层没提供那你就自己加入
*/
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
}
```
1. 启动类
启动类加入mapper层扫描:
```java
@SpringBootApplication
public class SpringBootM12Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootM12Application.class, args);
}
}
```
1. 测试
利用单元测试进行测试
```java
@SpringBootTest
class SpringBootM12ApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
// 查询全部USer:
List list = userService.list();
for (User user : list) {
System.out.println(user);
}
}
}
```
1. 测试增删改查
```java
@SpringBootTest
class SpringBootM12ApplicationTests {
@Autowired
private UserService userService;
@Test
void test01() {
// 查询全部USer:
List list = userService.list();
for (User user : list) {
System.out.println(user);
}
}
@Test
void test02() {
// 带条件的查询:
// 查询uid>=5的数据
// QueryWrapper 作用就是在原本的sql上拼接where条件
// 适用场合:查询、删除、更新
QueryWrapper q = new QueryWrapper<>();
q.ge("uid",5);// 条件指定参照使用手册
List list = userService.list(q); // 将条件传入
for (User user : list) {
System.out.println(user);
}
}
@Test
void test03() {
// 带条件的查询:
// 查询uid>=5,uname=fh的数据
QueryWrapper q = new QueryWrapper<>();
q.ge("uid", 5).eq("uname","fh");// 多条件追加调用即可
List list = userService.list(q); // 将条件传入
for (User user : list) {
System.out.println(user);
}
}
@Test
void test04() {
// 查询单个数据:
QueryWrapper q = new QueryWrapper<>();
q.eq("uname","fh");
User one = userService.getOne(q);
System.out.println(one);
}
@Test
void test05() {
// 增加数据:单个增加用save方法,多个用saveBatch方法
boolean save = userService.save(new User(1, 111, "a", "a", "a", 1));
System.out.println(save);
}
@Test
void test06() {
// 更新数据:
QueryWrapper q = new QueryWrapper<>();
q.eq("uid","111");
User u = new User(1, 111, "bbb", "bbb", "bb", 2);
boolean update = userService.update(u, q);//第一个参数为更新的字段,第二个参数为更新的where条件
System.out.println(update);
}
@Test
void test07() {
// 删除数据:
QueryWrapper q = new QueryWrapper<>();
q.eq("uid","111");
boolean remove = userService.remove(q);//第一个参数为更新的字段,第二个参数为更新的where条件
System.out.println(remove);
}
}
```
1. 分页操作
mybatis-plus自带分页插件,不用像之前一样整合`PageHelper`了。
(1)想要分页插件生效,先进行配置分页插件,配置分页拦截器:
可以修改为如下,可修改、也可以使用官网的
```java
@Configuration
public class PageConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor p = new PaginationInnerInterceptor();
p.setOverflow(false);// 设置是否轮回,就是查到最后一条的时候是否需要轮回到第一条
p.setMaxLimit(500L);// 单页最大数量500
p.setDbType(DbType.MYSQL);// 设置数据库类型
interceptor.addInnerInterceptor(p);
return interceptor;
}
}
```
(2)测试方法中:
```java
@SpringBootTest
class SpringBootM12ApplicationTests {
@Autowired
private UserService userService;
@Test
public void testPage(){
// 分页查询条件:当前页 页大小
// 带条件的分页加上QueryWrapper
QueryWrapper queryWrapper=new QueryWrapper<>();
queryWrapper.lt("uid", "5");
Page page = userService.page(new Page<>(1, 3), queryWrapper);
// 分页返回数据:当前页数据 总页数 总记录数 当前页 页大小 ... ..
List list = page.getRecords();
list.forEach(System.out::println);
System.out.println("总页数:"+page.getPages());
System.out.println("总记录数:"+page.getTotal());
System.out.println("当前页:"+page.getCurrent());
System.out.println("页大小:"+page.getSize());
}
}
```
## 事务支持
直接使用注解配置即可,无需xml配置内容。
```java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int deleteUserById(Integer uid) {
int num = userMapper.delete(uid);
int a = 1/0;
return num;
}
}
```
启动服务器,测试:
发现,数据库数据被删除,但是后台报错:
所以必须要加入事务的控制:
```java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation= Propagation.REQUIRED) //added
public int deleteUserById(Integer uid) {
int num = userMapper.delete(uid);
int a = 1/0;
return num;
}
}
```
启动服务器,测试,发现程序报错,数据库数据没有被删除,证明事务加入成功,非常简单的操作!在service层每个增删改方法前都加入@Transactional(propagation = Propagation.REQUIRED)注解即可。
## 异常控制
`template/error/error.html`
如果你整合的是thymeleaf就创建error.html,如果你整合的是freemarker你就创建error.ftl,SpringBoot会自动帮你跳转到异常页
面。
`template/error/404.html`
如果你想不同的异常跳转到不同的页面,可以做如下处理:页面名字设为即将出现错误的状态码
局部异常处理 @ExceptionHandler
这种处理方式属于局部处理方式,只对当前MyController控制单元中的异常生效
```java
@Controller
public class MyController {
@RequestMapping("/test1")
@ResponseBody
public String test01(){
System.out.println("test1---");
int a = 1/0;
return "test01";
}
// 出现算术异常,跳转到如下方法解决:
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public String myexceptionhandler(){
System.out.println("异常处理逻辑代码。。");
return "myerror";
}
}
```
实现全局异常处理 @ControllerAdvice、@ExceptionHandler
```java
@ControllerAdvice // 代表当前类为异常处理类
public class GlobalExceptionHandler {
// 出现算术异常,跳转到如下方法解决:
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public String myexceptionhandler(){
System.out.println("异常处理逻辑代码。。");
return "myerror";
}
}
```
配置方式处理
```java
public class GlobalExceptionHandler3 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView m = new ModelAndView();
if (ex instanceof ArithmeticException){
m.setViewName("myerror");
}
return m;
}
}
```
## 拦截器
首先定义拦截器,这个与SpringMVC中的学习是一致的,在com.msb.interceptors中定义拦截器:
注意需要加入@Component注解,代表使用容器管理拦截器对象
```java
@Component
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行拦截器");
return true;
}
// 省略postHandle、afterCompletion方法,与springmvc中定义一致
}
```
定义好拦截器以后,配置拦截器,在springmvc中是利用xml对拦截器配置进行注册,但是springboot中没有xml配置,怎么处理呢?使用配置类:
```java
@Configuration // 类上有注解@Configuration,此类相当于SpringMVC配置文件。
public class MyConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
//配置拦截器的映射
@Override
public void addInterceptors(InterceptorRegistry registry) {
/*InterceptorRegistration ir = registry.addInterceptor(demoInterceptor);
InterceptorRegistration ir2 = ir.addPathPatterns("/**"); // 设置拦截路径
InterceptorRegistration ir3 = ir2.excludePathPatterns("/login"); // 设置不拦截url*/
// 链式调用
registry.addInterceptor(demoInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
}
```
registry.addInterceptor(demoInterceptor) —— 代表注册拦截器
addPathPattern() —— 设置拦截路径,拦截哪些URL,/** 拦截全部
excludePathPatterns() —— 不拦截哪些URL
PS:当excludePathPatterns()和addPathPattern()冲突时,excludePathPatterns()生效。
如下:registry.addInterceptor(demoInterceptor).addPathPatterns("/login").excludePathPatterns("/login");
## Mybatis
注解开发
MyBatis提供了多种注解来简化SQL映射的配置,常用的注解包括:
```
@Select: 用于执行查询操作。
@Insert: 用于执行插入操作。
@Update: 用于执行更新操作。
@Delete: 用于执行删除操作。
@Results: 用于配置结果集的映射。
@Result: 用于配置单个结果映射。
@Param: 用于传递参数。
```
```java
import org.apache.ibatis.annotations.*;
@Mapper
public interface UserMapper {
// 查询用户列表
@Select("SELECT * FROM user")
@Results({
@Result(column="id", property="id"),
@Result(column="username", property="username"),
@Result(column="password", property="password")
})
List selectAll();
// 根据ID查询用户
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(@Param("id") Integer id);
// 插入用户
@Insert("INSERT INTO user(username, password) VALUES(#{username}, #{password})")
int insertUser(@Param("username") String username, @Param("password") String password);
// 更新用户
@Update("UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id}")
int updateUser(@Param("id") Integer id, @Param("username") String username, @Param("password") String password);
// 删除用户
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteUser(@Param("id") Integer id);
}
```
xml文件开发
```xml
```
xml文件放在resources目录下,并且配置路径
```yml
mybatis:
mapper-locations: classpath:mapper/*.xml
```
设置实体类包别名
~~~yaml
mybatis:
type-aliases-package: com.msb.pojo #给指定包中的所有类起别名
~~~
resultType中改为别名即可:
```xml