初始化

  1. 数据库
  2. maven 依赖
  3. 项目架构

开发流程

明确需求

接口文档

思路分析

接口开发

接口调试

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=100MB

OSS对象存储

  1. 阿里云控制台创建 bucket
  2. 添加 maven 依赖
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.17.4</version>
</dependency>
  1. 创建 AccessKey 和 Secret 并添加到环境变量中
  2. 测试连接
  3. 创建工具类 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注解

通知顺序:

  1. 按照切面类的字典序
  2. 制定注解@Order(优先级数字) 切入点表达式:
  3. execution(访问修饰符? 返回值 包名.类名.?方法名(参数) throws 异常) 通配符:*匹配一个 ..匹配多个
  4. annotation(注解) 匹配使用了某个注解的方法

连接点:

  1. ProceedingJoinPoint
  2. JoinPoint 常用的方法: 获取类名:getTarget().getClass().getName() 获取方法名:getSignature() 获取传入参数:getArgs() 放行执行:proceed()