背景:項目是老項目,而且比較舊為springmvc項目。項目使用的框架為公司內部自己開發,目前已經沒有框架的源碼可供修改,配置文件寫在底層框架內,可以看到但修改不到。
目的是為了實現日志記錄功能:
底層框架已經配置了aop 在ApplicationContext.xml
<aop:aspectj-autoproxy proxy-target-class="true" />
配置自動掃描包在
applicationContext-servlet.xml
<context:component-scan base-package="com.xxx"></context:component-scan>
自定義注解類
**
* 自定義操作日志記錄注解
*
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模塊
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人類別
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存請求的參數
*/
public boolean isSaveRequestData() default true;
}
自定義切面類
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 配置織入點
@Pointcut("within(com.transin..*) && @annotation(com.transin.waste.annotation.Log)")
public void logPointCut()
{
}
/**
* 處理完請求后執行
*
* @param joinPoint 切點
*/
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
{
handleLog(joinPoint, null, jsonResult);
}
/**
* 攔截異常操作
*
* @param joinPoint 切點
* @param e 異常
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e)
{
handleLog(joinPoint, e, null);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
{
try
{
// 獲得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// 獲取當前的用戶
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
// *========數據庫日志=========*//
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 請求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip);
// 返回參數
operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(e.getMessage().substring(0, 2000));
}
// 設置方法名稱
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 設置請求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 處理設置注解上的參數
getControllerMethodDescription(joinPoint, controllerLog, operLog);
// 保存數據庫
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
catch (Exception exp)
{
// 記錄本地異常日志
log.error("==前置通知異常==");
log.error("異常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 獲取注解中對方法的描述信息 用于Controller層注解
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception
{
// 設置action動作
operLog.setBusinessType(log.businessType().ordinal());
// 設置標題
operLog.setTitle(log.title());
// 設置操作人類別
operLog.setOperatorType(log.operatorType().ordinal());
// 是否需要保存request,參數和值
if (log.isSaveRequestData())
{
// 獲取參數的信息,傳入到數據庫中。
setRequestValue(joinPoint, operLog);
}
}
/**
* 獲取請求的參數,放到log中
*
* @param operLog 操作日志
* @throws Exception 異常
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
{
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
{
String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(params.substring(0, 2000));
}
else
{
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(paramsMap.toString().substring( 0, 2000));
}
}
/**
* 是否存在注解,如果存在就獲取
*/
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(Log.class);
}
return null;
}
/**
* 參數拼裝
*/
private String argsArrayToString(Object[] paramsArray)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
{
for (int i = 0; i < paramsArray.length; i++)
{
if (!isFilterObject(paramsArray[i]))
{
Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " ";
}
}
}
return params.trim();
}
/**
* 判斷是否需要過濾的對象。
*
* @param o 對象信息。
* @return 如果是需要過濾的對象,則返回true;否則返回false。
*/
public boolean isFilterObject(final Object o)
{
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
}
}
測試方法
@ResponseBody
@RequestMapping("/change_pwd")
@Log(title = "密碼修改",businessType = BusinessType.UPDATE)
public ResultVo changePwd(String pwd,HttpServletRequest request){
。。。。。。。。。。。。
}
如上配置之后發現怎么測試aop都無法成功。
做以下調整之后aop成功
1、在
applicationContext-servlet.mxl 添加如下配置
<context:component-scan base-package="com....."></context:component-scan>//切面所在包路徑
<aop:aspectj-autoproxy proxy-target-class="true" />
2、將切面類采用Bean的方法在配置文件進行注入,我是寫在applicationContext.mxl中
<bean id="logAspect" class="com.。。。。。"/>
做了以上兩步調整之后aop就可以成功了。
總結兩點
1、<aop:aspectj-autoproxy proxy-target-class="true" />要寫在SpringMVC的配置文件中。
2、如果還是不行就把切面類采用Bean的方法在配置文件進行注入。
以上內容僅個人實踐得來,有錯誤歡迎批評指正。