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核心是代理模式,可见代理模式的拓展性有多强,有业务的代码才会有收获,试一试,收获颇多

原文地址:https://www.cnblogs.com/heiqiubaihu/p/9304536.html

时间: 2024-10-28 23:24:25

SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试的相关文章

孢子框架-接口访问层、ESB、微服务API GateWay对比

如果从百度去搜索“接口访问层”你会发现主要是.NET里面的技术,叫做IDAL,其实是数据访问层接口.它的主要作用是兼容多种数据库.比如你定义一个标准接口,然后实现改接口的SqlServer访问和Oracle访问,那么利用IDAL就可以自由切换数据库.看.NET DEMO PetShop4,总共有22个项目.大体思想是3层,从Model.DAL.BLL,然后他在各层上又采用了工厂模式,把逻辑与实现想分离,比如以前BLL直接调用DAL就好了,但现在BLL却调用了IDAL,IDAL就是一个接口层,里面

通过ODBC接口访问人大金仓数据库

??国产化软件和国产化芯片的窘境一样,一方面市场已经存在性能优越的同类软件,成本很低,但小众的国产化软件不仅需要高价买入版权,并且软件开发维护成本高:另一方面,国产软件目前普遍难用,性能不稳定,Bug满天飞,虽然可以去迭代,但是没有人愿意耐心地等国产软件迭代,甚至市场的生态不给国产软件迭代的机会,选择性忽视国产,国内公司也不会给工程师去试错的机会,这直接限制了中国软件的能力提升. ??虽然国产软件处于劣势,但是在涉及GJ安全的领域,国产化一词是个香饽饽,处于强势地位,尤其是中兴事件以后,国产化会

Odoo(OpenERP)开发实践:通过XML-RPC接口访问Odoo数据库

Odoo(OpenERP)服务器支持通过XML-RPC接口访问.操作数据库,基于此可实现与其他系统的交互与集成. 本文是使用Java通过XMLRPC接口操作Odoo数据库的简单示例.本例引用的jar包包括xmlrpc-common-3.1.3.jar, xmlrpc-client-3.1.3.jar和ws-commons-util-1.0.2.jar,如需要,可点击这里下载. package memo.by.weichen; import java.net.URL; import java.ut

(6)s3c2440用I2C接口访问EEPROM

在前面阅读理解了I2C的官方协议文档后,就拿s3c2440和EEPROM来验证一下. 本来是想用s3c2440的SDA和SCL管脚复用为GPIO来模拟的,但在没有示波器的情况下搞了一周,怎么都出不来,最后还是放弃了.甚至参考了linux下i2c-algo-bit.c和i2c-gpio.c,依然没调出来.如果有示波器,可能很快就能找到原因,现在完全不知道问题出在哪里.其实想用GPIO模拟I2C的目的很简单,以一种简单而又深刻的方式来理解I2C. 既然这条路暂时没法走,退而求其次,用s3c2440的

轻型的接口访问频率限制服务模型的设计与实现【转】

原文地址:http://www.iam3y.com/html/878.html 最近需要设计open api的接口频次控制相关实现,便查阅相关文档. 接口频次控制主要包括两方面: (1)业务ID对某一个接口某时间间隔(如一分钟)内访问的次数 限制 (2)业务ID在某个时间周期(如一天)内访问的次数 限制 对于存储并进行频次计数的服务来说,要具备以下的特点: (1)自更新能力,在某个约定的时间点对所有的node(节点)进行自更新操作,也就是常说的出厂设置 (2)协议轻型能力,协议必须尽可能简单,才

聊聊高并发(十六)实现一个简单的可重入锁

可重入锁指的是如果一个线程已经获得了一个锁,那么它可以多次进入这个锁,当然前提是线程需要先获得这个锁. 可重入锁是最常使用的锁,Java的内置锁就是可重入锁,使用synchronized关键字可以启用内置锁机制,比如说一个类有两个synchronized方法A和B,在A方法中调用了B方法,如果锁不是可重入的,那么访问B时需要再次竞争锁,这样会带来死锁. public synchronized void A(){ B(); } public synchronized void B(){ } 可重入

sqlserver给指定用户授权访问指定表

一.   背景 外部公司的人授权访问我们公司的数据库,数据接口调用,要给他们建立查看指定的视图和授权的账号,因此要在数据库中,给指定用户授权访问指定表 二.sqlserver 脚本 ---创建视图CREATE VIEW [dbo].[View_RunningData] AS SELECT TOP (100) PERCENT a.areaorganize, c.F_FullName AS areaorganizeName, a.recorddate, d.F_ItemCode, d.F_ItemN

Postman 如何处理上一个接口返回值作为下一个接口入参?

今天做接口测试,有一个接口的参数是一个校验 token,会实时更新,开发提供了一个单独返回实时 token 的接口,所以就需要在功能接口使用时调用 token 接口的返回值,作为功能接口的参数来使用. 网上搜了一下,都没有现成的使用说明,刚才研究出来了,就记录下步骤,方便后面的同学. 如果返回 token 的接口的返回值,是标准的 JSON 格式的话,就很简单的两步就行了. 1.token 接口设置全局变量 第一步就是执行 token 接口,并把接口返回值里面的 token 值,赋值给一个全局变

mysql访问权限GRANT ALL PRIVILEGES ON,访问权限表

开启远程连接:2, 修改 Mysql-Server 用户配置mysql> USE mysql; -- 切换到 mysql DBDatabase changedmysql> SELECT User, Password, Host FROM user; -- 查看现有用户,密码及允许连接的主机+------+----------+-----------+| User | Password | Host      |+------+----------+-----------+| root |