***************************************分割線******************************************************java
參考:https://bbs.csdn.net/topics/390556755spring
兩位老哥的回覆。sql
想着在service層再單獨寫個方法去調用mapper;express
public int updateOrderStatus(Map param){ log.info("updateOrderStatus==============="+param+"======================"); return rechargeMapper.updateOrderStatus(param); }
int uptNum = this.updateOrderStatus(orderMap);
可是發現Spring AOP不攔截從對象內部調用的方法緣由apache
因此從新包裝一下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的方法改成調用新的實現類app
int uptNum = rechargeServiceAop.updateOrderStatus(orderMap);
而後去切這個新的類方法ide
@After("execution(public * com.zhx.recharge.service.RechargeServiceAop.updateOrderStatus(..))")
***************************************分割線******************************************************測試
以前引用的博客方法在junit本地測試可用,可是更新上服務器不可用。ui
***************************************分割線******************************************************
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>