专栏首页Lambdaspring AOP日志管理

spring AOP日志管理

Spring AOP 完成日志记录

SpringAOPAspectJsecurity日志记录

Spring AOP 完成日志记录

1、技术目标

  • 掌握Spring AOP基本用法
  • 使用Spring AOP完成日志记录功能

2、什么是AOP

AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,AOP实际是GoF设计模式的延续

注意:关于AOP的详细介绍不是本文重点

3、关于Spring AOP的一些术语

  • 切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现
  • 连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行
  • 通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括"around"、"before”和"after"等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链
  • 切入点(Pointcut):定义出一个或一组方法,当执行这些方法时可产生通知,Spring缺省使用AspectJ切入点语法。

4、通知类型

  • 前置通知(@Before):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)
  • 返回后通知(@AfterReturning):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回
  • 抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知
  • 后通知(@After):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
  • 环绕通知(@Around):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行

5、@AspectJ风格的AOP配置

Spring AOP配置有两种风格:

  • XML风格 = 采用声明形式实现Spring AOP 
  • AspectJ风格 = 采用注解形式实现Spring AOP

注意:本文采用AspectJ风格

6、使用准备

闲话少说,下面开始日志记录的准备工作

6.1)创建日志记录表(MySQL),

Sql代码  

 CREATE TABLE `t_log` (  
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  
   `userid` bigint(20) unsigned NOT NULL,  
   `createdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建日期',  
   `content` varchar(8000) NOT NULL DEFAULT '' COMMENT '日志内容',  
   `operation` varchar(250) NOT NULL DEFAULT '' COMMENT '用户所做的操作',  
  PRIMARY KEY (`id`)  
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

Sql代码  

 CREATE TABLE `t_log` (  
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  
   `userid` bigint(20) unsigned NOT NULL,  
   `createdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建日期',  
   `content` varchar(8000) NOT NULL DEFAULT '' COMMENT '日志内容',  
   `operation` varchar(250) NOT NULL DEFAULT '' COMMENT '用户所做的操作',  
  PRIMARY KEY (`id`)  
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

6.2)在经过了Spring Security的权限验证后,可以从Security中获取到

登录管理员的帐号,而日志记录表t_log中存储的是管理员id,所以需要通

过管理员的帐号查询出管理员id,创建管理员POJO、Mapper、Service,

代码及配置如下:

管理员POJO类:

Java代码  

 package com.xxx.pojo;  
 public class Admin extends BaseDomain {  
  
  private String nickname;//管理员帐号 
  private String passwd;//管理员密码 
  private String phoneno;//联系电话 
  
  public String getNickname() {  
  return nickname;  
     }  
  public void setNickname(String nickname) {  
  this.nickname = nickname;  
     }  
  public String getPasswd() {  
  return passwd;  
     }  
  public void setPasswd(String passwd) {  
  this.passwd = passwd;  
     }  
  public String getPhoneno() {  
  return phoneno;  
     }  
  public void setPhoneno(String phoneno) {  
  this.phoneno = phoneno;  
     }  
 }  

Java代码  

 package com.xxx.pojo;  
 public class Admin extends BaseDomain {  
  
  private String nickname;//管理员帐号 
  private String passwd;//管理员密码 
  private String phoneno;//联系电话 
  
  public String getNickname() {  
  return nickname;  
     }  
  public void setNickname(String nickname) {  
  this.nickname = nickname;  
     }  
  public String getPasswd() {  
  return passwd;  
     }  
  public void setPasswd(String passwd) {  
  this.passwd = passwd;  
     }  
  public String getPhoneno() {  
  return phoneno;  
     }  
  public void setPhoneno(String phoneno) {  
  this.phoneno = phoneno;  
     }  
 }  

管理员Mapper接口与XML配置文件:

Java代码  

 package com.xxx.dao;  
  
 import com.xxx.pojo.Admin;  
 /** 
  * 管理员Mapper接口 
  */ 
 public interface AdminMapper {  
  /** 
      * 获取指定帐号名的管理员 
      */ 
  public Admin findAdminByNickname(String userName);  
 }  

Java代码  

 package com.xxx.dao;  
  
 import com.xxx.pojo.Admin;  
 /** 
  * 管理员Mapper接口 
  */ 
 public interface AdminMapper {  
  /** 
      * 获取指定帐号名的管理员 
      */ 
  public Admin findAdminByNickname(String userName);  
 }  

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
 <mapper namespace="com.xxx.dao.AdminMapper"> 
  <!-- 通过账号名称查询管理员 --> 
  <select id="findAdminByNickname" parameterType="string" resultType="Admin"> 
     select * from t_admin where nickname=#{userName}  
  </select> 
 </mapper> 

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
 <mapper namespace="com.xxx.dao.AdminMapper"> 
  <!-- 通过账号名称查询管理员 --> 
  <select id="findAdminByNickname" parameterType="string" resultType="Admin"> 
     select * from t_admin where nickname=#{userName}  
  </select> 
 </mapper> 

管理员Service接口与实现类:

Java代码  

 package com.xxx.service;  
  
 import com.xxx.pojo.Admin;  
  
 /** 
  * 管理员信息业务逻辑接口 
  */ 
 public interface AdminService {  
  /** 
      * 获取指定帐号名的管理员 
      */ 
  public Admin findAdminByNickname(String userName);  
 }  

Java代码  

 package com.xxx.service;  
  
 import com.xxx.pojo.Admin;  
  
 /** 
  * 管理员信息业务逻辑接口 
  */ 
 public interface AdminService {  
  /** 
      * 获取指定帐号名的管理员 
      */ 
  public Admin findAdminByNickname(String userName);  
 }  

Java代码  

 package com.xxx.service;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import com.xxx.dao.AdminMapper;  
 import com.xxx.pojo.Admin;  
  
 public class AdminServiceImpl implements AdminService {  
  @Autowired 
  private AdminMapper adminMapper;//Mapper接口 
  
  public Admin findAdminByNickname(String userName) {  
  return adminMapper.findAdminByNickname(userName);  
     }  
 }  

Java代码  

 package com.xxx.service;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import com.xxx.dao.AdminMapper;  
 import com.xxx.pojo.Admin;  
  
 public class AdminServiceImpl implements AdminService {  
  @Autowired 
  private AdminMapper adminMapper;//Mapper接口 
  
  public Admin findAdminByNickname(String userName) {  
  return adminMapper.findAdminByNickname(userName);  
     }  
 }  

6.3)创建日志记录POJO、Mapper、Service,代码及配置如下:

日志记录POJO类:

Java代码  

 package com.xxx.pojo;  
  
 import java.io.Serializable;  
 import java.util.Date;  
  
 /** 
  * 日志记录POJO 
  */ 
 public class Log extends BaseDomain implements Serializable{  
  
  private static final long serialVersionUID = 1024792477652984770L;  
  
  private Long userid;//管理员id 
  private Date createdate;//日期 
  private String content;//日志内容 
  private String operation;//操作(主要是"添加"、"修改"、"删除") 
  
  //getter、setter,此处省略N字(你懂的) 
 }  

Java代码  

 package com.xxx.pojo;  
  
 import java.io.Serializable;  
 import java.util.Date;  
  
 /** 
  * 日志记录POJO 
  */ 
 public class Log extends BaseDomain implements Serializable{  
  
  private static final long serialVersionUID = 1024792477652984770L;  
  
  private Long userid;//管理员id 
  private Date createdate;//日期 
  private String content;//日志内容 
  private String operation;//操作(主要是"添加"、"修改"、"删除") 
  
  //getter、setter,此处省略N字(你懂的) 
 }  

日志记录Mapper接口与XML配置文件:

Java代码  

 package com.xxx.dao;  
  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录Mapper 
  */ 
 public interface LogMapper {  
  
  public void insert(Log log);//添加日志记录 
 }  

Java代码  

 package com.xxx.dao;  
  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录Mapper 
  */ 
 public interface LogMapper {  
  
  public void insert(Log log);//添加日志记录 
 }  

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
 <mapper namespace="com.xxx.dao.LogMapper"> 
  <!-- 添加日志记录 --> 
  <insert id="insert" parameterType="Log"> 
         INSERT INTO t_log(userid,createdate,operation,content)  
         VALUES(#{userid},NOW(),#{operation},#{content});  
  </insert> 
 </mapper> 

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
 <mapper namespace="com.xxx.dao.LogMapper"> 
  <!-- 添加日志记录 --> 
  <insert id="insert" parameterType="Log"> 
         INSERT INTO t_log(userid,createdate,operation,content)  
         VALUES(#{userid},NOW(),#{operation},#{content});  
  </insert> 
 </mapper> 

日志记录Service接口与实现类:

Java代码  

 package com.xxx.service;  
  
 import org.springframework.transaction.annotation.Transactional;  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录业务逻辑接口 
  */ 
 public interface LogService {  
  
  /** 
      * 日志记录 
      * @param log 
      */ 
  @Transactional 
  public void log(Log log);  
  
  /** 
      * 获取登录管理员ID 
      */ 
  public Long loginUserId();  
 }  

Java代码  

 package com.xxx.service;  
  
 import org.springframework.transaction.annotation.Transactional;  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录业务逻辑接口 
  */ 
 public interface LogService {  
  
  /** 
      * 日志记录 
      * @param log 
      */ 
  @Transactional 
  public void log(Log log);  
  
  /** 
      * 获取登录管理员ID 
      */ 
  public Long loginUserId();  
 } 

Java代码  

 package com.xxx.service;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.security.core.context.SecurityContextHolder;  
 import org.springframework.security.core.userdetails.UserDetails;  
 import com.xxx.dao.LogMapper;  
 import com.xxx.pojo.Admin;  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录业务逻辑接口实现类 
  * @author HotStrong 
  */ 
 public class LogServiceImpl implements LogService{  
  
  @Autowired 
  private AdminService adminService;  
  
  @Autowired 
  private LogMapper logMapper;  
  
  public void log(Log log) {  
         logMapper.insert(log);  
     }  
  
  /** 
      * 获取登录管理员ID 
      *  
      * @return 
      */ 
  public Long loginUserId() {  
  
  if(SecurityContextHolder.getContext() == null){  
  return null;  
         }  
  
  if(SecurityContextHolder.getContext().getAuthentication() == null){  
  return null;  
         }  
  
         UserDetails userDetails = (UserDetails) SecurityContextHolder  
                 .getContext().getAuthentication().getPrincipal();  
  
  if(userDetails == null){  
  return null;  
         }  
  
  //获取登录管理员帐号名 
         String userName = userDetails.getUsername();  
  
  if(userName == null || userName.equals("")){  
  return null;  
         }  
  
  // 根据管理员帐号名获取帐号ID 
         Admin admin = this.adminService.findAdminByNickname(userName);  
  
  if(admin == null){  
  return null;  
         }  
  
  return admin.getId();  
     }  
 }  

Java代码  

 package com.xxx.service;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.security.core.context.SecurityContextHolder;  
 import org.springframework.security.core.userdetails.UserDetails;  
 import com.xxx.dao.LogMapper;  
 import com.xxx.pojo.Admin;  
 import com.xxx.pojo.Log;  
  
 /** 
  * 日志记录业务逻辑接口实现类 
  * @author HotStrong 
  */ 
 public class LogServiceImpl implements LogService{  
  
  @Autowired 
  private AdminService adminService;  
  
  @Autowired 
  private LogMapper logMapper;  
  
  public void log(Log log) {  
         logMapper.insert(log);  
     }  
  
  /** 
      * 获取登录管理员ID 
      *  
      * @return 
      */ 
  public Long loginUserId() {  
  
  if(SecurityContextHolder.getContext() == null){  
  return null;  
         }  
  
  if(SecurityContextHolder.getContext().getAuthentication() == null){  
  return null;  
         }  
  
         UserDetails userDetails = (UserDetails) SecurityContextHolder  
                 .getContext().getAuthentication().getPrincipal();  
  
  if(userDetails == null){  
  return null;  
         }  
  
  //获取登录管理员帐号名 
         String userName = userDetails.getUsername();  
  
  if(userName == null || userName.equals("")){  
  return null;  
         }  
  
  // 根据管理员帐号名获取帐号ID 
         Admin admin = this.adminService.findAdminByNickname(userName);  
  
  if(admin == null){  
  return null;  
         }  
  
  return admin.getId();  
     }  
 }  

7、在MyBatis配置文件mybatis-config.xml中配置POJO,如下:

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE configuration  
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
     "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
 <configuration> 
  <settings> 
  <!-- changes from the defaults --> 
  <setting name="lazyLoadingEnabled" value="false" /> 
  </settings> 
  <typeAliases> 
  <typeAlias alias="Film" type="com.xxx.pojo.Film"/> 
  <typeAlias alias="Admin" type="com.xxx.pojo.Admin"/> 
  <typeAlias alias="Log" type="com.xxx.pojo.Log"/> 
  </typeAliases> 
 </configuration> 

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE configuration  
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
     "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
 <configuration> 
  <settings> 
  <!-- changes from the defaults --> 
  <setting name="lazyLoadingEnabled" value="false" /> 
  </settings> 
  <typeAliases> 
  <typeAlias alias="Film" type="com.xxx.pojo.Film"/> 
  <typeAlias alias="Admin" type="com.xxx.pojo.Admin"/> 
  <typeAlias alias="Log" type="com.xxx.pojo.Log"/> 
  </typeAliases> 
 </configuration> 

8、创建aop包,在aop包下创建切面类LogAspect

Java代码  

 package com.xxx.aop;  
  
 import java.lang.reflect.Method;  
 import java.util.Date;  
  
 import org.aspectj.lang.JoinPoint;  
 import org.aspectj.lang.ProceedingJoinPoint;  
 import org.aspectj.lang.annotation.AfterReturning;  
 import org.aspectj.lang.annotation.Around;  
 import org.aspectj.lang.annotation.Aspect;  
 import org.aspectj.lang.annotation.Pointcut;  
 import org.springframework.beans.factory.annotation.Autowired;  
  
 import com.xxx.pojo.Film;  
 import com.xxx.pojo.Log;  
 import com.xxx.service.FilmService;  
 import com.xxx.service.LogService;  
  
 /** 
  * 日志记录,添加、删除、修改方法AOP 
  * @author HotStrong 
  *  
  */ 
 @Aspect 
 public class LogAspect {  
  
  @Autowired 
  private LogService logService;//日志记录Service 
  
  @Autowired 
  private FilmService filmService;//影片Service 
  
  /** 
      * 添加业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.*.insert*(..))")  
  public void insertServiceCall() { }  
  
  /** 
      * 修改业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.*.update*(..))")  
  public void updateServiceCall() { }  
  
  /** 
      * 删除影片业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.FilmService.deleteFilm(..))")  
  public void deleteFilmCall() { }  
  
  /** 
      * 管理员添加操作日志(后置通知) 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @AfterReturning(value="insertServiceCall()", argNames="rtv", returning="rtv")  
  public void insertServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable{  
  
  //获取登录管理员id 
         Long adminUserId = logService.loginUserId();  
  
  if(adminUserId == null){//没有管理员登录 
  return;  
         }  
  
  //判断参数 
  if(joinPoint.getArgs() == null){//没有参数 
  return;  
         }  
  
  //获取方法名 
         String methodName = joinPoint.getSignature().getName();  
  
  //获取操作内容 
         String opContent = adminOptionContent(joinPoint.getArgs(), methodName);  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent(opContent);//操作内容 
         log.setOperation("添加");//操作 
  
         logService.log(log);//添加日志 
     }  
  
  /** 
      * 管理员修改操作日志(后置通知) 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @AfterReturning(value="updateServiceCall()", argNames="rtv", returning="rtv")  
  public void updateServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable{  
  
  //获取登录管理员id 
         Long adminUserId = logService.loginUserId();  
  
  if(adminUserId == null){//没有管理员登录 
  return;  
         }  
  
  //判断参数 
  if(joinPoint.getArgs() == null){//没有参数 
  return;  
         }  
  
  //获取方法名 
         String methodName = joinPoint.getSignature().getName();  
  
  //获取操作内容 
         String opContent = adminOptionContent(joinPoint.getArgs(), methodName);  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent(opContent);//操作内容 
         log.setOperation("修改");//操作 
  
         logService.log(log);//添加日志 
     }  
  
  /** 
      * 管理员删除影片操作(环绕通知),使用环绕通知的目的是 
      * 在影片被删除前可以先查询出影片信息用于日志记录 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @Around(value="deleteFilmCall()", argNames="rtv")  
  public Object deleteFilmCallCalls(ProceedingJoinPoint pjp) throws Throwable {  
  
         Object result = null;  
  //环绕通知处理方法 
  try {  
  
  //获取方法参数(被删除的影片id) 
             Integer id = (Integer)pjp.getArgs()[0];  
             Film obj = null;//影片对象 
  if(id != null){  
  //删除前先查询出影片对象 
                 obj = filmService.getFilmById(id);  
             }  
  
  //执行删除影片操作 
             result = pjp.proceed();  
  
  if(obj != null){  
  
  //创建日志对象 
                 Log log = new Log();  
                 log.setUserid(logService.loginUserId());//用户编号 
                 log.setCreatedate(new Date());//操作时间 
  
                 StringBuffer msg = new StringBuffer("影片名 : ");  
                 msg.append(obj.getFname());  
                 log.setContent(msg.toString());//操作内容 
  
                 log.setOperation("删除");//操作 
  
                 logService.log(log);//添加日志 
             }  
  
          }  
  catch(Exception ex) {  
             ex.printStackTrace();  
          }  
  
  return result;  
     }  
  
  /** 
      * 使用Java反射来获取被拦截方法(insert、update)的参数值, 
      * 将参数值拼接为操作内容 
      */ 
  public String adminOptionContent(Object[] args, String mName) throws Exception{  
  
  if (args == null) {  
  return null;  
         }  
  
         StringBuffer rs = new StringBuffer();  
         rs.append(mName);  
         String className = null;  
  int index = 1;  
  // 遍历参数对象 
  for (Object info : args) {  
  
  //获取对象类型 
             className = info.getClass().getName();  
             className = className.substring(className.lastIndexOf(".") + 1);  
             rs.append("[参数" + index + ",类型:" + className + ",值:");  
  
  // 获取对象的所有方法 
             Method[] methods = info.getClass().getDeclaredMethods();  
  
  // 遍历方法,判断get方法 
  for (Method method : methods) {  
  
                 String methodName = method.getName();  
  // 判断是不是get方法 
  if (methodName.indexOf("get") == -1) {// 不是get方法 
  continue;// 不处理 
                 }  
  
                 Object rsValue = null;  
  try {  
  
  // 调用get方法,获取返回值 
                     rsValue = method.invoke(info);  
  
  if (rsValue == null) {//没有返回值 
  continue;  
                     }  
  
                 } catch (Exception e) {  
  continue;  
                 }  
  
  //将值加入内容中 
                 rs.append("(" + methodName + " : " + rsValue + ")");  
             }  
  
             rs.append("]");  
  
             index++;  
         }  
  
  return rs.toString();  
     }  
  
 }  

Java代码  

 package com.xxx.aop;  
  
 import java.lang.reflect.Method;  
 import java.util.Date;  
  
 import org.aspectj.lang.JoinPoint;  
 import org.aspectj.lang.ProceedingJoinPoint;  
 import org.aspectj.lang.annotation.AfterReturning;  
 import org.aspectj.lang.annotation.Around;  
 import org.aspectj.lang.annotation.Aspect;  
 import org.aspectj.lang.annotation.Pointcut;  
 import org.springframework.beans.factory.annotation.Autowired;  
  
 import com.xxx.pojo.Film;  
 import com.xxx.pojo.Log;  
 import com.xxx.service.FilmService;  
 import com.xxx.service.LogService;  
  
 /** 
  * 日志记录,添加、删除、修改方法AOP 
  * @author HotStrong 
  *  
  */ 
 @Aspect 
 public class LogAspect {  
  
  @Autowired 
  private LogService logService;//日志记录Service 
  
  @Autowired 
  private FilmService filmService;//影片Service 
  
  /** 
      * 添加业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.*.insert*(..))")  
  public void insertServiceCall() { }  
  
  /** 
      * 修改业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.*.update*(..))")  
  public void updateServiceCall() { }  
  
  /** 
      * 删除影片业务逻辑方法切入点 
      */ 
  @Pointcut("execution(* com.xxx.service.FilmService.deleteFilm(..))")  
  public void deleteFilmCall() { }  
  
  /** 
      * 管理员添加操作日志(后置通知) 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @AfterReturning(value="insertServiceCall()", argNames="rtv", returning="rtv")  
  public void insertServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable{  
  
  //获取登录管理员id 
         Long adminUserId = logService.loginUserId();  
  
  if(adminUserId == null){//没有管理员登录 
  return;  
         }  
  
  //判断参数 
  if(joinPoint.getArgs() == null){//没有参数 
  return;  
         }  
  
  //获取方法名 
         String methodName = joinPoint.getSignature().getName();  
  
  //获取操作内容 
         String opContent = adminOptionContent(joinPoint.getArgs(), methodName);  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent(opContent);//操作内容 
         log.setOperation("添加");//操作 
  
         logService.log(log);//添加日志 
     }  
  
  /** 
      * 管理员修改操作日志(后置通知) 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @AfterReturning(value="updateServiceCall()", argNames="rtv", returning="rtv")  
  public void updateServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable{  
  
  //获取登录管理员id 
         Long adminUserId = logService.loginUserId();  
  
  if(adminUserId == null){//没有管理员登录 
  return;  
         }  
  
  //判断参数 
  if(joinPoint.getArgs() == null){//没有参数 
  return;  
         }  
  
  //获取方法名 
         String methodName = joinPoint.getSignature().getName();  
  
  //获取操作内容 
         String opContent = adminOptionContent(joinPoint.getArgs(), methodName);  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent(opContent);//操作内容 
         log.setOperation("修改");//操作 
  
         logService.log(log);//添加日志 
     }  
  
  /** 
      * 管理员删除影片操作(环绕通知),使用环绕通知的目的是 
      * 在影片被删除前可以先查询出影片信息用于日志记录 
      * @param joinPoint 
      * @param rtv 
      * @throws Throwable 
      */ 
  @Around(value="deleteFilmCall()", argNames="rtv")  
  public Object deleteFilmCallCalls(ProceedingJoinPoint pjp) throws Throwable {  
  
         Object result = null;  
  //环绕通知处理方法 
  try {  
  
  //获取方法参数(被删除的影片id) 
             Integer id = (Integer)pjp.getArgs()[0];  
             Film obj = null;//影片对象 
  if(id != null){  
  //删除前先查询出影片对象 
                 obj = filmService.getFilmById(id);  
             }  
  
  //执行删除影片操作 
             result = pjp.proceed();  
  
  if(obj != null){  
  
  //创建日志对象 
                 Log log = new Log();  
                 log.setUserid(logService.loginUserId());//用户编号 
                 log.setCreatedate(new Date());//操作时间 
  
                 StringBuffer msg = new StringBuffer("影片名 : ");  
                 msg.append(obj.getFname());  
                 log.setContent(msg.toString());//操作内容 
  
                 log.setOperation("删除");//操作 
  
                 logService.log(log);//添加日志 
             }  
  
          }  
  catch(Exception ex) {  
             ex.printStackTrace();  
          }  
  
  return result;  
     }  
  
  /** 
      * 使用Java反射来获取被拦截方法(insert、update)的参数值, 
      * 将参数值拼接为操作内容 
      */ 
  public String adminOptionContent(Object[] args, String mName) throws Exception{  
  
  if (args == null) {  
  return null;  
         }  
  
         StringBuffer rs = new StringBuffer();  
         rs.append(mName);  
         String className = null;  
  int index = 1;  
  // 遍历参数对象 
  for (Object info : args) {  
  
  //获取对象类型 
             className = info.getClass().getName();  
             className = className.substring(className.lastIndexOf(".") + 1);  
             rs.append("[参数" + index + ",类型:" + className + ",值:");  
  
  // 获取对象的所有方法 
             Method[] methods = info.getClass().getDeclaredMethods();  
  
  // 遍历方法,判断get方法 
  for (Method method : methods) {  
  
                 String methodName = method.getName();  
  // 判断是不是get方法 
  if (methodName.indexOf("get") == -1) {// 不是get方法 
  continue;// 不处理 
                 }  
  
                 Object rsValue = null;  
  try {  
  
  // 调用get方法,获取返回值 
                     rsValue = method.invoke(info);  
  
  if (rsValue == null) {//没有返回值 
  continue;  
                     }  
  
                 } catch (Exception e) {  
  continue;  
                 }  
  
  //将值加入内容中 
                 rs.append("(" + methodName + " : " + rsValue + ")");  
             }  
  
             rs.append("]");  
  
             index++;  
         }  
  
  return rs.toString();  
     }  
  
 }  

9、对管理员登录操作进行日志记录

还记得《使用Spring Security实现权限管理》一文中第7步提到的两个类吗?其中LoginSuccessHandler类中可以记录管理员的登录操作,代码如下:

Java代码  

 package com.xxx.security;  
  
 import java.io.IOException;  
 import java.util.Date;  
  
 import javax.servlet.ServletException;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.security.core.Authentication;  
 import org.springframework.security.core.userdetails.UserDetails;  
 import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;  
  
 import com.xxx.pojo.Log;  
 import com.xxx.service.LogService;  
  
 /** 
  * 处理管理登录日志 
  * 
  */ 
 public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{  
  
  @Autowired 
  private LogService logService;//日志记录Service 
  
  @Override 
  public void onAuthenticationSuccess(HttpServletRequest request,  
             HttpServletResponse response, Authentication authentication) throws IOException,  
             ServletException {  
  
         UserDetails userDetails = (UserDetails)authentication.getPrincipal();  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent("管理员 " + userDetails.getUsername());//操作内容 
         log.setOperation("登录");//操作 
  
         logService.log(log);//添加日志 
  
  super.onAuthenticationSuccess(request, response, authentication);  
     }  
  
 }  

Java代码  

 package com.xxx.security;  
  
 import java.io.IOException;  
 import java.util.Date;  
  
 import javax.servlet.ServletException;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.security.core.Authentication;  
 import org.springframework.security.core.userdetails.UserDetails;  
 import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;  
  
 import com.xxx.pojo.Log;  
 import com.xxx.service.LogService;  
  
 /** 
  * 处理管理登录日志 
  * 
  */ 
 public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{  
  
  @Autowired 
  private LogService logService;//日志记录Service 
  
  @Override 
  public void onAuthenticationSuccess(HttpServletRequest request,  
             HttpServletResponse response, Authentication authentication) throws IOException,  
             ServletException {  
  
         UserDetails userDetails = (UserDetails)authentication.getPrincipal();  
  
  //创建日志对象 
         Log log = new Log();  
         log.setUserid(logService.loginUserId());//设置管理员id 
         log.setCreatedate(new Date());//操作时间 
         log.setContent("管理员 " + userDetails.getUsername());//操作内容 
         log.setOperation("登录");//操作 
  
         logService.log(log);//添加日志 
  
  super.onAuthenticationSuccess(request, response, authentication);  
     }  
  
 }  

10、在applicationContext-services.xml中加入新的配置

applicationContext-services.xml中加入了Aspectj配置以及新增的管理员Service、日志记录Service配置:

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:aop="http://www.springframework.org/schema/aop" 
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xsi:schemaLocation="  
             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> 
  
  <!-- 加入Aspectj配置 --> 
  <aop:aspectj-autoproxy /> 
  <bean id="logAspect" class="com.xxx.aop.LogAspect" /> 
  
  <!-- 电影业务逻辑对象 --> 
  <bean id="filmService" class="com.xxx.service.FilmServiceImpl"></bean> 
  
  <!-- 管理员业务逻辑对象 --> 
  <bean id="adminService" class="com.xxx.service.AdminServiceImpl"></bean> 
  
  <!-- 日志记录业务逻辑对象 --> 
  <bean id="logService" class="com.xxx.service.LogServiceImpl"></bean> 
  
 </beans> 

Xml代码  

 <?xml version="1.0" encoding="UTF-8"?> 
 <beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:aop="http://www.springframework.org/schema/aop" 
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xsi:schemaLocation="  
             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> 
  
  <!-- 加入Aspectj配置 --> 
  <aop:aspectj-autoproxy /> 
  <bean id="logAspect" class="com.xxx.aop.LogAspect" /> 
  
  <!-- 电影业务逻辑对象 --> 
  <bean id="filmService" class="com.xxx.service.FilmServiceImpl"></bean> 
  
  <!-- 管理员业务逻辑对象 --> 
  <bean id="adminService" class="com.xxx.service.AdminServiceImpl"></bean> 
  
  <!-- 日志记录业务逻辑对象 --> 
  <bean id="logService" class="com.xxx.service.LogServiceImpl"></bean> 
  
 </beans> 

11、配置成功后分别进行登录、添加、修改、删除影片操作

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第四篇:SpringBoot与任务

    版权声明:本文为博主原创文章,未经博主允许不得转载。 ...

    用户1212940
  • Junit加载Spring容器作单元测试_添加事务回滚

    Junit加载Spring容器作单元测试_添加事务回滚 在执行单元测试时,为了避免产生脏数据,将测试单元设置成事务回滚!!!!! package com...

    用户1212940
  • Redis在Window服务下的安装

    版权声明:本文为博主原创文章,未经博主允许不得转载。 ...

    用户1212940
  • VUE路由去除#问题

    最近自己在写一个vue的小型管理系统,在浏览器中看到的路由都是带有#的,很是不好看。为了解决此问题,大家一般都会想到:mode: 'history'。可是在开发...

    用户1174387
  • 分享个刚写好的 android 的 ListView 动态加载类,功能全而代码少。

    (转载声明出处:https://cloud.tencent.com/developer/user/1148436/activities) 简介:       该...

    林冠宏-指尖下的幽灵
  • 【高并发】秒杀系统高并发请求排队处理

    今天无意中看见了这位兄弟的文章 通过请求队列的方式来缓解高并发抢购(初探)  但文章最后说并发超过500 就会出现超发,看了下代码,的确有这个问题

    肖哥哥
  • 猿学-讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute

    ASP.NET Core MVC 2.1 特意为构建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (注:文...

    黑客联盟
  • 设计模式之中介者模式(mediator模式)引入中介者模式中介者模式的实例中介者模式分析

    大家想象一下有十个人要共同完成一个工作,他们要互相合作和沟通,并且根据对方的通知可能要改变自己的状态,但这通常会带来很多问题,流程过于复杂,使得每个人不仅要专注...

    desperate633
  • 讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute

    ASP.NET Core MVC 2.1 特意为构建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (注:文...

    _淡定_
  • 讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute

    转自:https://www.cnblogs.com/sheldon-lou/p/9495377.html

    莫问今朝

扫码关注云+社区

领取腾讯云代金券