文档章节

SSM框架_4(添加log日志管理(aop)+Exception异常统一处理(aop))

开源小菜鸟2333
 开源小菜鸟2333
发布于 2017/05/19 19:01
字数 1643
阅读 753
收藏 2

2017-05-15(添加log日志管理(aop)+Exception异常统一处理(aop))

github版本号 b3a44f8a7a4452fd28bf2c4562a3e2a6aa7221dc

1、添加log日志管理(aop)

1.1 Log.java:定义Log注解,Target为类和方法上

package com.ssm.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

	/**
	 * 操作事件,idea test
	 */
	String value();
	/**
	 * 字段组装描述内容,
	 * 如{"name=名称","status=状态,1=成功;2=失败"},
	 * 表单参数为:name=张三&status=1这样生成的描述信息为:
	 * 名称=张三,状态=成功
	 */
	String[] entry() default {};
}

1.2 OperLog.java:model类,即日志

package com.ssm.model;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/***
 * @description 对应t_log表
 * */
@Data
public class OperLog implements Serializable {

	private static final long serialVersionUID = -8690056878905494181L;

	private Long id;
	private String userId;// '操作用户ID',
	private String userName;// '操作人名称',
	@JSONField (format="yyyy-MM-dd HH:mm:ss") 
	private Date operTime;// '操作时间(yyyy-MM-dd HH:mm:ss)',
	private String clientIp;// '客户端IP',
	private String reqUrl;// 访问url
	private String method;// 请求方法
	private String operEvent;// 操作事件(删除,新增,修改,查询,登录,退出)',
	private int operStatus;// '操作状态(1:成功,2:失败)',
	private String logDesc;// 描述信息',
}

1.3 LogAspect.java:日志切面

Pointcut为匹配含有Log注解的类和方法,同时将某些重要参数,比如操作用户ID、操作人名称(如果已登录的话)、操作时间(yyyy-MM-dd HH:mm:ss)、客户端IP、访问url、请求方法、操作事件(删除,新增,修改,查询,登录,退出等,即Log注解中的value的值)、操作状态、操作状态等存到数据库中。

package com.ssm.annotation;

import com.ssm.model.OperLog;
import com.ssm.model.User;
import com.ssm.service.LogService;
import com.ssm.service.UserService;
import com.ssm.utils.ConstantVar;
import com.ssm.utils.IPAddressUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 日志切面
 */
@Aspect
@Component
public class LogAspect {
	@Autowired
	private HttpServletRequest request;

	@Autowired
	private UserService userService;

	// 注入Service用于把日志保存数据库
	@Autowired
	private LogService logService;

	private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

	// Controller层切点
	@Pointcut("@annotation(com.ssm.annotation.Log)") //@annotation用于匹配当前执行方法持有指定注解的方法;
	public void logAspect() {
	}

	/**
	 * 后置通知 用于拦截Controller层记录用户的操作
	 *
	 * @param joinPoint
	 *            切点
     * @param rvt
     *            指定一个 returning 属性,该属性值为 rvt , 表示 允许在 增强处理方法中使用名为rvt的形参,该形参代表目标方法的返回值。
	 */
	@AfterReturning(returning = "rvt", pointcut = "logAspect()")
	public void after(JoinPoint joinPoint, Object rvt) {
		try {
			String targetName = joinPoint.getTarget().getClass().getName(); // 请求类名称
			String methodName = joinPoint.getSignature().getName(); // 请求方法
			Object[] arguments = joinPoint.getArgs();
			Class<?> targetClass = Class.forName(targetName);
			Method[] methods = targetClass.getMethods();
			String value = "";
			StringBuffer descr = new StringBuffer();
			for (Method method : methods) {
				if (method.getName().equals(methodName)) {
					@SuppressWarnings("rawtypes")
					Class[] clazzs = method.getParameterTypes();
					if (clazzs.length == arguments.length) {
						if(method.getAnnotation(Log.class) != null){ // 如果包含注解@log()
							value = method.getAnnotation(Log.class).value();
							String[] anEntry = method.getAnnotation(Log.class).entry();
							for (String en : anEntry) {
								String[] entry = en.split(",");
								String[] nameArray = entry[0].split("=");
								String val = StringUtils.defaultString(request.getParameter(nameArray[0]), "");
								if (!StringUtils.isBlank(val)) {
									if (entry.length == 2) {
										String[] valueEntry = entry[1].split(";");
										for (String valueArray : valueEntry) {
											String[] vals = valueArray.split("=");
											if (vals[0].equalsIgnoreCase(val)) {
												val = vals[1];
												break;
											}
										}
									}
									descr.append(',');
									descr.append(nameArray[1]);
									descr.append('=');
									descr.append(val);
								}
							}
							if (descr.length() > 0) {
								descr.deleteCharAt(0);
							}
							break;
						}
					}
				}
			}
			OperLog operLog = new OperLog();

			if (request.getRequestURI().contains("/login") && "loginPost".equalsIgnoreCase(joinPoint.getSignature().getName())) {
				// 用户登录日志记录
				operLog.setUserId(request.getParameter("username"));
				Subject curUser = SecurityUtils.getSubject();
				User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER);
				if (loginUser != null) {
					operLog.setUserId(loginUser.getId());
					operLog.setUserName(loginUser.getUserName());
					operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_SUCCESS);
				} else {
					operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_FAIL);
				}
			}else if (request.getRequestURI().contains("/logout")
					&& "logout".equalsIgnoreCase(joinPoint.getSignature().getName())) {
				// 退出日志
				String userId = (String) arguments[0];
				operLog.setUserId(userId);
				
		        User loginUser = userService.findUserByUserId(userId);
				operLog.setUserName(loginUser.getUserName());
			} else {
				Subject curUser = SecurityUtils.getSubject();
				if(curUser.getPrincipal()!=null){
					//从session中获取当前登录用户的User对象
					User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER);
					operLog.setUserName(loginUser.getUserName());
					operLog.setUserId(loginUser.getId());
				}

			}
			if(new Integer(operLog.getOperStatus())!=null){
                operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_SUCCESS);
            }
            operLog.setClientIp(IPAddressUtil.getIpAddress(request));
            operLog.setReqUrl(request.getRequestURI());
            joinPoint.getSignature();
            operLog.setMethod(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName());
            operLog.setOperEvent(value);
            operLog.setLogDesc("该方法实际入参为:"+descr.toString()); // 描述信息
			// 保存数据库
			logService.insertLog(operLog);
		} catch (Exception e) {
			// 记录本地异常日志
			logger.error("后置通知异常:异常信息:", e.getMessage());
			e.printStackTrace();
		}
	}
}

