初始化
- 数据库
- maven 依赖
- 项目架构
开发流程
明确需求
接口文档
思路分析
接口开发
接口调试
postman测试
前后端联调
日志
org.slf4j.Logger
加入注解 @slf4j 避免使用 private static final Logger log = LoggerFactory.getLogger(DeptContorller.class);
RestController 可以把对象转为 json
@GetMapping 指定接口请求的方式
一个完整的请求路径,应该是类上的@RequestMapping的value属性+方法上的@RequestMapping的value属性。
请求参数默认值注解 `@RequestParam(defaultValue = “1”)
上传文件
前端页面展示
<form action="/upload" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="username"><br>
年龄:<input type="text" name="age"><br>
头像:<input type="file" name="image"><br>
<input type="submit" value="提交">
</form>后端响应并储存文件到指定目录
@RestController
public class UploadController {
@RequestMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("上传文件:{}, {}, {}", username, age, image);
image.transferTo(new File("D:\\upload\\" + image.getOriginalFilename()));
return Result.success();
}
}MultipartFile 接口常用方法
String getOriginalFilename();//获取原始文件名
void transferTo (File dest);//将接收的文件转存到磁盘文件中
long getSize();//获取文件的大小,单位:字节
byte[] getBytes();//获取文件内容的字节数组
InputStreamgetlnputStream();//获取收到的文件内容的输入流使用uuid(通用唯一识别码)生成文件名
String uuid = UUID.randomUUID().toString();
String extension = image.getOriginalFilename().substring(image.getOriginalFilename().lastIndexOf("."));
image.transferTo(new File("D:\\upload\\" + uuid + extension));修改最大文件限制:在 application.properties 中添加配置
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MBOSS对象存储
- 阿里云控制台创建 bucket
- 添加 maven 依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>- 创建 AccessKey 和 Secret 并添加到环境变量中
- 测试连接
- 创建工具类 AliOSSUtils 使用 bean 注入
@Component
public class AliOSSUtils {
private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
private String accessKeyId = "LTAI5tSp9ND46ZFVoGfiXStX";
private String acccessKeySecret = "ll2sDOg7Ljut7hLtzWDVZ7Ga2Bq5qG";
private String bucketName = "tanphoon-tlias";
public String upload(MultipartFile file) throws IOException, ClientException {
// 获取文件输入流
InputStream inputStream = file.getInputStream();
// 创建文件名
String originalFilename = file.getOriginalFilename();
String objectName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.indexOf("."));
System.out.println(objectName);
// 上传到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, acccessKeySecret);
ossClient.putObject(bucketName, objectName, inputStream);
// 访问链接
String url = "https://" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
// 关闭 OSS
ossClient.shutdown();
return url;
}
}配置文件
变量前注入
在 appliication.properties 中添加配置
aliyun.oss.endpoint=https://oss-cn-beijing.aliyuncs.com使用注解 @Value("${aliyun.oss.endpoin}") 在变量前注入
创建配置 bean 类注入
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String acccessKeySecret;
private String bucketName;
}登录校验
统一拦截:过滤器 Filter、拦截器 Interceptor
会话技术
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术 JWT
跨域:协议、IP/域名、端口号
JWT
全称:JSON Web Token(https://jwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
组成:
第一部分:Header(头),记录令牌类型、签名算法等。例如:{“alg”:“HS256”,“type”:“JWT”}
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{“id”:“1”,“username”:“Tom”}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
过滤器(Filter)
概念:Filter过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
使用:
定义类:实现Filter接口
配置:@WebFilter(urlPatterns="/*") @ServletComponentScan
过滤器链:配置多个过滤器,运行时会一个一个过滤
顺序:按照类名(字符串字典序)
Interceptor(拦截器)
使用:
定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。
注册到 com.example.config.WebConfig 配置类
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
}拦截规则:
/** 任意级路径
/* 一级路径
全局异常
事务
注解:@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
位置:业务(service)层的方法上、类上、接口上
作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务
添加日志输出
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug事务的传播
- REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
AOP 面向切面编程
AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程。
场景:
案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时
实现:
动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
使用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>定义切面类:
package com.example.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.service.*.*(..))")
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
Long begin = System.currentTimeMillis();
Object result = joinPoint.proceed();
Long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "execute time {} ms", end - begin);
return result;
}
}通知类型:
@Around:需要手动调用 ProceedingJoinPoint.proceed 和 添加返回值
@Before
@After
@AfterReturning
@AfterThrowing
切入点表达式抽取为方法:使用@Pointcut注解
通知顺序:
- 按照切面类的字典序
- 制定注解@Order(优先级数字) 切入点表达式:
- execution(访问修饰符? 返回值 包名.类名.?方法名(参数) throws 异常) 通配符:
*匹配一个..匹配多个 - annotation(注解) 匹配使用了某个注解的方法
连接点:
- ProceedingJoinPoint
- JoinPoint
常用的方法:
获取类名:
getTarget().getClass().getName()获取方法名:getSignature()获取传入参数:getArgs()放行执行:proceed()