⽬录
⽬标实现
POM⼀些实体类CheckedParamProcessDbModel测试⽤的⼊参对象⼀些⼯具类
BaseUtilSpringBootBeanUtilProcessBeanUtilCheckedTransmitableUtilPrivateTransmitableUtil⼀些Bean
PostProcessTestCheckPostProcessAspect注解切⾯类 CheckedAop线程池配置持久化service审批⽤的service测试⽤的service审批⽤的controller测试⽤的controller开启异步功能测试⽬标
本⽂提供⼀种⾃定义注解,来实现业务审批操作的DEMO,不包含审批流程的配置功能。具体⽅案是
⾃定义⼀个Aspect注解,拦截sevice⽅法,将拦截的信息持久化,待审批;审批时获取持久化数据,执⾏⽬标⽅法。
实现
POM
⼀些实体类
CheckedParam
⽤于包装页⾯传进来的参数
package com.proc.model;import java.util.List;
public class CheckedParam {
//业务标记,由页⾯传⼊,⽤于审批时页⾯根据tagPageJs解析data,渲染到页⾯,审批管理员可看到审批的内容 private String tagPageJs; //页⾯传⼊的原始数据 private List public String getTagPageJs() { return tagPageJs; } public void setTagPageJs(String tagPageJs) { this.tagPageJs = tagPageJs; } public List public void setData(List 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 + \ + \ + 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 + \ }} package com.proc.model;import java.util.List; public class Person private String age; private String sex; private String testName; private String salary; private String work; private List 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 public void setGrades(List @Override public String toString() { return \"Person [name=\" + name + \ + salary + \ } } ⼀些⼯具类 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 return MAPPER.getTypeFactory().constructType(clazz).toCanonical(); } public static 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.Base; import java.util.zip.DeflaterOutputStream;import java.util.zip.InflaterOutputStream;public class StringZipUtil { private StringZipUtil () {} public static String zipBase(String text) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { try (OutputStream os = new DeflaterOutputStream(out)) { os.write(text.getBytes(StandardCharsets.UTF_8)); } return Base.getEncoder().encodeToString(out.toByteArray()); } catch (Exception e) { throw new RuntimeException(\"压缩字符串出错\ } } public static String unzipBase(String text) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { try (OutputStream os = new InflaterOutputStream(out)) { os.write(Base.getDecoder().decode(text)); } return new String(out.toByteArray(), StandardCharsets.UTF_8); } catch (Exception e) { throw new RuntimeException(\"解压字符串出错\ } }} BaseUtil ⼀些参数值转为Base后持久化 package com.proc.util; import java.nio.charset.StandardCharsets;import java.util.ArrayList;import java.util.Base;import java.util.List; import java.util.stream.Collectors; import com.fasterxml.jackson.databind.json.JsonMapper;public class BaseUtil { private BaseUtil () {} private static final JsonMapper MAPPER = new JsonMapper(); public static String[] toStrings (Object[] objs) { List for (Object obj : objs) { list.add(MAPPER.writeValueAsString(obj)); } } catch (Exception e) { throw new RuntimeException(\"序列化对象出错\ } return list.toArray(new String[0]); } public static String encode (String[] strs) { List list.add(Base.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(\"\\\\|\ List list.add(new String(Base.getDecoder().decode(base), 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 list.add(StringZipUtil.zipBase(str)); } String join = list.stream().collect(Collectors.joining(\"|\")); return StringZipUtil.zipBase(join); } public static String[] decodeZip (String text) { String str = StringZipUtil.unzipBase(text); String[] strs = str.split(\"\\\\|\ List list.add(StringZipUtil.unzipBase(base)); } 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 public static public static 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(\"未找到类\ } 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 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 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 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 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.BaseUtil; 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 = BaseUtil.toStrings(args); dbModel.setParamArgs(BaseUtil.encodeZip(argStrs)); dbModel.setData(BaseUtil.encode(argStrs)); } else { tag = checkedParam.getTagPageJs(); dbModel.setParamArgs(BaseUtil.encodeZip(args)); dbModel.setData(BaseUtil.encode(checkedParam.getData().toArray(new String[0]))); } dbModel.setTag(tag); dbModel.setParamTypes(BaseUtil.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.BaseUtil; 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 = BaseUtil.decodeZip(model.getParamArgs()); String[] paramTypes = BaseUtil.decodeZip(model.getParamTypes()); List
Copyright © 2019- dfix.cn 版权所有 湘ICP备2024080961号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务