1.4 spring-mvc.xml:添加aop相关配置

<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 日志注解 -->
<bean id="LogAspect" class="com.ssm.annotation.LogAspect"/>

1.5 IndexController.IndexController:Log注解的实际应用


@Controller
public class IndexController {

	@Log(value = "进入guest", entry = { "parameter1=参数1","parameter2=参数2", })
	@RequestMapping(value = "/guest", method = RequestMethod.GET)
	public String guest(Model model,String parameter1,Integer parameter2) {
		return "guest/guestIndex";
	}
	
}

2、Exception异常统一处理(aop)

2.1ExceptionHandler.java:aop

异常处理,除常规日志字段外,还将 具体错误信息Exception类型该方法实际入参都保存到数据库中

package com.ssm.aop;

import com.ssm.annotation.Log;
import com.ssm.model.OperLog;
import com.ssm.model.User;
import com.ssm.service.LogService;
import com.ssm.utils.IPAddressUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.aop.ThrowsAdvice;
import com.ssm.utils.ConstantVar;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * aop:异常处理
 */
public class ExceptionHandler implements ThrowsAdvice {
    private static final Logger LOG = Logger.getLogger(ExceptionHandler.class);

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private LogService logService;

    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        LOG.error("出现Exception:url为" + request.getRequestURI() + ";错误类型为"+e.getStackTrace()[0]+"");
        OperLog operLog = new OperLog();
        StringBuffer operEvent = new StringBuffer();
        String descr4Exception = "";   // 具体错误信息

        try {
            String targetName = joinPoint.getTarget().getClass().getName(); // 请求类名称
            String methodName = joinPoint.getSignature().getName(); // 请求方法
            Object[] arguments = joinPoint.getArgs();
            Class<?> targetClass = null;
            targetClass = Class.forName(targetName);

            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        if(method.getAnnotation(Log.class) != null){ // 如果包含注解@log()
                            operEvent.append(method.getAnnotation(Log.class).value());
                            operEvent.append("。");
                            break;
                        }
                    }
                }
            }
            operEvent.append("该方法实际入参为:");
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                operEvent.append(joinPoint.getArgs()[i]);
                operEvent.append(",");
            }
            operEvent.deleteCharAt(operEvent.length()-1); //删除最后一个 ","
            operEvent.append("。Exception类型为:");
            operEvent.append(e.getClass());
            descr4Exception = createExceptionDetail(e);

            Subject curUser = SecurityUtils.getSubject();
            if (request.getRequestURI().contains("/logout")
                    && "logout".equalsIgnoreCase(joinPoint.getSignature().getName())) {
                // 退出日志
                String userId = (String) arguments[0];
                operLog.setUserId(userId);
            }
            if(curUser.getPrincipal()!=null){
                //从session中获取当前登录用户的User对象
                User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER);
                operLog.setUserName(loginUser.getUserName());
                operLog.setUserId(loginUser.getId());
            }
            operLog.setClientIp(IPAddressUtil.getIpAddress(request));
        }catch (ClassNotFoundException e1) {
            e1.printStackTrace();
            LOG.error("实例化失败:ClassNotFoundException");
        }catch (IOException e2) {
            e2.printStackTrace();
            operLog.setClientIp("未知IP:IOException");
        }

        operLog.setReqUrl(request.getRequestURI());
        operLog.setMethod(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName());
        operLog.setOperEvent((operEvent.toString()).length()>255?(operEvent.toString()).substring(0,255):operEvent.toString());
        operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_FAIL);
        operLog.setLogDesc("具体Exception信息为:"+ descr4Exception);
        try{
            // 保存到数据库
            logService.insertLog(operLog);
        }catch (Exception ex){
            ex.printStackTrace();
            LOG.error("log保存数据库失败");
        }
    }

    /**
     * 异常数组转成字符串
     *
     * @param e
     * @return
     * @author
     * @2016-8-18 下午5:43:20
     */
    private String createExceptionDetail(Exception e) {
        StackTraceElement[] stackTraceArray = e.getStackTrace();
        StringBuilder detail = new StringBuilder();
        for (int i = 0; i < stackTraceArray.length; i++) {
            //255位,此处是考虑数据库相应字段的大小限制
            if((detail.toString()+stackTraceArray[i]).length() > 255){
                return detail.toString();
            }
            detail.append(stackTraceArray[i] + "\r\n");
        }
        return detail.toString();
    }
}

