定制设计自定义注解结合之实战应用
背景介绍
定制设计最近在项目中写了一个定制设计公共的上传文件接口,定制设计项目中有多个业务场景定制设计会使用到上传文件,定制设计每个场景对上传的文件类型,定制设计文件大小有不同的要求。
按常规操作,我们可以在Controller层提供多个接口,然后在每个接口里写if去校验;或者是在一个接口里定义类型去区分不同的业务场景,再分别写if去校验;总而言之,就是要写if去校验。
然后呢,我就不想写if校验,觉得重复代码太多,不够优雅。于是考虑能否通过类似@RequestParam这样的注解,入参上加上一个简单注解就能实现校验。
好了,废话不多说,开始干吧。
步骤流程
首先贴一下项目目录结构
1. 定义注解
如果小伙伴们对如何自定义注解存在疑惑的话,请先阅读这篇文章
- 定义用于参数上的注解
package com.example.demo.aop.annotation;import java.lang.annotation.*;/** * @author Dong */@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FileParam { /** * 文件后缀 */ public String[] suffix() default {"doc", "xls", "ppt", "png", "txt"}; /** * 文件大小 */ public int size() default 1024;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 定义用于方法上的注解
package com.example.demo.aop.annotation;import java.lang.annotation.*;/** * @author Dong */@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FileValid {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2.将注解应用于方法和参数
package com.example.demo.controller;import com.example.demo.aop.annotation.FileParam;import com.example.demo.aop.annotation.FileValid;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @author Dong */@RestController@RequestMapping("/file")@Slf4jpublic class AopTestController { @PostMapping("/upload") @FileValid public String upload(@FileParam(suffix = {"doc"}) MultipartFile file, HttpServletRequest request, HttpServletResponse response) { log.info("in the method ..."); return "success"; }}
- 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
到这里之后,你运行项目会发现,并没有什么卵用啊,是不是很气,气就对了
所以你应该认识到,如果只是有注解,对项目其实是不起任何作用的,因为它只是相当于一个标记,真正要让它起作用,那就得写一个能识别这些注解,并且在识别到这些注解之后能做出一系列操作的处理器。这就是Java里面强大的反射,或者是Spring的AOP。
如果小伙伴们对AOP不是很了解的话,可以参考这篇文章
3.定义切面
package com.example.demo.aop.aspect;import com.example.demo.aop.annotation.FileParam;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.util.Arrays;/** * @author Dong */@Component@Aspect@Slf4jpublic class FileValidAspect { @Pointcut("@annotation(com.example.demo.aop.annotation.FileValid)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint pjp) { // 参数值 Object[] args = pjp.getArgs(); // 方法 Method method = ((MethodSignature) pjp.getSignature()).getMethod();// String name = pjp.getSignature().getName();// Method method1 = null;// try {// method1 = pjp.getTarget().getClass().getMethod(name, MultipartFile.class, HttpServletRequest.class, HttpServletResponse.class);// } catch (NoSuchMethodException e) {// e.printStackTrace();// } // 参数列表 Parameter[] mParameters = method.getParameters(); for (int i = 0; i < mParameters.length; i++) { // 判断参数上是否修饰了注解 if (mParameters[i].isAnnotationPresent(FileParam.class)) { // 获取注解进而得到注解上的参数值 Annotation annotation = mParameters[i].getAnnotation(FileParam.class); String[] suffixs = ((FileParam) annotation).suffix(); int size = ((FileParam) annotation).size(); log.info("suffixs: {}, size: {}", suffixs, size); // 实际文件大小 long rSize = 0L; // 实际文件后缀 String suffix = null; if (args[i] instanceof MultipartFile) { MultipartFile temp = ((MultipartFile) args[i]); rSize = temp.getSize(); suffix = temp.getOriginalFilename().split("\\.")[1]; log.info("suffix: {}, size: {}", suffix, rSize); } if (rSize > size) { return String.format("文件大小:%sByte,超过限定大小:%sByte", rSize, size); } if (!Arrays.asList(suffixs).contains(suffix)) { return String.format("不支持文件上传类型:%s", suffix); } } } try { return pjp.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } return "error"; } @Before("pointcut()") public void before() { log.info("before ..."); } @AfterReturning("pointcut()") public void afterReturning() { log.info("afterReturning ..."); } @After("pointcut()") public void after() { log.info("after ..."); } @AfterThrowing("pointcut()") public void afterThrowing() { log.info("afterThrowing ..."); }}
- 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
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
4.测试结果
到此呢,我们整个实战的代码就撸完了,下面跑项目看结果
致谢
最后感谢几位大佬的美文:
1.
2.
3.