之前總結寫了一篇通過xml配置的方式,切面編程實現日志記錄的功能demo
http://blog.csdn.net/weiweiai123456/article/details/38561085
可參考http://blog.csdn.net/heirenheiren/article/details/36634497 ,講的是注解實現
現在實現一個通過注解方式實現的樣例:
一:準備
xml中需要開啟CGLIB動態代理
<!-- 啟用CGliB --> <aop:aspectj-autoPRoxy proxy-target-class="true"/>切面編程----AOP,依賴的是代理,即JDK代理和CGLIB代理,而代理的實現依靠的是反射。maven配置省...
二:注解類
SaveSysLog.java
package com.cooya.partner.metadata.entity.baseConfig;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * * Description: 保存系統日志注解接口 * * @author suoww * @date 2017-2-8 * */@Retention(RetentionPolicy.RUNTIME) //注解會在class中存在,運行時可通過反射獲取@Target(ElementType.METHOD) //注解到方法public @interface SaveSysLog { //調用方 1:嗨賺客戶端 2:支付寶 3:微信 4:錢寶 5:其他第三方 int send() default 1; //接口url(從二級目錄記起) String url(); //接口類型(前臺,后臺) int type();}定義三個成員,這里只能定義八種基本數據類型,分別是
byte-->Byte
short-->Short
int-->Integer
long-->Long
float-->Float
double-->Double
char-->Character
boolean-->Boolean
注意:只能是上述這些8種類型的變量
三:切面類
SysLogAspect.java
package com.cooya.partner.service.baseConfig;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Date;import java.util.HashMap;import java.util.Map;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.Httpsession;import org.apache.shiro.SecurityUtils;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.alibaba.fastjson.JSONObject;import com.cooya.partner.constant.InterfaceTypeConst;import com.cooya.partner.metadata.entity.baseConfig.PartnerSystemLog;import com.cooya.partner.metadata.entity.baseConfig.SaveSysLog;import com.cooya.partner.metadata.entity.user.PartnerUser;import com.cooya.partner.metadata.mapper.baseConfig.PartnerSystemLogMapper;import com.cooya.partner.permission.dto.ShiroUser;/** * * Description: 切面類記錄接口調用失敗日志信息 * * @author suoww * @date 2017-2-8 * */@Aspect@Componentpublic class SysLogAspect { public static final int CODE_SUCCESS = 0; private Logger logger = LoggerFactory.getLogger(SysLogAspect.class); @Resource private PartnerSystemLogMapper partnerSystemLogMapper; /** * * Description: 定義切點名controllerAspect,此方法需要為空,只是標識切點和切面關系 * * @author suoww * @date 2017-2-8 */ @Pointcut("@annotation(com.cooya.partner.metadata.entity.baseConfig.SaveSysLog)") public void controllerAspect(){} /** * * Description:織入后增強 * * @param join * @author suoww * @throws Exception * @date 2017-2-8 */ @AfterReturning(pointcut = "controllerAspect()", returning = "res") public void doAfter(JoinPoint joinPoint, Object res) throws Exception{ //獲取反射參數 logger.debug("---------------AfterReturning開始--------------"); if(null == res){ return; } Map<String, Object> map = Obj2Map(res); int code = (Integer)map.get("code"); if(code == CODE_SUCCESS){ return; } String message = (String)map.get("message"); //類名 String targetName = joinPoint.getTarget().getClass().getSimpleName(); //得到方法名 String methodName = joinPoint.getSignature().getName(); MethodSignature ms = (MethodSignature) joinPoint.getSignature(); //入參key String[] parameterNames = ms.getParameterNames(); //入參value Object[] arguments = joinPoint.getArgs(); Method method = ms.getMethod(); //方法的注解對象 SaveSysLog logParam = method.getAnnotation(SaveSysLog.class); /* logger.debug("SaveSysLog注解參數send:" + logParam.send()); logger.debug("SaveSysLog注解參數url:" + logParam.url()); logger.debug("SaveSysLog注解參數type:" + logParam.type()); logger.debug("targetName:" + targetName); logger.debug("methodName:" + methodName); logger.debug("ms:" + ms); logger.debug("arguments:" + JSONObject.toJSONString(arguments)); logger.debug("parameterNames:" + JSONObject.toJSONString(parameterNames)); logger.debug("method:" + JSONObject.toJSONString(method));*/ //拼參數 PartnerSystemLog sysLog = new PartnerSystemLog(); //獲取用戶 if(logParam.type() == InterfaceTypeConst.InterfaceType.APP){ sysLog.setUserId(getAppUserId()); }else{ sysLog.setUserId(getMgrUserId()); } sysLog.setSend(logParam.send()); sysLog.setUrl(logParam.url()); sysLog.setType(logParam.type()); //入參字符串 StringBuffer jsonParamSb = new StringBuffer(); for(int i = 0;i < parameterNames.length;i++){ jsonParamSb.append(parameterNames[i]).append("=").append(JSONObject.toJSONString(arguments[i])); if(i != (parameterNames.length - 1)){ jsonParamSb.append("&"); } } //截取返回json if(jsonParamSb.toString().length() <= 1000){ sysLog.setJsonParam(jsonParamSb.toString()); }else{ sysLog.setJsonParam(jsonParamSb.toString().substring(0, 1000)); } //出參 sysLog.setJsonResult(JSONObject.toJSONString(res)); StringBuffer remarkSb = new StringBuffer(); remarkSb.append(targetName).append(".").append(methodName).append("報錯信息:").append(message); //截取remark if(remarkSb.toString().length() <= 1000){ sysLog.setRemark(remarkSb.toString()); }else{ sysLog.setRemark(remarkSb.toString().substring(0, 1000)); } sysLog.setCreateTime(new Date()); sysLog.setUpdateTime(new Date()); handleLog(sysLog); logger.debug("---------------AfterReturning結束--------------"); } /** * * Description: 異步記錄接口調用失敗的日志 * * @param systemLog * @author suoww * @date 2017-2-8 */ @Async public void handleLog(PartnerSystemLog sysLog){ //寫日志 int row = partnerSystemLogMapper.insertSelective(sysLog); logger.debug("------日志寫入行數:" + row); } /** * * Description: 對象轉map * * @param obj * @return * @throws Exception * @author suoww * @date 2017-2-8 */ public Map<String,Object> Obj2Map(Object obj) throws Exception{ Map<String,Object> map=new HashMap<String, Object>(); Field[] fields = obj.getClass().getDeclaredFields(); for(Field field:fields){ field.setaccessible(true); map.put(field.getName(), field.get(obj)); } return map; } /** * * Description: 獲取APP用戶ID * * @return * @author suoww * @date 2017-2-8 */ protected Long getAppUserId() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); if (null == session) { return null; } PartnerUser user = (PartnerUser) session.getAttribute("userInfo"); if (null == user) { return null; } return user.getId(); } /** * * Description: 獲取Mgr的用戶ID * * @return * @author suoww * @date 2017-2-8 */ protected Long getMgrUserId(){ ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal(); return user.getId(); }}@Aspect和@Component分別表示這是一個切面類、Spring要幫我實例化對象并管理
@Pointcut(XX) :使用SaveSysLog作為注解(annotation)的將作為切點,對應切面controllerAspect
1.@AfterReturning 表示切點后增強,即切入點的方法執行結束后,即執行切面中的增強代碼,但是不會改變原切入點方法返回值。下面具體說明
2.pointcut="controllerAspect()" ,returning="res" 表示切點和切面對應關系,一個方法上可以有多個切面,指定順序
參考:http://blog.csdn.net/rainbow702/article/details/52185827
3.下面的是反射獲取的參數,類名,方法名,入參key,參數value,注解對象,方法返回值
四:調用
controller中調用
/** * * Description: 接口:查詢場次下商品 * * @param channelId * @return * @author suoww * @date 2017-1-13 */ @RequestMapping("/queryGoodsUnderChannel") @ResponseBody @SaveSysLog(send=InterfaceTypeConst.SendType.HZ, url="/goods/api/queryGoodsUnderChannel.html", type=InterfaceTypeConst.InterfaceType.APP) public AjaxResult queryGoodsUnderChannel(@RequestParam(value = "channelId", required = true)Long channelId){ try{ List<PartnerChannelGoodsDto> list = partnerGoods2ChannelService.getChannelGoodsDto(channelId); logger.info("根據channelId:" + channelId + "獲取到的商品集合為" + JSONObject.toJSONString(list)); return AjaxResult.success(list, "成功獲取頻道下商品"); }catch(ResultCodeException e){ e.printStackTrace(); return AjaxResult.failed("校驗失?。?quot; + e.getMessage()); }catch(Exception e){ e.printStackTrace(); return AjaxResult.failed("系統異常:" + e.getMessage()); } }@SaveSysLog(send=InterfaceTypeConst.SendType.HZ, url="/goods/api/queryGoodsUnderChannel.html", type=InterfaceTypeConst.InterfaceType.APP)這里對應注解接口三個成員,send,url,type
queryGoodsUnderChannel 這個方法將整體作為一個切入點,結合@AfterReturning 在queryGoodsUnderChannel ()執行結束會,會進入到SysLogAspect.doAfter 執行一段代碼,記錄日志
五:測試
輸入http://localhost:8080/partner-app/goods/api/queryGoodsUnderChannel.html?channelId=17
入參中channelId=17
參數可以對應。
總結:權限控制,日志記錄應該使用AOP,注解方式實際使用時候比XML配置方式要省事很多。
新聞熱點
疑難解答