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('\n');
                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('\n');
                        }else if(StringUtils.contains(cn,SERVICE) &&StringUtils.contains(cn,IMPL) && e.getLineNumber()>0) {
                            sb.append( SERVICE+" 層 ->類名:"+e.getClassName()+",方法名:"+e.getMethodName()+",代碼行數:"+e.getLineNumber()+"");
                            sb.append('\n');
                        }else if(StringUtils.contains(cn,DAO) &&StringUtils.contains(cn,IMPL) && e.getLineNumber()>0 ){
                            sb.append(DAO +" 層->類名:"+e.getClassName()+",方法名:"+e.getMethodName()+",代碼行數:"+e.getLineNumber()+"");
                            sb.append('\n');
                        }
                    }
                }
                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('\n');
        sb.append(sql+'\n');

        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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM