基于SSM框架通过Spring AOP实现Controller、Service日志管理
1.项目背景
需要在Web管理平台添加用户操作日志,采用Spring AOP的方式完成该功能
2.编写测试代码:
(1)数据库
CREATE TABLE `syslog` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`operName` varchar(200) DEFAULT NULL,
`operResult` varchar(200) DEFAULT NULL,
`operDate` varchar(200) DEFAULT NULL,
`operExceptionDesc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
(2)日志管理实体类
package com.ssm.entity;
public class SysLog {
private String operName;
private String operResult;
private String operDate;
private String operExceptionDesc;
public String getOperExceptionDesc() {
return operExceptionDesc;
}
public void setOperExceptionDesc(String operExceptionDesc) {
this.operExceptionDesc = operExceptionDesc;
}
public String getOperName() {
return operName;
}
public void setOperName(String operName) {
this.operName = operName;
}
public String getOperResult() {
return operResult;
}
public String getOperDate() {
return operDate;
}
public void setOperDate(String operDate) {
this.operDate = operDate;
}
public void setOperResult(String operResult) {
this.operResult = operResult;
}
@Override
public String toString() {
return "SysLog{" +
"operName='" + operName + '\'' +
", operResult='" + operResult + '\'' +
", operDate='" + operDate + '\'' +
", operExceptionDesc='" + operExceptionDesc + '\'' +
'}';
}
}
(3)通过自定义注解实现
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {
String operName() default "";
String operResult() default "";
}
(4)定义切面类,
@Aspect
@Service
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.ssm.service.SysLog)")
public void getSysLog(){
}
@AfterReturning("getSysLog()")
public void afterReturning(JoinPoint joinPoint){
handlerSysLog(joinPoint,null);
}
@AfterThrowing(value = "getSysLog()",throwing = "e")
public void afterReturning(JoinPoint joinPoint,Exception e){
handlerSysLog(joinPoint,e);
}
public void handlerSysLog(JoinPoint joinPoint,Exception e){
//整体思路:通过反射获取到目标方法对象,通过方法对象拿到注解,,再通过注解拿到注解属性值
SysLog sysLog = null;
String operName = "";
String operResult = "";
String operDate = "";
String operExceptionDesc = "";
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();
for(Method method:methods){
if(method.getName().equals(methodName)){
Class[] classes = method.getParameterTypes();
if(arguments.length == classes.length){
sysLog = method.getAnnotation(SysLog.class);
}
}
}
}catch (Exception ex){
ex.printStackTrace();
}
operName = sysLog.operName();
operDate = dateString(new Date());
if(e != null){
operResult = "异常";
operExceptionDesc = e.toString();
}else if(e == null){
operResult = "正常";
}
com.ssm.entity.SysLog entity = new com.ssm.entity.SysLog();
entity.setOperDate(operDate);
entity.setOperName(operName);
entity.setOperResult(operResult);
entity.setOperExceptionDesc(operExceptionDesc);
sysLogService.saveSysLog(entity);
}
public String dateString(Date date){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
return simpleDateFormat.format(date);
}
}
@AfterReturning:后置通知,方法正常执行结束后执行该方法
@AfterThrowing:后置异常通知:方法发生异常后执行该方法
本测试案例中,后置通知和后置异常通知调用的同一个方法,在方法内通过判断是否有异常来区分方法是正常执行结束还是发生了异常。
(4)日志操作mapper.xml
<mapper namespace="com.ssm.mapper.SysLogMapper" >
<insert id="saveSysLog" parameterType="sysLog">
insert into sysLog(operName,operResult,operDate,operExceptionDesc) values(#{operName},#{operResult},#{operDate},#{operExceptionDesc})
</insert>
</mapper>
(5)日志操作mapper.java
public interface SysLogMapper {
public void saveSysLog(SysLog sysLog);
}
(6)日志操作业务层接口
public interface SysLogService {
public void saveSysLog(SysLog sysLog);
}
业务层实现类
@Service
public class SysLogImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public void saveSysLog(SysLog sysLog) {
sysLogMapper.saveSysLog(sysLog);
}
}
(7)
Spring AOP的动态代理是通过JDK实现的,基于JDK实现的动态代理,被代理的类需要实现接口,在测试代码中,业务层实现了接口,可以直接使用@SysLog完成日志管理,业务层配置文件applicationContext_service.xml不需要做任何配置;
控制层没有实现接口,默认通过Cglib实现动态代理,需要在springmvc.xml配置文件中,让Spring AOP强制使用cglib代理,这样才能在控制层使用@SysLog
添加的配置如下:
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
(8)
控制层代码:
@SysLog(operName = "查看自身贷款及应还款查询")
@RequestMapping("queryLoanInfoByIdCard.action")
public ModelAndView queryLoanInfoByIdCard(HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
String idCard = ((User)request.getSession().getAttribute("user")).getIdCard();
List<LoanInfo> infos = userService.queryLoanInfoByIdCard(idCard);
modelAndView.addObject("infos", infos);
modelAndView.setViewName("personalAllLoanInfo");
String a = null;
System.out.println(a.length());
return modelAndView;
}
业务层代码
SysLog(operName = "业务层")
@Override
public List<LoanInfo> queryLoanInfoByIdCard(String idCard) {
List<LoanInfo> list = userMapper.queryLoanInfoByIdCard(idCard);
logger.info("根据身份证号查询到的已贷款信息:"+list);
return list;
}
(9)代码执行后,数据库数据如下:
在后置通知和后置异常通知中,主要思路是:通过反射拿到类对象,通过类对象拿到所有方法,获取有指定注解的方法,再获取注解属性值,这样就可以在数据库中记录用户操作了哪些方法。