SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试

Posted heiqiubaihu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试相关的知识,希望对你有一定的参考价值。

# SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试 #

  • Spring aop
  • Spring 定时任务
  • 代理模式深化

1.表设计

技术分享图片

2.Aop主要代码

@Aspect
@Component
public class AopUtils implements Ordered {
    //当前
    private static final Logger logger = LoggerFactory.getLogger(AopUtils.class);

    public static String  AOPLOG_NO_RETREN_VALUE = "NONE RETURN";

    @Value("${接口地址.url}")
    private String url;

    @Autowired
    private RequestInfoDao requestInfoDao;

    /**
     *  API 接口日志访问记录
     * 环绕通知实现
     * @param jp
     * @return
     */
    @Around("execution(* com.*.*.modules.*.service.CommonService.*(..))")
    public Object logApiMethod(ProceedingJoinPoint jp) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        Object rvt = null;
        RequestInfo requestInfo = new RequestInfo();



        //配合后面的定时任务重试接口机制
        if(sra == null) {
            try {
                rvt = jp.proceed(jp.getArgs());
                String params = "";
                List<Object> filterArgs = Lists.newArrayList();
                try {
                    if(jp.getArgs()!=null) {
                        Arrays.stream(jp.getArgs()).forEach(arg -> {
                            if (!(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse)) {
                                filterArgs.add(arg);
                            }
                        });
                    }
                    params = (filterArgs.size()==0)? "":JsonMapper.toJsonString(filterArgs);
                }catch (Exception e) {
                    params = filterArgs+"";
                }
                String returnValue = "";
                try {
                    returnValue = rvt==null ? "": JsonMapper.toJsonString(rvt);
                }catch (Exception e) {
                    returnValue = "返回值过长不显示";
                }
                requestInfo.setParam(params);
                requestInfo.setReturnValue(returnValue);
                requestInfo.setUpdateDate(new Date());
                User user = new User("接口重试");
                requestInfo.setUpdateBy(user);
                requestInfo.setResult(true);
                requestInfo.setErrorResson(null);
                requestInfoDao.updateByParams(info);
                return rvt;
            } catch (Throwable var6) {
                requestInfo.setResult(false);
                requestInfo.setErrorResson("线上接口访问异常:"+var6.getMessage());
                logger.error("线上接口访问异常:", var6);
            }
        }




        HttpServletRequest request = sra.getRequest();
        requestInfo.setResult(true);
        try {
            rvt = jp.proceed(jp.getArgs());
        } catch (Throwable var6) {
            requestInfo.setResult(false);
            requestInfo.setErrorResson("线上接口访问异常:"+var6.getMessage());
            logger.error("线上接口访问异常:", var6);
        }
        String methodName = jp.getSignature().getName();

        //参数写成一个集合数组,接口重试的时候需要转换
        String params = "";
        List<Object> filterArgs = Lists.newArrayList();
        try {
            if(jp.getArgs()!=null) {
                Arrays.stream(jp.getArgs()).forEach(arg -> {
                    if (!(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse)) {
                        filterArgs.add(arg);
                    }
                });
            }
            params = (filterArgs.size()==0)? "":JsonMapper.toJsonString(filterArgs);
        }catch (Exception e) {
            params = filterArgs+"";
        }
        String returnValue = "";
        if(isShowReturnValue(methodName)) {
            try {
                returnValue = rvt==null ? "": JsonMapper.toJsonString(rvt);
            }catch (Exception e) {
                returnValue = "返回值过长不显示";
            }
        }else {
            returnValue = "不显示返回值";
        }
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        String beanName = jp.getThis().getClass().getSimpleName();
        int num = beanName.indexOf("$$");
        beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring( 1, num);
        requestInfo.setId(IdGen.uuid());
        requestInfo.setBeanName(beanName);
        requestInfo.setMethod(methodName);
        requestInfo.setParam(params);
        requestInfo.setRequestUrl(url+requestURI);
        requestInfo.setReturnValue(returnValue);
        requestInfo.setCreateDate(new Date());
        requestInfo.setUpdateDate(new Date());



        //接口是否可重复
        Method aopMethod = ((MethodSignature)jp.getSignature()).getMethod();
        Method m = jp.getTarget().getClass().getDeclaredMethod(methodName, aopMethod.getParameterTypes());
        InterfaceRepeatFlag interfaceRepeatFlag = m.getAnnotation(InterfaceRepeatFlag.class);
        if (interfaceRepeatFlag == null){
            requestInfo.setRepeatFlag(false);
        }else {
            requestInfo.setRepeatFlag(interfaceRepeatFlag.value());
        }


        User user = new User();
        user.setId("接口调用");
        requestInfo.setCreateBy(user);
        requestInfo.setUpdateBy(user);

        if(requestInfoDao != null) {
            requestInfoDao.insert(requestInfo);
        }
        return rvt;
    }

    private boolean isShowReturnValue(String methodName) {
        List<String> debugRoles = Arrays.asList(AOPLOG_NO_RETREN_VALUE.split(","));
        for(String debugRole : debugRoles) {
            if(!StringUtils.isBlank(methodName)
                    && methodName.indexOf(debugRole) == 0){
                return false;
            }
        }
        return true;
    }



    @Override
    public int getOrder() {
        return 1001;
    }
}

