记录了一些在学习SpringBoot和MyBatis时的知识点

SpringBoot

简介

​ Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发成为领导者。

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 校验参数 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>

</project>

启动类

​ springboot通过创建启动类和main函数调用对应的方法启动项目,启动类需要放在软件包的根目录

1
2
3
4
5
6
7
8
9
10
11
package com.forum;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Start {
public static void main(String[] args) {
SpringApplication.run(Start.class, args);
}
}

分层解耦思想

  • 内聚:软件中各个功能模块内部的功能联系。

  • 耦合:软件中各个层/模块之间的依赖,关联程度。

    spring所有功能都是围绕着分层解耦思想来展开,在该思想中遵循着所有代码被分三层架构,其目的在于降低耦合,提高内聚。

controller:控制层

​ 控制层只负责接受请求和返回参数,所有业务逻辑在service层实现,controller通过调用services层方法来实现业务处理。

Service:业务逻辑层

​ 业务逻辑层负责业务的实现,通过调取dao层实现对数据库进行操作并为controller层提供业务处理方法。

Dao:数据访问层

​ 数据访问层负责对数据库进行增删改查等操作,为业务逻辑层处理数据提供操作数据库的方法。

IOC和DI

​ 通常情况下控制层需要创建业务逻辑层对象调用方法来实现业务处理,业务逻辑层通过创建数据访问层对象调用方法来实现数据访问,这种方式增加了各个层之间的依赖程度,当一个层中的方法出现改变,那么另一个层中的调用也需要进行相应修改。spring为了解决这个问题提出了IOC的概念。

​ IOC是一个容器,他通过注解的形式将对应的类对象放入容器中统一管理,其他层使用时只需要声明即可取出对象而不需要再创建对象

注解 功能
@RestController 创建控制层对象交给IOC(控制层类必须有该注解)
@Service 创建业务逻辑层数据对象交给IOC
@Mapper 创建数据控制层对象交给IOC(需要Mybatis)
@@Autowired 从IOC容器中取出该类型对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//控制层
public class UserCotroller {
@Autowired
private UserServiceImpl userServiceImpl;
@PostMapping("adduser")
public Result addUser() {
pass
}
}
//业务逻辑层
@Service
public class UserServiceImpl implements Userservice {
@Autowired
private UserMapper userMapper;
@Transactional
public boolean addUser(User user) {
pass
}
}
//数据控制层
@Mapper
public interface UserMapper {
public void addUser(User user);
}

Controller层

注解 功能
@RequestMapping(“user”) 设置路由,可以在作用在类、方法上
@GetMapping(“#{id}”) 设置路由,只接受GET请求(POST,DELECT,PULL请求同上)
@RequestBody 作用在参数上,可以接收POST请求参数
@PathVariable 作用在参数上,可以接收路由参数
@Validated 校验参数是否符合要求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
@RequestMapping("user")
public class UserCotroller {
@PostMapping("adduser")
public Result addUser(@RequestBody @Validated User user) {}
@GetMapping("#{id}")
public Result vistUser(@PathVariable int id) {}
}

//实体类
@Data
public class User {
@Null //必须为空
private Integer uid;
@NotBlank //不能为空字符串和NULL
@Size(min = 2, max = 20)
private String username;
@NotBlank
@Size(min = 2, max = 20)
private String password;
@NotBlank
@Pattern(regexp="^[男,女]") //必须符合该正则表达式
private String sex;
@NotNull
private Integer role;
private String head;
private LocalDate createTime;
private LocalDate endTime;
}

事务管理

开启事物

通过在方法、类和接口上面添加@Transactional 注解来开启事物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.forum.service;

import com.forum.mapper.UserMapper;
import com.forum.pojo.User;
import com.forum.service.port.Userservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;

@Service
public class UserServiceImpl implements Userservice {
@Autowired
private UserMapper userMapper;
@Transactional //开启事物
public boolean addUser(User user) {
LocalDate localDate = LocalDate.now();
user.setCreateTime(localDate);
user.setEndTime(localDate);
if (user.getRole() == 0){
userMapper.addUser(user);
return true;
}else {
System.out.println("判断是否为管理员操作---------暂未实现");
return false;
}
}
}

事务属性

属性名 作用
rollbackFor 设置出现何种错误才回滚
propagation 当该事物方法被调用时事物的处理方式
事务处理方式 描述
Propagation.REQUIRED 有则加入调用方法的事务,没有创建方法
Propagation.REQUIRES_NEW 创建新事务
Propagation.SUPPORTS 有则加入,没有则无事物运行
Propagation.NOT_SUPPORTED 不支持事物,调用方法有事务时挂起当前事物
Propagation.MANDATORY 必须有事物,否则抛出异常
Propagation.NEVER 必须没事务,否则抛出异常
1
2
3
4
5
6
7
8
@Transactional
public void a(){
b()
}
@Transactional(Propagation.REQUIRES_NEW)
public void a(){
pass
}

开启事务管理的debug日志

1
logging.level.org.springframework.jdbc.support.JdbcTransactionManager=debug

文件上传

1
2
3
4
5
6
7
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
</form>

<!-- 上传文件必须使用Post请求,input类型需要使用file,表单类型需要使用multipart/form-data -->


1
2
3
4
5
6
7
public Result upload(MultipartFile file){
String fileName = file.getOriginalFilename(); //获取原始文件名
file.transferTo(new File(fileName)); //将文件存到本地

}

// 后端参数类型必须为MultipartFile

设置上传文件大小

1
2
3
4
<!-- 配置单个文件最大上传大小 -->
spring.servlet.multipart.max-file-size=10MB
<!-- 配置单个请求最大上传大小 -->
spring.servlet.multipart.max-request-size=100MB

会话技术

加入依赖

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//旧版
public Result login() {
Map<String,Object> map = new HashMap<>();
map.put("uid",user.getUid());
map.put("username",user.getUsername());
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "SVRIRULNQQ==") //设置加密方式和密钥
.addClaims(map) //设置存入信息
.setExpiration(new Date(System.currentTimeMillis() + 12*1500*1000)) //设置有效时间
.compact();

return Result.SUCCESS("");
}

