• aop切入mapper接口


    ***************************************分割线******************************************************

    参考:https://bbs.csdn.net/topics/390556755

    两位老哥的回复。

    想着在service层再单独写个方法去调用mapper;

      public int updateOrderStatus(Map param){
            log.info("updateOrderStatus==============="+param+"======================");
            return  rechargeMapper.updateOrderStatus(param);
        }
    int uptNum = this.updateOrderStatus(orderMap);

    但是发现Spring AOP不拦截从对象内部调用的方法原因

    所以重新包装一下mapper类,新建一个类

    @Service
    public class RechargeServiceAop {
        private org.slf4j.Logger log = LoggerFactory.getLogger(String.valueOf(RechargeServiceAop.class));
        @Autowired
        private RechargeMapper rechargeMapper;
    
        public int updateOrderStatus(Map param){
            log.info("updateOrderStatus==============="+param+"======================");
            return  rechargeMapper.updateOrderStatus(param);
        }
    }

    将业务层调用mapper的方法改为调用新的实现类

     int uptNum = rechargeServiceAop.updateOrderStatus(orderMap);

    然后去切这个新的类方法

      @After("execution(public * com.zhx.recharge.service.RechargeServiceAop.updateOrderStatus(..))")

    ***************************************分割线******************************************************

    之前引用的博客方法在junit本地测试可用,但是更新上服务器不可用。

    ***************************************分割线******************************************************

    https://blog.csdn.net/weixin_35562755/article/details/78689862?utm_source=copy

    以下为项目dao层的简单接口定义: 

    public interface BaseDao {
    
    public List<List<String>> queryListData(String sql, Object[] o);
    
    public List<List<String>> queryListDataNoParams(String sql);
    
    public List queryList(String sql, Object[] o);
    
    public <T> List<T> queryObjList(String sql, Object[] args, Class<T> clazz);
    
    public int queryForInt(String sql, Object[] o);
    
    public int addOrUpdate(String sql, Object[] o);
    }

    可以发现传参比较简单,基本都是传入sql,以及一些sql参数,我们只需要拦截到这些要执行的方法,通过JAVA反射拿到对应的参数,进行控制台输出就好了。但是仅仅输出了sql还不够,我们还需要显示的知道这个方法的调用过程。这里通过Java线程来获取方法运行栈的信息。对比看了下具有sql监控的淘宝数据源druid,其实现逻辑大体上也是运用了AOP的原理进行SQL的监控。

    废话不少说直接上代码:
    1)自定义一个方法拦截器 DisplayExecuteSqlInterceptor :

    package com.XXX.CCC.aop;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.lang.reflect.Method;
    
    /**
     *
     * 方法拦截  粒度在方法上
     *
     * @desc 调试管理 利用 AOP 原理, 在开发模式下于控制台展示 dao层 的实际执行的SQL
     * 粘出来即可 在pl/sql下执行,已经替换掉 ? 了
     *
     * @author luotianyi
     * @create 2017-11-30 14:03
     **/
    public class DisplayExecuteSqlInterceptor implements MethodInterceptor {
    
        private static final Logger log = LoggerFactory.getLogger(DisplayExecuteSqlInterceptor.class);
    
        private static final String CONTROLLER ="CONTROLLER";
        private static final String SERVICE ="SERVICE";
        private static final String DAO ="DAO";
        private static final String IMPL ="IMPL";
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
    
            //系统的开发模式
            String codeModel =System.getProperty("codeModel");
            String flag="true";
            if(StringUtils.equals(flag,codeModel)){
                //获取该方法的传参
                Object[] ars = mi.getArguments();
    
                //通过反射机制 获取到该方法 (Method 包含 作用域 返回类型  方法名  参数类型)
                Method method= mi.getMethod();
    
                //获取代理的对象 (也就是这个方法所在内存中的对象)
                Object obj = mi.getThis();
    
                Object [] params =new Object[]{} ;
                String sql ="";
                for(Object o :ars){
                    if(o instanceof Object[]){
                        params= (Object[]) o;
                    }else if(o instanceof String){
                        sql=(String) o;
                    }
                }
    
                Thread current = Thread.currentThread();
                StackTraceElement[] elements =current.getStackTrace();
    
                //倒序输出 栈帧 信息 ,过滤出 项目的代码 这里只过滤出(Controller / service impl / dao impl)层的代码,如需要其他的可自行遍历
                if(elements !=null && elements.length>0){
                    //获得项目名
                    String packageName =DisplayExecuteSqlInterceptor.class.getPackage().getName();
                    packageName=StringUtils.substringBefore(packageName,".");
                    StringBuilder sb =new StringBuilder();
                    sb.append(" -------->本次执行SQL的代码在<--------- ");
                    sb.append('
    ');
                    for(int i=elements.length ;i>0 ;i--){
                        StackTraceElement e =elements[i-1];
                        if(StringUtils.contains(e.getClassName(),packageName)){
                            String cn=StringUtils.upperCase(e.getClassName());
                            if(StringUtils.contains(cn,CONTROLLER)){
                                sb.append( CONTROLLER+" 层 ->类名:"+e.getClassName()+",方法名:"+e.getMethodName()+",代码行数:"+e.getLineNumber()+"");
                                sb.append('
    ');
                            }else if(StringUtils.contains(cn,SERVICE) &&StringUtils.contains(cn,IMPL) && e.getLineNumber()>0) {
                                sb.append( SERVICE+" 层 ->类名:"+e.getClassName()+",方法名:"+e.getMethodName()+",代码行数:"+e.getLineNumber()+"");
                                sb.append('
    ');
                            }else if(StringUtils.contains(cn,DAO) &&StringUtils.contains(cn,IMPL) && e.getLineNumber()>0 ){
                                sb.append(DAO +" 层->类名:"+e.getClassName()+",方法名:"+e.getMethodName()+",代码行数:"+e.getLineNumber()+"");
                                sb.append('
    ');
                            }
                        }
                    }
                    log.info(sb.toString());
                }
                getExecuteSql(sql,params);
            }
            //执行被拦截的方法,切记,如果此方法不调用,则被拦截的方法不会被执行。
            return mi.proceed();
        }
    
        private String getExecuteSql(String sql, Object[] params) {
            if (StringUtils.isNotBlank(sql)) {
                if (params != null && params.length > 0) {
                    int a = getCount(sql, '?');
                    int b = params.length;
                    if (a == b) {
                        sql = StringUtils.replace(sql, "?", "XXXX");
                        for (int i = 0; i < params.length; i++) {
                            Object obj = params[i];
                            if (StringUtils.isNotBlank(String.valueOf(obj)) && StringUtils.isNumeric(String.valueOf(obj))) {
                                obj = Integer.valueOf(String.valueOf(obj));
                            } else {
                                obj = "'" + obj + "'";
                            }
                            sql = sql.replaceFirst("XXXX", String.valueOf(obj));
                        }
                    } else {
                        log.info("参数个数传的不正确, sql中 需要 :{} 个参数,实际传入参数为 :{} 个。", a, b);
                        return null;
                    }
                }
            }
    
            StringBuilder sb =new StringBuilder();
            sb.append(" ----------->本次执行sql为:<----------- ");
            sb.append('
    ');
            sb.append(sql+'
    ');
    
            log.info(sb.toString());
            return sql;
    
        }
    
        private  int getCount(String sql ,char a ){
            int count=0;
            if(StringUtils.isNotBlank(sql)){
                for (int i = 0; i < sql.length(); i++) {
                    if(sql.charAt(i)==a){
                        count++;
                    }
                }
            }
            return count;
        }
    
    }

    2)在spring配置文件中,添加下一下的配置:

    <bean id="displayExecuteSqlInterceptor" class="com.zhx.base.interceptor.DisplayExecuteSqlInterceptor" ></bean>
    <!--将自定义拦截器注入到spring中-->
    <aop:config>
    
        <!--切入点,也就是你要监控哪些类下的方法,由于是监控SQL的执行情况,这里写的是DAO层的目录,包名要记得换成你自己项目的目录哟,大兄弟-->
        <aop:pointcut id="displayExecuteSql" expression="execution(public * com. com.XXX.CCC.base.dao.impl.*.*(..)) "/>
        <!--在该切入点使用自定义拦截器-->
        <aop:advisor pointcut-ref="displayExecuteSql" advice-ref="displayExecuteSqlInterceptor"/>
    
    </aop:config>
  • 相关阅读:
    组合数
    POJ2774 Long Long Message
    后缀排序【后缀数组入门题】
    luogu P3205 [HNOI2010]合唱队 区间dp
    luogu P3119 [USACO15JAN]Grass Cownoisseur G 有向图强连通分量+分层最短路
    luogu P3469 [POI2008]BLO-Blockade 割点
    luogu P2569 [SCOI2010]股票交易 单调优化dp
    luogu P2939 [USACO09FEB]Revamping Trails G 分层最短路
    luogu P3957 跳房子 二分+斜率优化dp
    luogu P1772 [ZJOI2006]物流运输 spfa最短路+dp
  • 原文地址:https://www.cnblogs.com/SimonHu1993/p/9772476.html
Copyright © 2020-2023  润新知