一、Restful
package com.example.crudtest1.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应码,1 代表成功; 0 代表失败
private String msg; //响应信息 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}
二、日志输出
项目开发中尽量避免使用sout输出,使用日志输出
private static Logger log = LoggerFactory.getLogger(DeptController.class);
//或者使用注解
@Slf4j
三、分页查询
注意sql中limit的第一个参数即可
例如查询第二页,每页展示10条数据
selcet * from emp limit 10,10
3.1、分页查询插件PageHelper
四、文件上传
4.1、前端
前端,必须提供form表单,提交方式为post 必须要有个input框设置为file
enctype 属性值 如果需要文件上传,必须使用multipart/form-data
4.2、后端
后端在spring boot中使用 MultpartFile 且表单项名字前后端一致
4.3、本地存储
前置知识
1、
这是pom.xml配置文件:
Spring Boot 默认的文件上传大小限制是 1MB。如果上传文件过大,可以自行配置:
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大大小的限制(一次请求中是可以上传多个文件)
spring.servlet.multipart.max-request-size=10MB
2、由于上传的图片可能会出现同名问题,我们需要给一个唯一的标识,这里推荐使用UUID
,拼接原始的上传文件名
Controller层
/*
* 文件上传
* */
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws Exception {
log.info("文件上传,{},{},{}",username,age,image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//构造唯一的文件名(不能重复)--uuid(通用唯一识别码) a758f47d-8807-4d50-af22-5b4b3208f193
int index = originalFilename.lastIndexOf(".");
String extname = originalFilename.substring(index);
String newFileName = UUID.randomUUID().toString()+extname;
log.info("新的文件名:{}", newFileName);
//将文件存储在服务器磁盘下
image.transferTo(new File("G:\\笔记\\Java\\project\\" +
"springboot-mybatis-crudTest\\crudTest1\\image\\"+newFileName));
return Result.success();
}
4.4、阿里云OSS存储
流程
- 登录阿里云->控制台->oss基础服务
- 点击Bucket列表->创建bucket->输入名称,创建
- 获取密钥,点击头像AccessKey管理-创建一个账号
- 回到oss服务,拉到最下面找到SDK下载,就可以看到官方文档
https://help.aliyun.com/zh/oss/developer-reference/overview-21
引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
//java9以上
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
引入工具类
package com.example.crudtest1.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;
/**
* 阿里云 OSS 工具类
*/
public class AliOSSUtils {
private String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
private String accessKeyId = "LTAI4GCH1vX6DKqJWxd6nEuW";
private String accessKeySecret = "yBshYweHOpqDuhCArrVHwIiBKpyqSL";
private String bucketName = "web-tlias";
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
五、配置文件
优化阿里云配置文件,利用@ConfigurationProperties(prefix = “aliyun.oss”)
这里的属性必须要与yml配置文件里面同名
在AliOSSUtils中利用注解注入
package com.example.crudtest1.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;
/**
* 阿里云 OSS 工具类
*/
@Component
public class AliOSSUtils {
@Autowired
private AliOSSProperties aliOSSProperties;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
String endpoint = aliOSSProperties.getEndpoint();
String accessKeyId = aliOSSProperties.getAccessKeyId();
String accessKeySecret = aliOSSProperties.getAccessKeySecret();
String bucketName = aliOSSProperties.getBucketName();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
5.1、yml配置文件
基本语法
- 大小写敏感
- 数值前边必须有空格,作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- #表示注释,从这个字符一直到行尾,都会被解析器忽略
server:
port: 9000
#定义对象/map集合
user:
name:Tom
age:18
#定义Liset/set集合
hobby:
-java
-java2
spring:
#数据库连接
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tlias
username:
password:
#文件上传
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
#mybatis
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
#阿里云OSS
aliyun:
oss:
endpoint : "";
accessKeyId : "";
accessKeySecret : "";
bucketName : "";
六、登录校验
会话技术
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪
一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
6.1、JWT令牌
由三个部分组成,由Base64进行编码
- header(头)
- Payload(有效载荷)
- Signature(签名)
在登录成功后,生成令牌,后续每个请求都需要携带令牌,系统在处理请求时,需要先校验令牌
6.1.1、依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
工具类
package com.example.crudtest1.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "zyyy";
//设置生成时间为12小时
private static Long expire = 43200000L;
/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
七、过滤与拦截
过滤器链,由多个过滤器组成,由类文件名A-Z执行
7.2、拦截器
定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
package com.example.crudtest1.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法前运行, 返回true 放行,返回false 不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("目标资源方法前运行");
// return HandlerInterceptor.super.preHandle(request, response, handler);
return true;
}
@Override //目标资源方法后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
System.out.println("目标资源方法后运行");
}
@Override //视图渲染完后执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
System.out.println("视图渲染完后执行");
}
}
注册拦截器
package com.example.crudtest1.config;
import com.example.crudtest1.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//要拦截哪些资源 addPathPatterns
//excludePathPatterns 放行
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");;
}
}
执行流程
八、异常处理
全局异常处理器 RestControllerAdvice
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)//捕获全部异常
public Result ex(Exception e){
e.printStackTrace();
return Result.error("操作失败,请联系管理员");
}
}
九、事务管理
事务:一组操作集合,不可分割,要么同时成功,要么失败
注解@Transactional
注意 默认情况下只会出现RuntimeException才回滚异常
可以设置rollbackFor 来指定出现何种异常回滚
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
9.1、传播行为
事务传播行为:指的是一个事务方法被另一个事务调用
10、AOP
AOP:面向切面编程,就是面向特定的方法编程
通过底层的动态代理机制,对特定的方法进行编程
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
获取每个方法的运行时长
package com.example.crudtest1.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@Aspect
public class TimeAspect {
@Around("execution(* com.example.crudtest1.service..*.*(..))")
public Object recordTime(ProceedingJoinPoint joinPoint ) throws Throwable {
//1.记录开始时间
long begin = System.currentTimeMillis();
//调用原始方法运行
Object result = joinPoint.proceed();
//记录结束时间,计算方法执行耗时
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature()+ "方法耗时,{}",end-begin);
return result;
}
}
10.1、核心概念
- 连接点:JoinPoint,可以被AOP控制的方法
- 通知:Advice,指重复的逻辑,也就是共性功能
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
这里表现的是只有list这一个功能点运行时,才会被通知
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
- 目标对象:Target,通知所应用的对象
执行流程
10.2、通知类 ![image-20240312104719894](https://i-blog.csdnimg.cn/blog_migrate/0f0b0045cb359eb63981c08cef71323c.png)
@Pointcut 切入点,可以将重复的片段抽取起来统一使用
@Pointcut("execution(* com.example.crudtest1.service.impl.DeptServiceImpl.*(..))")
public void pt(){};
@Before("pt()")
public void before(){
log.info("before ...");
}
不同切面类中,默认按照切面类的类名字母排序:
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行
可以使用@Order(数字)控制顺序
目标方法前的通知方法:数字越小前的先执行
目标方法后的通知方法:数字越小的后执行
10.3、切入点表达式
- execution():根据方法的签名来进行匹配
书写建议
所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是find开头,更新类方法都是update开头。
描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性。
在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用…使用*匹配单个包。
- @annotation():根据注解匹配
10.4、连接点
JoinPoint抽象了连接点,可以用它获得方法执行时的相关信息,如目标类、方法名、方法参数
注意点:
@Around通知,获取连接点信息只能使用ProceedingJoinPoint
其他四种通知,获取连接点信息只能使用JoinPoint
它是ProceedingJoinPoint的父类型
//1、获取目标对象的类名
String className = proceedingJoinPoint.getClass().getName();
log.info("目标对象的类名,{}",className);
//2.获取目标方法的方法名
String methodName = proceedingJoinPoint.getSignature().getName();
log.info("目标对象的类名,{}",methodName);
//3.获取目标运行时传入的参数
Object[] args = proceedingJoinPoint.getArgs();
log.info("目标对象的类名,{}", Arrays.toString(args));
//4.放行 目标方法执行
Object result = proceedingJoinPoint.proceed();
11.springBoot原理
配置优先级问题
第三方Bean对象
- 如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解。
- 若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过@Configuration注解声明一个配置类。
11.1、起步依赖
spring boot 中引入项目依赖不需要那么繁琐,主要靠依赖传递,保证引入项目需要的依赖
11.2、自动配置
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
-
原理实现
- @import
- @EnableXXX注解,封装了@Import(推荐)
-
源码
-
@SpringBootApplication
-
@SpringBootConfiguration
- 和configuration注解作用相同,声明当前也是一个配置类
-
@EnableAutoConfiguration
- 组件扫描,默认扫描当前引导类所在包及其子类
-
@ComponentScan
- 实现自动化配置的核心注解
-
-
条件装配注解
- Conditional注解
- 按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中
- 常用Conditional注解
- @ConditionalOnClass:判断是否有对应的字节码文件
- @ConditionalOnMissingBean:判断环境中没有对应的bean才会注入
- @ConditionalOnProperty:判断配置文件中对应属性和值,才注册bean到IOC容器
11.3、案例(自动starter)
自定义起步依赖-制作阿里云工具类的自动装配
12、maven
12.1、继承与聚合
作用
聚合用于快速构建项目
继承用于简化依赖配置、统一管理依赖
相同点:
聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中聚合与继承均属于设计型模块,并无实际的模块内容
不同点:
聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
12.2、私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
- Release:发行版本,功能趋于稳定,当前停止更新
- snapshot:快照版本,功能不稳定、尚且 处于开发中的版本
步骤
- 配置本地仓库访问私服的权限,修改maven的conf/settings.xml配置文件,指定访问私服的用户名/密码
<server>
<id>releases</id>
<username>admin</username>
#这里是私服设置的密码
<password>123456</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
#这里是私服设置的密码
<password>123456</password>
</server>
- IDEA中配置要上传项目中的发布管理,修改pom.xml,指定上传的位置
<distributionManagement>
<repository>
<id>releases</id>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>