//新版
public static String generateJwt(User user) {

String token = Jwts.builder()
.claim("uid", user.getUid())
.claim("username", user.getUsername())
.claim("role", user.getRole())
.expiration(new Date(System.currentTimeMillis() + TIME))
.signWith(KEY)
.compact();
return token;
}

解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//旧版
public Result login() {
Claims claims = Jwts.parser().setSigningKey("SVRIRULNQQ==")
.parseClaimsJws("token")
.getBody();
return Result.SUCCESS("");
}

//新版
public static Claims parseJwt(String token) {
return Jwts.parser()
.verifyWith(KEY)
.build()
.parseSignedClaims(token)
.getPayload();
}

过滤器

  1. 定义Filter::定义一个类,实现Filter接口,重写方法
  2. 配置Filter:在Filter类上加@WebFilter注解,配置拦截路径,启动类上加@ServletComponentScan开启Servlet组件支持。

tip : filter过滤器放行后还会回到dofilter方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@WebFilter(value = "/*")
public class DemoFilter implements Filter {

//初始化方法,Web服务器启动时执行
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}

//每次拦截到请求时执行一次
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 放行前执行

filterChain.doFilter(servletRequest, servletResponse); //放行请求

//放行后执行
}

//销毁方法,Web服务器正常关闭时执行
@Override
public void destroy() {
Filter.super.destroy();
}
}

登陆验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebFilter(value = "/*")
public class DemoFilter implements Filter {
//每次拦截到请求时执行一次
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String uri = request.getRequestURI();
if (uri.equals("/login")) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
String token = request.getHeader("token");
if (token == null) {
response.setStatus(401);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

拦截器

  1. 定义一个拦截器类,实现HandlerInterceptor接口,重写方法
  2. 创建配置类,注册拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//拦截器类
@Component
public class DemoInterceptor implements HandlerInterceptor {
//目标方法执行前触发,返回true代表放行,返回false代表不放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//目标方法执行后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
return;
}
//视图渲染完毕后执行(最后执行)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
return;
}
}

//配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor)
.addPathPatterns("/**") //拦截的请求
.excludePathPatterns("/login"); //不拦截的请求
}
}
拦截路径 说明
/* 拦截一级路径(拦截/usr/不拦/usr/bin)
/** 任意级路径
/usr/* 拦截/usr下的一级路径
/usr/** 拦截/usr下的任意级路径

AOP

加入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
概念 说明
连接点:JoinPoint 可以被AOP控制的方法(可以被aop作用的方法)
通知:Advice 所抽出来的共性方法(AOP方法)
切入点:PointCut AOP方法所作用的方法(@Around中匹配的方法
切面:Aspect 通知+注解
目标对象:Target AOP函数所作用的对象

通知类型

类型 说明
@Around 环绕通知,通知方法在目标方法前后都执行
@Before 前置通知,通知方法在目标方法前被执行
@After 后置通知,通知方法在目标方法后被执行,无论异常都通知
@AfterReturning 返回后通知,通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing 异常后通知,通知方法在发生异常后执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Aspect                 //定义为切面类
@Component
public class AopTest {

@Around("execution(* com.sky.controller.admin.*(*))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
// 调用原始方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
return result;
}

}

自定义通知执行顺序

​ 在切面使用@Order注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Order(1)                  //数字越小优先级越高
@Aspect //定义为切面类
@Component
public class AopTest {

@Around("execution(* com.sky.controller.admin.*(*))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
// 调用原始方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
return result;
}

}

使用annotation注解

  1. 创建自定义注解
  2. 在通知方法上使用@@Pointcut(“@annotation(自定义注解路径)”)
  3. 在连接点使用自定义注解注解方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAop {
}
//通知方法
public class AopTest {

@Pointcut("@annotation(com.sky.controller.admin.MyAop)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
// 调用原始方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
return result;
}

}
//需要作用的方法
@MyAop
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
// 调用原始方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
return result;
}

连接点对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Before("execution(* com.sky.controller.admin.*(*))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
// 1. 获取目标类名
String name = joinPoint.getTarget().getClass().getName();

// 2. 获取目标方法签名
Signature signature = joinPoint.getSignature();

// 3. 获取目标方法名
String methodName = signature.getName();

// 4. 获取方法参数
Object[] args = joinPoint.getArgs();

// 5. 获取连接点对象
Object proceed = joinPoint.proceed();
}

Mybatis

简介

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

​ Mybati提供注解开发和xml开发两种开发方式,相比于注解开发,xml开发支持动态sql更加灵活。

pom文件

配置

1
2
3
4
5
6
7
8
9
spring.datasource.url=jdbc:mysql://local/forum
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
# 配置SQL日志打印在控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名映射规则(数据库字段名:create_time -> 实体类属性名:createTime)
mybatis.configuration.map-underscore-to-camel-case=true

注解开发

xml开发