2.2 spring-mvc.xml:添加aop相关配置

<!-- 日志注解 -->
<bean id="LogAspect" class="com.ssm.annotation.LogAspect"/>

<!-- 异常捕获aop -->
<bean id="exceptionHandler" class="com.ssm.aop.ExceptionHandler" />

<aop:config>
	<aop:aspect ref="exceptionHandler">
		<aop:pointcut id="exceptionService" expression="execution(* com.ssm.*.*.*(..))" />
		<aop:after-throwing pointcut-ref="exceptionService" method="afterThrowing" throwing="e"/>
	</aop:aspect>
</aop:config>

3 相关日志

3.1 为方便测试,利用guestError方法来抛出异常

@Log(value = "进入guest,此处模拟抛出异常")
@RequestMapping(value = "/guestError", method = RequestMethod.GET)
public String guestError(Model model) {
	LOG.info("进入guest的index");
	if(true) {
		throw new RuntimeException();
	}
	return "guest/guestIndex";
}

3.2 当url为 http://127.0.0.1:8080/项目名/guestError,数据库插入数据为

数据库异常日志_1

数据库异常日志_2

数据库异常日志_3

3.3 未登录前访问,与登录后访问所留下的日志记录

控制台日志

数据库日志

4、相关参考链接

Spring aop 实现异常拦截 - 涂墨留香 - 博客园

利用spring aop统一处理异常和打日志 - Ray的专栏 - 博客频道 - CSDN.NET

基于spring注解AOP的异常处理 - 小眼儿 - 博客园

© 著作权归作者所有

开源小菜鸟2333
粉丝 12
博文 38
码字总数 28926
作品 0
杭州
私信 提问
Spring AOP 的实现方式(以日志管理为例)

Spring AOP 的实现方式(以日志管理为例) 2016年10月08日 00:13:57 阅读数:23198 在学习Spring框架的历程中,最重要的是要理解Spring的IOC和AOP了,不但要学会怎么用,最好是知道它是怎么实...

Jeam_
2018/07/04
0
0
Spring核心技术之Ioc和AOP

一、Spring应用环境的配置: 1. 在classpath中添加以下包: 1) SPRING_HOME/dist/spring.jar、 2) SPRING_HOME/lib/jakarta-commons/commons-logging.jar 3) SPRING_HOME/lib/log4j/log4j-1.......

商者
2016/03/30
12
0
SpringBoot | 第二十四章:日志管理之AOP统一日志

前言 上一章节,介绍了目前开发中常见的及日志框架的整合知识。在很多时候,我们在开发一个系统时,不管出于何种考虑,比如是审计要求,或者防抵赖,还是保留操作痕迹的角度,一般都会有个全...

oKong
2018/08/24
0
4
简单理解AOP(面向切面编程)

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。   AOP与OOP是面向不同领域的两种设计思想。   OOP(...

lisn
2015/07/01
0
0
陆地小神仙/jfast

JFast : Java Web MVC开发框架 + Restful服务端框架 全原创设计, 全新构思, 比使用普通SSH或者SSM开发效率提高5-10倍,如果真正使用Jfast进行开发,速度远超SSH,JFinal。 使用教程:参见 http...

陆地小神仙
2015/06/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

C 语言 二级指针操作文件 柔性数组使用

#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct _info* pInfo;struct _info{int line;int len;char data[0];}info;int getFil......

小张525
39分钟前
0
0
中介者模式

https://blog.csdn.net/jason0539/article/details/45216585

南桥北木
39分钟前
0
0
抽离css以及公共js

分离css 分离css:为何要把 CSS 文件分离出来,而不是直接一起打包在 JS 中。最主要的原因是我们希望更好地利用缓存。 extract-text-webpack-plugin > 1. 假设我们原本页面的静态资源都打包成...

莫西摩西
今天
2
0
Jenkins的配置从节点中默认没有Launch agent via Java Web Start,该如何配置使用

Jenkins的配置从节点中默认没有Launch agent via Java Web Start,如下图所示,而这种启动方式在Windows上是最方便的。 如何设置才能让出来呢? 1:打开"系统管理"——"Configure Global Sec...

shzwork
今天
2
0
BAT面试必问HashMap源码分析

HashMap 简介 HashMap 主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一。 JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈...

别打我会飞
今天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部