One:AspectJ实现日志增删改操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Sugar_map/article/details/80992763

 本文核心:AspectJ【Aop】实现对管理员操作的监视,并将日志信息记录在数据库中。难点,在Service层获取HttpSession。

这系列博客是记录自己做过项目学到知识。one、two代表项目编号。

前言:

后台管理系统

技术选型:ssm + easyui + ajax + js/jq + jsp + mysql + redis + webservice

工具版本:jdk1.8 + tomcat7/8 + mysql8.0.11 + idea2017 + spring4.1.13

项目规范:maven3.3.9 + git

一:AspectJ

1. AOP框架AspectJ,简化AOP代码(通过注解简化AOP)

2. AspactJ框架常用注解

@Aspect      //声明切面  【注解类上】

@Order(1)  //该注解用于指定多切面执行顺序(@Order 值越小优先级越高)

@PonitCut  // 声明切入点的注解

@Before    // 声明前置通知注解

@After     // 声明后置通知注解(原始方法执行正常或者非正常执行都会进入)

@AfterReturing // 声明后置通知注解(原始方法必须正常执行)

@AfterThrowing // 声明异常通知

@Around // 环绕通知注解

二:AspectJ实战 【记录增、删、改操作的日志】

日志需要记录的信息

	user : session.getAttribute("user");//管理名  存在于HttpSession中【难点所在】
	操作时间:sysdate() now()           //系统时间
	操作资源类型:xxxServiceImpl        //xxx
	动作:	   methodName     【add/update/delete】
	操作详细信息:method.getArgs();//方法的形参
	操作结果:success             //操作结果

Aop实现日志操作本身没有什么难点。但是日志要求的字段:日志记录信息有User,用户登录成功,Session存有用户信息。但是Session存在哪里?一般情况存在于Controller层中,Service层获取不到Session。

那么实现日志操作的难点:① Advice中的around(ProceedingJoinPoint pjp)获取日志需要的字段

                                           ②如何在Service层访问到HttpSession

解决:获取上述日志的需要的字段【对Service实现类和增删改方法的命名有一定的要求】

xxxServiceImpl   【业务层实现方法】

queryXXXXX  查询

addXXXXX   添加

modifyXXXXXX   更改

        /*设置代理功能
	* pjp.getArgs()->获取方法的参数信息
	*pjp.getSignature()->获取代理执行的方法
	*pjp.getTarget().toString()->获取代理类的全限定名  ->截取资源名
	* */

解决:Service层获取HttpSession。  18年-8月-1日【修改、完善】

第一种:ThreadLocal绑定Session。 【需要考虑绑定和移除时机,建议通过Filter拦截请求和响应完成】

第二种:RequestContextHolder 对象获取HttpServletRequest对象,间接获取HttpSession。

①:配置web.xml

        <listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>

②:通过 RequestContextHolder 获取Request对象,再通过Request对象获取Session对象

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

全部该功能涉及的源码及配置:

Spring 的配置

	<!--使用aspectJ 风格的Aop-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	<!--配置增强方法-->
	<bean id="myAdvice" class="com.baizhi.advice.MyAdvice"/>

日志实体类

public class Log implements Serializable {
	private  Integer id;
	private  String user;
	private  java.util.Date time;
	private  String resource;
	private  String action;
	private  String message;
	private  String result;
	
	
	public Log() {
	}
        //省略get/set方法
}

MyAdvice源码:

/**
 * @Description 切面方法
 * @Author Maps
 * @Time 2018/7/9 19:46
 * @Version 1.0
 */
@Aspect
public class MyAdvice {

	@Autowired
	private LogService logService;

	/*定义切点*/
	@Pointcut("execution(* com.baizhi.service.impl.*.add*(..))||execution(* com.baizhi.service.impl.*.modify*(..))")
	public void cut(){}
	
	/*设置代理功能
	* pjp.getArgs()->获取方法的参数信息
	*pjp.getSignature()->获取代理执行的方法
	*pjp.getTarget().toString()->获取代理类的全限定名  ->截取资源名
	* */
	@Around("cut()")
	public Object around(ProceedingJoinPoint pjp) {
		//声明日志对象
		Log log=new Log();
		//设置流程成功,失败回滚【默认】
		log.setResult("success");
		//设置消息 -->参数    操作结果、成功或失败
		Object[] args = pjp.getArgs();
		log.setMessage(args.toString());
		//设置操作名
		MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
		Method method = methodSignature.getMethod();
		String name= method.getName();//获取方法名
		if(name.startsWith("modify")){
			log.setAction("modify");
		}else{
			log.setAction("add");
		}
		//获取资源名
		String resource= pjp.getTarget().toString();
		int start=resource.lastIndexOf(".");
		int end=resource.lastIndexOf("ServiceImpl");
		log.setResource(resource.substring(start+1,end));
		//获取session中的用户名
		String managerName=(String) RequestContextHolder.currentRequestAttributes().getAttribute("managerName", RequestAttributes.SCOPE_SESSION);
		log.setUser(managerName);
		Object object=null;
		try {
			//调用原函数
			object=pjp.proceed();
		} catch (Throwable throwable) {
			log.setResult("fail");
			throwable.printStackTrace();
		}
		//写入日志
		logService.writeLog(log);
		return object;
	}
}

最后衷心提示Log日志添加信息操作的方法名,不要以add开头。因为我切入点选入的时Service层,注入了LogService来完成日志操作,会造成栈溢出。【因为logService的方法以add开头,符合切点表达式,会再次进入切面,无限递归】

@Pointcut("execution(* com.baizhi.service.impl.*.add*(..))||execution(* com.baizhi.service.impl.*.modify*(..))")

可以看出,我的切点表达式以方法名前缀做了区分。
 

猜你喜欢

转载自blog.csdn.net/Sugar_map/article/details/80992763