前言: 最近接手了一個項目,大概過了下需求,然後打開項目準備開搞的時候發現一個問題,這個項目是提供rest服務的一個web項目,其中很多關鍵的查詢都調用這個項目,之前的開發人員為了監控每個方法的執行時間,在方法開始和結束寫了很多logger.info("耗時:"+time)這種代碼。很顯然這是不合理 ...
前言:
最近接手了一個項目,大概過了下需求,然後打開項目準備開搞的時候發現一個問題,這個項目是提供rest服務的一個web項目,其中很多關鍵的查詢都調用這個項目,之前的開發人員為了監控每個方法的執行時間,在方法開始和結束寫了很多logger.info("耗時:"+time)這種代碼。很顯然這是不合理的,因為在項目里到處有這樣的代碼存在,沒有做到面向對象,而JAVA最基本的就是面向對象,並且對於有1-2年的開發人員來說不僅僅要考慮如何完功能,還要考慮如何把代碼優化好。廢話不多說,簡單描述下我的改造過程。
-
創建一個方法攔截器:
/** * 用來監控方法的執行時間-- 對應配置文件是spring-servlet.xml * PS:必須放到springmvc的配置文件里,放在spring父容器裡面由於先初始化的是spring父容器上下文,先實例化的是除@Controller外的bean,所以無法生成代理。 * * @author dada * @version $Id: MethodTimeAdvice.java, v 0.1 * @date 2017年11月15日 09:47:23 */ public class MethodTimeAdvice implements MethodInterceptor { private final static Logger logger = Logger.getLogger("DAL-MONITOR"); /** * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) */ public Object invoke(MethodInvocation invocation) throws Throwable { //用 commons-lang 提供的 StopWatch 計時,Spring 也提供了一個 StopWatch StopWatch clock = new StopWatch(); clock.start(); //計時開始 Object result = null; //監控的方法名 String methodName = getRequestMappingName(invocation); if(null == methodName){ methodName = invocation.getMethod().getDeclaringClass().getSimpleName(); } try { //這個是我們監控的bean的執行並返回結果 result = invocation.proceed(); } catch (Throwable e) { //監控的類名 String className = invocation.getMethod().getDeclaringClass().getSimpleName(); //監控的參數 Object[] objs = invocation.getArguments(); logger.error("控制層執行異常,方法名:" + className + "參數: " + getString(objs), e); throw e; } clock.stop(); //計時結束 if (logger.isInfoEnabled()) { logger.info("[ " + methodName + " ] 執行時間:" + clock.getTime() + " ms "); } return result; } /** * 這個類主要是用於輸出方法的參數 * * @param objs * @return */ @SuppressWarnings("unchecked") public String getString(Object[] objs) { StringBuffer stringBuffer = new StringBuffer(); for (int i = 0, len = objs.length; i < len; i++) { if (objs[i] instanceof String) { stringBuffer.append("String類型:" + objs[i].toString()); } else if (objs[i] instanceof Map) { HashMap<String, Object> hashMap = (HashMap<String, Object>) objs[i]; HashMap<String, Object> map = hashMap; HashSet<String> set = (HashSet<String>) map.keySet(); stringBuffer.append("Map類型"); for (String str : set) { stringBuffer.append(str + "=" + map.get(str)); } } else if (objs[i] instanceof Integer) { stringBuffer.append("整數類型:"); stringBuffer.append(objs[i].toString()); } else { stringBuffer.append(objs[i].toString()); } } return stringBuffer.toString(); } public String getRequestMappingName(MethodInvocation invocation){ Method method = invocation.getMethod(); RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); if(requestMapping!=null){ return requestMapping.name(); } return null; } }
-
配置spring BeanNameAutoProxyCreator 用來攔截上面的 MethodTimeAdvice
<bean id="methodTimeAdvice" class="com.i2p.admin.interceptor.MethodTimeAdvice" /> <!-- 根據 Bean 的名字自動實現代理攔截 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <value>methodTimeAdvice</value> </list> </property> <property name="beanNames"> <list> <!-- 添加到其中的 Bean 自動就被代理攔截了(下麵的是對應的bean的名稱,那麼當請求InnovatePartBorrowController任何一個方法都會執行MethodTimeAdvice裡面的Invoke方法) --> <value>innovatePartBorrowController</value> </list> </property> </bean>
總結:
通過spring 代理,不僅減少了代碼的copy工作,同時又方便管理;至於後續功能的擴展,我們只需要在spring-mvc.xml里動態配置好對應的bean就能實現日誌的列印。