利用Spring AOP實現業務和異常日誌記錄
AOP是面向切面編程,利用這個技術可以對商務邏輯的各個部分進行隔離,從而使得商務邏輯各個部分的耦合性降低,提高代碼的可重用性,同時提高開發效率(來自百度百科)。
實際上這個確實非常好用。最近碰到一個問題,就是發現以前action中的日誌記錄的不夠完善,需要在所有action中的每個介面改下調用日誌的方法,這種工作量太大而且毫無意義,因此就想到用AOP。(當然也可以用攔截器)
通過AOP把所有action中的介面作為切點,設定對應的切面和方法,讓介面返回後進行返回通知,在這個通知方法中進行日誌記錄,既減少了代碼量,也利於修改。
Spring AOP有兩種實現方式,一種是在spring-mvc中進行配置,一種是通過註解的方式實現。 spring-mvc配置
<!-- 啟用AOP --> <aop:aspectj-autoproxy /><!-- aop日誌記錄方法 --><bean id="busiLogAopAction" class="com.edf.optrace.core.LogAopAction"/><!-- 配置AOP --><aop:config> <!-- 配置切點運算式 --> <aop:pointcut id="busiLogPointcut" expression="execution(* com.edf.*.controller.*.*(..)) || execution(* com.abc.*.controller.*.*(..))" /> <!-- 配置切面及配置 --> <aop:aspect order="3" ref="busiLogAopAction"> <!-- 前置通知 <aop:before method="beforMethod" pointcut-ref="busiLogPointcut"/> --> <!-- 後置通知 <aop:after method="afterMethod" pointcut-ref="busiLogPointcut"/> --> <!-- 返回通知 --> <aop:after-returning method="afterReturnMethod" pointcut-ref="busiLogPointcut" returning="result"/> <!-- 返回異常 <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/> --> </aop:aspect></aop:config>
public class LogAopAction { @Autowired private OpHistoryService opHistoryService; /** * 返回通知(在方法正常結束執行的代碼) * 返回通知可以訪問到方法的傳回值。 * @param joinPoit */ public void afterReturnMethod(JoinPoint joinPoint, Object result){ //擷取方法入參list List<Object> args = Arrays.asList(joinPoint.getArgs()); String methodName = joinPoint.getSignature().getName();//擷取方法名 String className = joinPoint.getTarget().getClass().getName();//擷取所在實體類 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); String params = ""; if(args!=null && args.size()>0){ //1、處理request中入參,但擷取不到註解的bean Enumeration em = request.getParameterNames(); while (em.hasMoreElements()) { String name = (String) em.nextElement(); String value = request.getParameter(name); params += name +"=" + value +","; } params += ";"; //2、處理註解的bean for ( int i = 0; i < args.size(); i++) { try { params += JSONObject.fromObject(args.get(i)) + ";"; } catch (Exception e) { e.printStackTrace(); } } } String costTime = (String) request.getSession().getAttribute("costTime"); if(!"".equals(StringUtil.parseString(className))){ //插入業務日誌 opHistoryService.setOperateHistory(request, className.substring(className.lastIndexOf(".")+1), methodName, params, result.toString(), costTime); request.getSession().removeAttribute("costTime"); } }}
註解方式
/** * 設定異常日誌AOP */@Aspect@Order(2)@Componentpublic class ExLogAopAction { @Autowired private OpHistoryService opHistoryService; /** * 定義一個方法,用於聲明切入點運算式,方法中一般不需要添加其他代碼 * 使用@Pointcut聲明切入點運算式 * 後面的通知直接使用方法名來引用當前的切點運算式 */ @Pointcut("execution(* com.edf..*.*(..)) || execution(* com.abc..*.*(..))") public void declearJoinPointExpression(){} /** * 異常通知(方法發生異常執行的代碼) * 可以訪問到異常對象;且可以指定在出現特定異常時執行的代碼 * @param joinPoint * @param ex */ @AfterThrowing(value="declearJoinPointExpression()", throwing="ex") public void afterThrowingMethod(JoinPoint joinPoint, Exception ex){ String methodName = joinPoint.getSignature().getName(); String entity = joinPoint.getTarget().getClass().getName(); OpHistory opEx = new OpHistory(); opEx.setDictionaryId("new"); opEx.setOperateType("1");//1 通用異常;後期可加上 opEx.setOperateName("Exception"); opEx.setExt6(methodName);//方法名 opEx.setExt5(entity);//所在模組 opEx.setUpdateContent(StringUtil.getStringFixedLen(ex.toString(),3800));//異常資訊內容 //插入異常到DB opHistoryService.insertExErrorHistory(opEx); }}