springboot注解Aspect
daodfs1 人气:0目标
本文提供一种自定义注解,来实现业务审批操作的DEMO,不包含审批流程的配置功能。
具体方案是
自定义一个Aspect注解,拦截sevice方法,将拦截的信息持久化,待审批;审批时获取持久化数据,执行目标方法。
实现
POM
<?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 https://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>2.5.8</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.proc</groupId> <artifactId>process-test</artifactId> <version>1.0.0-SNAPSHOT</version> <name>process-test</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <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-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 用于主线程给子线程传递数据,支持线程池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.12.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
一些实体类
CheckedParam
用于包装页面传进来的参数
package com.proc.model; import java.util.List; public class CheckedParam { //业务标记,由页面传入,用于审批时页面根据tagPageJs解析data,渲染到页面,审批管理员可看到审批的内容 private String tagPageJs; //页面传入的原始数据 private List<String> data; public String getTagPageJs() { return tagPageJs; } public void setTagPageJs(String tagPageJs) { this.tagPageJs = tagPageJs; } public List<String> getData() { return data; } public void setData(List<String> data) { this.data = data; } }
ProcessDbModel
拦截的信息包装类,用于持久化数据
package com.proc.model; public class ProcessDbModel { //bean的目标类全限定名 private String targetClassName; //拦截到的service方法名 private String methodName; //页面传入的tagPageJs或Checked注解的tag private String tag; private String description; //拦截到的service入参类型,包含泛型信息 private String paramTypes; //拦截到的service入参值 private String paramArgs; //拦截到的service入参值或页面传入的原始数据 private String data; public String getTargetClassName() { return targetClassName; } public void setTargetClassName(String targetClassName) { this.targetClassName = targetClassName; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public String getTag() { return tag; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public void setTag(String tag) { this.tag = tag; } public String getParamTypes() { return paramTypes; } public void setParamTypes(String paramTypes) { this.paramTypes = paramTypes; } public String getParamArgs() { return paramArgs; } public void setParamArgs(String paramArgs) { this.paramArgs = paramArgs; } public String getData() { return data; } public void setData(String data) { this.data = data; } @Override public String toString() { return "ProcessDbModel [targetClassName=" + targetClassName + ", methodName=" + methodName + ", tag=" + tag + ", description=" + description + ", paramTypes=" + paramTypes + ", paramArgs=" + paramArgs + ", data=" + data + "]"; } }
测试用的入参对象
package com.proc.model; import java.math.BigDecimal; public class Score { private BigDecimal langue; private BigDecimal math; private BigDecimal english; public BigDecimal getLangue() { return langue; } public void setLangue(BigDecimal langue) { this.langue = langue; } public BigDecimal getMath() { return math; } public void setMath(BigDecimal math) { this.math = math; } public BigDecimal getEnglish() { return english; } public void setEnglish(BigDecimal english) { this.english = english; } @Override public String toString() { return "Score [langue=" + langue + ", math=" + math + ", english=" + english + "]"; } }
package com.proc.model; import java.util.List; public class Person<T> { private String name; private String age; private String sex; private String testName; private String salary; private String work; private List<T> grades; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getSalary() { return salary; } public void setSalary(String salary) { this.salary = salary; } public String getTestName() { return testName; } public void setTestName(String testName) { this.testName = testName; } public String getWork() { return work; } public void setWork(String work) { this.work = work; } public List<T> getGrades() { return grades; } public void setGrades(List<T> grades) { this.grades = grades; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", testName=" + testName + ", salary=" + salary + ", work=" + work + ", grades=" + grades + "]"; } }
一些工具类
JacksonCanonicalUtil
package com.proc.util; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.json.JsonMapper; public class JacksonCanonicalUtil { private static final JsonMapper MAPPER = new JsonMapper(); private JacksonCanonicalUtil () {} public static <T> String toCanonical (Class<T> clazz) { return MAPPER.getTypeFactory().constructType(clazz).toCanonical(); } public static <T> String toCanonical (TypeReference<T> tr) { return MAPPER.getTypeFactory().constructType(tr).toCanonical(); } //反序列化时从持久数据中获取JavaType public static JavaType constructFromCanonical (String canonical) { return MAPPER.getTypeFactory().constructFromCanonical(canonical); } }
StringZipUtil
用于压缩和解压字符串,减少持久数据占用空间
package com.proc.util; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterOutputStream; public class StringZipUtil { private StringZipUtil () {} public static String zipBase64(String text) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { try (OutputStream os = new DeflaterOutputStream(out)) { os.write(text.getBytes(StandardCharsets.UTF_8)); } return Base64.getEncoder().encodeToString(out.toByteArray()); } catch (Exception e) { throw new RuntimeException("压缩字符串出错", e); } } public static String unzipBase64(String text) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { try (OutputStream os = new InflaterOutputStream(out)) { os.write(Base64.getDecoder().decode(text)); } return new String(out.toByteArray(), StandardCharsets.UTF_8); } catch (Exception e) { throw new RuntimeException("解压字符串出错", e); } } }
Base64Util
一些参数值转为Base64后持久化
package com.proc.util; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.stream.Collectors; import com.fasterxml.jackson.databind.json.JsonMapper; public class Base64Util { private Base64Util () {} private static final JsonMapper MAPPER = new JsonMapper(); public static String[] toStrings (Object[] objs) { List<String> list = new ArrayList<>(); try { for (Object obj : objs) { list.add(MAPPER.writeValueAsString(obj)); } } catch (Exception e) { throw new RuntimeException("序列化对象出错", e); } return list.toArray(new String[0]); } public static String encode (String[] strs) { List<String> list = new ArrayList<>(); for (String str : strs) { list.add(Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8))); } String join = list.stream().collect(Collectors.joining("|")); return join; } public static String[] decode (String text) { String[] strs = text.split("\\|", -1); List<String> list = new ArrayList<>(); for (String base64 : strs) { list.add(new String(Base64.getDecoder().decode(base64), StandardCharsets.UTF_8)); } return list.toArray(new String[0]); } public static String encodeZip (Object[] objs) { return encodeZip(toStrings(objs)); } public static String encodeZip (String[] strs) { List<String> list = new ArrayList<>(); for (String str : strs) { list.add(StringZipUtil.zipBase64(str)); } String join = list.stream().collect(Collectors.joining("|")); return StringZipUtil.zipBase64(join); } public static String[] decodeZip (String text) { String str = StringZipUtil.unzipBase64(text); String[] strs = str.split("\\|", -1); List<String> list = new ArrayList<>(); for (String base64 : strs) { list.add(StringZipUtil.unzipBase64(base64)); } return list.toArray(new String[0]); } }
SpringBootBeanUtil
package com.proc.util; import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringBootBeanUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringBootBeanUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } public static Object getBean(String name) { return applicationContext.getBean(name); } public static <T> T getBean(Class<T> clazz) { return (T) applicationContext.getBean(clazz); } public static <T> T getBean(String name, Class<T> clazz) { return applicationContext.getBean(name, clazz); } public static <T> Map<String, T> getBeansOfType(Class<T> clazz) { return applicationContext.getBeansOfType(clazz); } }
ProcessBeanUtil
用于执行目标方法
package com.proc.util; import java.lang.reflect.Method; import org.springframework.util.ReflectionUtils; public class ProcessBeanUtil { private ProcessBeanUtil () {} public static Object excuteBeanMethod (String targetClassName, String methodName, Class<?>[] parameterTypes, Object[] args) { Class<?> targetClass; try { targetClass = Class.forName(targetClassName); } catch (ClassNotFoundException e) { throw new RuntimeException("未找到类", e); } return excuteBeanMethod(targetClass, methodName, parameterTypes, args); } public static Object excuteBeanMethod (Class<?> targetClass, String methodName, Class<?>[] parameterTypes, Object[] args) { Object bean = SpringBootBeanUtil.getBean(targetClass); Method method = ReflectionUtils.findMethod(targetClass, methodName, parameterTypes); return ReflectionUtils.invokeMethod(method, bean, args); } }
CheckedTransmitableUtil
用于传递业务参数
package com.proc.util; import com.alibaba.ttl.TransmittableThreadLocal; import com.proc.model.CheckedParam; public class CheckedTransmitableUtil { private static final TransmittableThreadLocal<CheckedParam> threadLocal = new TransmittableThreadLocal<>(); private CheckedTransmitableUtil () {} public static void set (CheckedParam checkedParam) { threadLocal.set(checkedParam); } public static CheckedParam getAndRemove () { CheckedParam checkedParam = threadLocal.get(); threadLocal.remove(); return checkedParam; } }
PrivateTransmitableUtil
为Aspect判断是否拦截提供依据
package com.proc.util; import com.alibaba.ttl.TransmittableThreadLocal; public class PrivateTransmitableUtil { private static final String CHECKED = "__CHECKED__"; private static final TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>(); private PrivateTransmitableUtil () {} public static void set () { threadLocal.set(CHECKED); } //是否执行的审批程序 public static boolean isCheck () { String checked = threadLocal.get(); threadLocal.remove(); return CHECKED.equals(checked); } }
一些Bean
PostProcess
用于拦截方法后做的个性处理
package com.proc.bean; public interface PostProcess<T> { //返回说明内容,审批时在页面显示 String description(String tag, Class<?>[] parameterTypes, Object[] args); //返回代替的返回值 T retObject(String tag, Class<?>[] parameterTypes, Object[] args); }
TestCheckPostProcess
测试用
package com.proc.bean; import org.springframework.stereotype.Component; @Component public class TestCheckPostProcess implements PostProcess<String> { @Override public String description(String tag, Class<?>[] parameterTypes, Object[] args) { return tag + "测试testCheck"; } @Override public String retObject(String tag, Class<?>[] parameterTypes, Object[] args) { return tag + "返回拦截响应"; } }
Aspect注解
package com.proc.config; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import com.proc.bean.PostProcess; @Retention(RUNTIME) @Target(METHOD) public @interface Checked { String tag() default ""; /** * @see com.proc.util.JacksonCanonicalUtil * @return */ String[] paramCanonical(); Class<? extends PostProcess<?>> postProcess(); }
切面类 CheckedAop
package com.proc.config; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.proc.bean.PostProcess; import com.proc.model.CheckedParam; import com.proc.model.ProcessDbModel; import com.proc.service.ProcessDbService; import com.proc.util.Base64Util; import com.proc.util.CheckedTransmitableUtil; import com.proc.util.PrivateTransmitableUtil; import com.proc.util.SpringBootBeanUtil; @Component @Aspect public class CheckedAop { @Autowired private ProcessDbService processDbService; //拦截Checked注释的方法 @Pointcut("@annotation(com.proc.config.Checked)") public void check() { } @Around(value = "com.proc.config.CheckedAop.check() && @annotation(checked)") public Object around(ProceedingJoinPoint joinPoint, Checked checked) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Class<?>[] parameterTypes = signature.getParameterTypes(); String methodName = signature.getMethod().getName(); Object[] args = joinPoint.getArgs(); if (PrivateTransmitableUtil.isCheck()) { //审批后,执行业务代码 Object returnVal = joinPoint.proceed(); return returnVal; } else { //不是审批操作,拦截 Class<? extends PostProcess<?>> postProcess = checked.postProcess(); PostProcess<?> bean = SpringBootBeanUtil.getBean(postProcess); //组装持久化数据 ProcessDbModel dbModel = new ProcessDbModel(); dbModel.setTargetClassName(joinPoint.getTarget().getClass().getName()); dbModel.setMethodName(methodName); String tag = checked.tag(); CheckedParam checkedParam = CheckedTransmitableUtil.getAndRemove(); if (checkedParam == null || checkedParam.getTagPageJs() == null || checkedParam.getTagPageJs().isEmpty()) { //不是页面调用的业务,使用注解的tag,data保存为service的参数,这时需要页面专门解析渲染 String[] argStrs = Base64Util.toStrings(args); dbModel.setParamArgs(Base64Util.encodeZip(argStrs)); dbModel.setData(Base64Util.encode(argStrs)); } else { tag = checkedParam.getTagPageJs(); dbModel.setParamArgs(Base64Util.encodeZip(args)); dbModel.setData(Base64Util.encode(checkedParam.getData().toArray(new String[0]))); } dbModel.setTag(tag); dbModel.setParamTypes(Base64Util.encodeZip(checked.paramCanonical())); dbModel.setDescription(bean.description(tag, parameterTypes, args)); //持久化数据 processDbService.save(dbModel); return bean.retObject(tag, parameterTypes, args); } } }
线程池配置
测试用
package com.proc.config; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import com.alibaba.ttl.threadpool.TtlExecutors; @Configuration public class TaskExecutePoolConfig { @Bean public Executor processExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程池大小 executor.setCorePoolSize(10); //最大线程数 executor.setMaxPoolSize(10); //队列容量 executor.setQueueCapacity(500); //活跃时间 executor.setKeepAliveSeconds(60); //线程名字前缀 executor.setThreadNamePrefix("ProcessExecutor-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 等待所有任务结束后再关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); //用transmittable-thread-local包装,才可以正确给线程池中的线程传递数据 return TtlExecutors.getTtlExecutor(executor); } }
持久化service
为测试方便,未真正实现持久化
package com.proc.service; import com.proc.model.ProcessDbModel; public interface ProcessDbService { void save (ProcessDbModel model); ProcessDbModel get (); }
package com.proc.service.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.proc.model.ProcessDbModel; import com.proc.service.ProcessDbService; @Component public class ProcessDbServiceImpl implements ProcessDbService { private static final Logger log = LoggerFactory.getLogger(ProcessDbService.class); private volatile ProcessDbModel model; @Override public void save(ProcessDbModel model) { this.model = model; log.info(model.toString()); } @Override public ProcessDbModel get() { return this.model; } }
审批用的service
package com.proc.service; import com.proc.model.ProcessDbModel; public interface ProcessCheckService { void process (ProcessDbModel model); }
package com.proc.service.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.json.JsonMapper; import com.proc.model.ProcessDbModel; import com.proc.service.ProcessCheckService; import com.proc.util.Base64Util; import com.proc.util.JacksonCanonicalUtil; import com.proc.util.PrivateTransmitableUtil; import com.proc.util.ProcessBeanUtil; @Service public class ProcessCheckServiceImpl implements ProcessCheckService { private static final Logger log = LoggerFactory.getLogger(ProcessCheckServiceImpl.class); private static final JsonMapper MAPPER = new JsonMapper(); @Override public void process(ProcessDbModel model) { PrivateTransmitableUtil.set(); String[] paramArgs = Base64Util.decodeZip(model.getParamArgs()); String[] paramTypes = Base64Util.decodeZip(model.getParamTypes()); List<Class<?>> parameterTypes = new ArrayList<>(); List<Object> args = new ArrayList<>(); try { for (int i = 0; i < paramTypes.length; i++) { JavaType javaType = JacksonCanonicalUtil.constructFromCanonical(paramTypes[i]); parameterTypes.add(javaType.getRawClass()); args.add(MAPPER.readValue(paramArgs[i], javaType)); } } catch (JsonProcessingException e) { throw new RuntimeException("反序列化对象出错", e); } Object ret = ProcessBeanUtil.excuteBeanMethod( model.getTargetClassName(), model.getMethodName(), parameterTypes.toArray(new Class<?>[0]), args.toArray(new Object[0]) ); log.info(Objects.toString(ret)); } }
测试用的service
package com.proc.service; import com.proc.model.Person; import com.proc.model.Score; public interface TestService { String testCheck(Person<Score> person, String team); String testCheck2(Person<Score> person, String team); String testCheckAsync(Person<Score> person, String team); }
package com.proc.service.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.proc.bean.TestCheckPostProcess; import com.proc.config.Checked; import com.proc.model.Person; import com.proc.model.Score; import com.proc.service.TestService; @Service public class TestServiceImpl implements TestService { private static final Logger log = LoggerFactory.getLogger(TestServiceImpl.class); //paramCanonical对应testCheck的参数类型 @Checked( paramCanonical = {"com.proc.model.Person<com.proc.model.Score>", "java.lang.String"}, postProcess = TestCheckPostProcess.class) @Override public String testCheck(Person<Score> person, String team) { log.info(team + ">>>>" + person); return "target方法"; } @Checked( tag = "A1", paramCanonical = {"com.proc.model.Person<com.proc.model.Score>", "java.lang.String"}, postProcess = TestCheckPostProcess.class) @Override public String testCheck2(Person<Score> person, String team) { log.info(team + ">>2>>" + person); return "target2方法"; } @Async("processExecutor") @Checked( paramCanonical = {"com.proc.model.Person<com.proc.model.Score>", "java.lang.String"}, postProcess = TestCheckPostProcess.class) @Override public String testCheckAsync(Person<Score> person, String team) { log.info(team + ">>>>" + person); return "target方法"; } }
审批用的controller
package com.proc.ctrl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.proc.model.ProcessDbModel; import com.proc.service.ProcessCheckService; import com.proc.service.ProcessDbService; @RestController public class ProcessCheckController { private static final Logger log = LoggerFactory.getLogger(ProcessCheckController.class); @Autowired private ProcessDbService processDbService; @Autowired private ProcessCheckService processCheckService; @GetMapping(value = "process") public String process() { ProcessDbModel processDbModel = processDbService.get(); log.info(processDbModel.toString()); processCheckService.process(processDbModel); return "审批成功"; } }
测试用的controller
package com.proc.ctrl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.proc.model.CheckedParam; import com.proc.model.Person; import com.proc.model.Score; import com.proc.service.TestService; import com.proc.util.CheckedTransmitableUtil; @RestController public class TestController { @Autowired private TestService testService; //模拟页面调用 @GetMapping(value = "index") public String testCheck() { CheckedParam checkedParam = new CheckedParam(); checkedParam.setTagPageJs("01"); List<String> data = new ArrayList<>(); data.add("前端传进来的数据1"); data.add("前端传进来的数据2"); checkedParam.setData(data); CheckedTransmitableUtil.set(checkedParam); Person<Score> person = new Person<>(); person.setName("一个人"); person.setAge("18"); person.setSex("1"); person.setSalary("20000.00"); person.setTestName("测试人"); person.setWork("工作"); Score score1 = new Score(); score1.setEnglish(new BigDecimal("12.4")); score1.setLangue(new BigDecimal("764")); score1.setMath(new BigDecimal("87.4")); Score score2 = new Score(); score2.setEnglish(new BigDecimal("12.4")); score2.setLangue(new BigDecimal("764")); score2.setMath(new BigDecimal("87.4")); List<Score> list = new ArrayList<>(); list.add(score1); list.add(score2); person.setGrades(list); testService.testCheck(person, "team>>>>>>>>"); return "12345"; } //模拟其他渠道调用 @GetMapping(value = "index2") public String testCheck2() { Person<Score> person = new Person<>(); person.setName("一个人"); person.setAge("18"); person.setSex("1"); person.setSalary("20000.00"); person.setTestName("测试人"); person.setWork("工作"); Score score1 = new Score(); score1.setEnglish(new BigDecimal("12.4")); score1.setLangue(new BigDecimal("764")); score1.setMath(new BigDecimal("87.4")); Score score2 = new Score(); score2.setEnglish(new BigDecimal("12.4")); score2.setLangue(new BigDecimal("764")); score2.setMath(new BigDecimal("87.4")); List<Score> list = new ArrayList<>(); list.add(score1); list.add(score2); person.setGrades(list); testService.testCheck2(person, "team>>>2>>>>>"); return "12345"; } //模拟调用异步方法 @GetMapping(value = "index3") public String testCheckAsync() { CheckedParam checkedParam = new CheckedParam(); checkedParam.setTagPageJs("01"); List<String> data = new ArrayList<>(); data.add("前端传进来的数据1"); data.add("前端传进来的数据2"); checkedParam.setData(data); CheckedTransmitableUtil.set(checkedParam); Person<Score> person = new Person<>(); person.setName("一个人"); person.setAge("18"); person.setSex("1"); person.setSalary("20000.00"); person.setTestName("测试人"); person.setWork("工作"); Score score1 = new Score(); score1.setEnglish(new BigDecimal("12.4")); score1.setLangue(new BigDecimal("764")); score1.setMath(new BigDecimal("87.4")); Score score2 = new Score(); score2.setEnglish(new BigDecimal("12.4")); score2.setLangue(new BigDecimal("764")); score2.setMath(new BigDecimal("87.4")); List<Score> list = new ArrayList<>(); list.add(score1); list.add(score2); person.setGrades(list); testService.testCheckAsync(person, "team>>>3>>>>>"); return "12345"; } }
开启异步功能
package com.proc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class ProcessTestApplication { public static void main(String[] args) { SpringApplication.run(ProcessTestApplication.class, args); } }
测试
http://localhost:8080/indexhttp://localhost:8080/index2http://localhost:8080/index3
浏览器访问上面其中一个路径一次,再访问http://localhost:8080/process一次即可
加载全部内容