3.定时任务配置

<bean name="schedulerFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <!-- 定义数据源 ,数据源里面需要Quzrtz集群所需要的表 -->
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <!-- 指定spring容器的key,如果不设定在job中的jobmap中是获取不到spring容器的 -->
    <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
    <!-- 指定spring的配置相关信息 -->
    <property name="configLocation" value="classpath:quartz.properties" />
    <!-- 指定触发器,可以指定多个 -->
    <property name="triggers">
        <list>
            <ref bean="interfaceJobTrigger"/>
        </list>
    </property>
</bean>

<!-- 定义任务 -->
<bean id="interfaceJob" class="com.*.*.modules.quzrtz.service.interfaceJob"/>
<bean id="interfaceJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass">
        <!-- 上面的任务的代理类(自己实现) -->
        <value>com.*.*.modules.quzrtz.scheduler.InterfaceJobSchedule</value>
    </property>
    <property name="description">
        <value>定时任务接口重试</value>
    </property>
    <property name="jobDataAsMap">
        <map>
            <!--实际的任务BeanName,填上EventMonitorService的BeanName -->
            <entry key="targetObject" value="interfaceJob" />
            <!-- 执行Bean中的哪个方法 -->
            <entry key="targetMethod" value="repeatInterface" />
        </map>
    </property>
</bean>
<bean id="interfaceJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail">
        <!-- 任务代理Bean name -->
        <ref bean="interfaceJobDetail" />
    </property>
    <property name="description">
        <!-- 任务代理Bean name -->
        <value>定时任务接口重试</value>
    </property>
    <property name="cronExpression">
        <!-- 配置表达式,这里表示每1分钟执行一次 -->
        <value>0 0/1 * * * ?</value>
    </property>
</bean>

4.定时任务

@Transactional(readOnly = false)
public void repeatInterface() {
    RequestInfo requestInfo = new RequestInfo();
    requestInfo.setRepeatFlag(true);
    requestInfo.setResult(false);
    List<RequestInfo> requestInfoList = requestInfoDao.findAllList(requestInfo);
    for (RequestInfo requestInfo : requestInfoList){
        String paramStr = requestInfo.getParam();
        String beanStr = requestInfo.getBeanName();
        String methodStr = requestInfo.getMethod();
       try {
           Object obj = SpringContextHolder.getBean(beanStr);
           Method m = null;
           Method[] methods = obj.getClass().getMethods();
           if (methods != null && methods.length > 0) {
               for (Method method : methods) {
                   if (method.getName().equals(methodStr)) {
                       m = method;
                       break;
                   }
               }
           }
           Class paramCls = m.getParameterTypes()[0];
        List<Object> params =  JSONArray.parseArray(paramStr);
        MethodUtils.invokeExactMethod(obj, methodStr, JSON.parseObject(JSONObject.toJSONString(params.get(0)), paramCls));
        }catch (Exception e){
            e.printStackTrace();
            logger.error("接口重试:失败,失败原因:"+e.getMessage());
            continue;
        }
        logger.info("接口重试成功:类名"+beanStr+";方法:"+methodStr+";参数:"+paramStr+";结束重试");
    }
}

5.定时任务调度器-定时任务代理

public class InterfaceJobSchedule extends QuartzJobBean {
    private Logger logger = LoggerFactory.getLogger(InterfaceJobSchedule.class);

    private String targetObject;
    private String targetMethod;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            Object otargetObject = SpringContextHolder.getBean(targetObject);
            Method m = null;
            Method[] methods = otargetObject.getClass().getMethods();
            if (methods != null && methods.length > 0) {
                for (Method method : methods) {
                    if (method.getName().equals(targetMethod)) {
                        m = method;
                        break;
                    }
                }
                m.invoke(otargetObject, new Object[]{});
            }

        } catch (Exception e) {
            logger.error("InterfaceJobSchedule Exception:", e);
        }
    }

    /**
     * @param targetObject the targetObject to set
     */
    public void setTargetObject(String targetObject) {
        this.targetObject = targetObject;
    }

    /**
     * @param targetMethod the targetMethod to set
     */
    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }
}

6.接口重试注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InterfaceRepeatFlag {
    public boolean value() default true;
}

7.学习总结

Spring AOP核心是代理模式,可见代理模式的拓展性有多强,有业务的代码才会有收获,试一试,收获颇多

以上是关于SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试的主要内容,如果未能解决你的问题,请参考以下文章

定时任务 && 例行任务

Kubernetes中的Job(工作计划)&amp;amp;CronJob(定时工作计划)Demo+实战

定时任务 & 定时线程池 ScheduledThreadPoolExecutor

十一:并发编程之定时任务&定时线程池

JS 定时器&异步任务与函数节流

openresty跑定时任务配置&启动