Spring AOP 使用注解的方式实现用户日志的两种方法

来源:互联网 发布:关于加强网络信息安全 编辑:程序博客网 时间:2024/06/05 15:48

  前几天接触到用户日志,作为一名菜鸟,我决定从零开始介绍做用户日志的两种方法。

  第一种是比较简单点的,完成第一个就是无脑粘贴复制,比较适合赶任务做出来,下面开始。

  在开始之前,我们还是先做好前期的准备,就是关于用户日志Mylog类的定义,以及它的dao层,由于只是一个例子,所以我尽量定义简单一点,到时候你可以根据实际情况定义详细一点的属性。


package com.planone.entity;import java.util.Date;public class MyLog {private String id;  private String user;       //操作人private String command;    //执行了什么操作private String method;     //方法名private int normal;         //是否正常public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public void setId(String id) {this.id = id;}public String getId() {return id;}public String getUser() {return user;}public void setUser(String user) {this.user = user;}public String getCommand() {return command;}public void setCommand(String command) {this.command = command;}public int getNormal() {return normal;}public void setNormal(int normal) {this.normal = normal;}public MyLog(String id, String user, String command, int normal) {super();this.id = id;this.user = user;this.command = command;this.normal = normal;}public MyLog(){System.out.println("log is building.........");}}
接着定义ILogDao这个接口,使用LogDao来对其进行实现,方法如下

package com.planone.dao.impl;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;import com.planone.dao.ILogDao;import com.planone.entity.MyLog;import com.planone.mapping.LogMapper;@Repository("logDao")public class LogDao implements ILogDao {    @Autowired    private LogMapper logMapper;        /*public LogDao(){        System.out.println("logDao now is coming!!!!");    }*/        @Override    public List<MyLog> listAllLogs() {            return logMapper.listAllLogs();    }    @Override    public void addLog(MyLog log) {        logMapper.addLog(log);    }}


这里的logMapper使用的是注解的方式,对两个方法进行实现。因为采用的是Mybatis加Mysql,所以我还是贴出来

package com.planone.mapping;import java.util.List;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Select;import org.springframework.stereotype.Component;import com.planone.entity.MyLog;@Componentpublic interface LogMapper {    @Select(value="SELECT * FROM t_log")    public List<MyLog> listAllLogs();        @Insert(value="INSERT INTO t_log(id,user,command,normal,method) VALUES (#{id},#{user},#{command},#{normal},#{method})")    public void addLog(MyLog log);}

后面的关于Service层的实现,写法跟Dao层差不多

package com.planone.service.impl;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.planone.dao.ILogDao;import com.planone.entity.MyLog;import com.planone.service.ILogService;@Service("logService")public class LogService implements ILogService{@Autowiredprivate ILogDao logDao;@Overridepublic List<MyLog> listAllLogs() {return logDao.listAllLogs();}@Overridepublic void addLog(MyLog log) {logDao.addLog(log);}/*public LogService(){System.out.println("logService is now coming!!!!!!!!!!!");}*/}

也是实现ILogService接口,那么接下来前期工作就准备好了。下面正式进入日志的管理,

首先我们先要定义一个切面,也就是SystemLogAspect类,其定义如下:

package com.miva.handler;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import com.miva.entity.Entry;import com.miva.entity.Log;import com.miva.entity.User;import com.miva.service.ILogService;import com.miva.util.HarJson;import com.miva.util.SessionUtils;@Component@Aspectpublic class LogAspect {private final static int LOG_STATE_NORMAL = 1;private final static int LOG_STATE_UNNORMAL = 2;@Autowiredprivate ILogService logService;/** * 管理员后台登陆日志 */@AfterReturning(value="execution(* com.miva.adm.controller.LoginController.doAdminLogin(..))", returning="url")public void adminLogin(String url) {               HttpSession session = request.getSession();           //读取session中的用户             User user = (User) session.getAttribute("loginUser");//获取session中user               //接着就可以新建一个log然后存进数据库中                MyLog myLog = new MyLog();         myLog.setId("12345");         myLog.setMethod("doAdminLogin");         myLog.setNormal(LOG_NORMAL);         myLog.setUser(user.getName());         myLog.setCommand("用户登录");         logService.addLog(myLog);  }}}

按照这种因此当Controller层中执行到doAdminLogin方法时,就可以添加到这个用户日志到数据库中了,按照这个原理,在对用户的增删改查中,也使用这样,就可以得到相对应的用户日志。在这里,我们我注意的是@Aspect这个注解要记得加在类文件上,同时,我们要理解@AfterReturning(value="execution(*com.miva.adm.controller.LoginController.doAdminLogin(..))", returning="url")这条语句,execution括号里面是你要执行
的方法,就是所在的包下面的具体方法,(..)表示的是可以是任何参数,省略写,具体可以查找一下他们的用法。

       最重要的一点,就是在springmvc.xml的配置文件中加上以下一句,

<aop:aspectj-autoproxy/>

,简单理解就是使用到切面代理 最后当我们的controller层中执行doAdminLogin()这个方法时,就会在数据库中增加一条日志记录,显示用户用户登录,这种方法 就是这么简单。这种方法的弊端就是我们需要在某一个执行方法的时候都要加一个这样的东西,如果并且是重复的,如果我们有很多个方法,这样的东西就要写很多次,非常麻烦,因此我建议的是使用第二种方法重构一下,虽然比第一种方法难理解一点,但是非常清晰。


    第二种方法,这种方法我是从别人的博客那里翻到的,但是找回来本来想直接转载,再找不回来了,就用我自己仿的例子吧。

    首先我们先自定义注解,如下

package com.planone.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.PARAMETER,ElementType.METHOD})    @Retention(RetentionPolicy.RUNTIME)    @Documented    public @interface SystemControllerLog {  public String description() default "";}

其中关于上面的@Target,@Retention,@Document是源注解,代表注解的注解

@Retention(RetentionPolicy.SOURCE)    //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)  // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)   // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
接下来同样需要一个定义一个切面SystemLogAspect类
package com.planone.aspect;import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.apache.log4j.Logger;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.mongodb.util.JSON;import com.planone.annotation.SystemControllerLog;import com.planone.entity.MyLog;import com.planone.entity.User;import com.planone.service.ILogService;import com.planone.util.MyUIDUtils;import net.sf.json.util.JSONUtils;@Aspect@Componentpublic class SystemLogAspect {@Autowiredprivate ILogService logService;private final static int LOG_NORMAL=1;private final static int LOG_UNNORMAL=0;public SystemLogAspect(){System.out.println("SystemLogAspect is initing!!!!");}public Logger logger = Logger.getLogger(SystemLogAspect.class); //Controller层切点   @Pointcut("@annotation(com.planone.annotation.SystemControllerLog)")public void controllerAspect(){} /**       * 后置通知 用于拦截Controller层记录用户的操作       *       * @param joinPoint 切点       */   @AfterReturning("controllerAspect()")public void doBefore(JoinPoint joinPoint){System.out.println("我是否被拦截过!!!");HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession();//读取session中的用户        User user = (User) session.getAttribute("loginUser");        //获取请求ip        String ip = request.getRemoteAddr();        try { MyLog myLog = new MyLog(); myLog.setId(MyUIDUtils.getId10()); myLog.setMethod(joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()+"()"); myLog.setNormal(LOG_NORMAL); myLog.setUser(user.getName()); myLog.setCommand(getControllerMethodDescription(joinPoint)); logService.addLog(myLog); logger.info("here wait for moment");} catch (Exception e) {e.printStackTrace();}} /**       * 获取注解中对方法的描述信息 用于Controller层注解       *       * @param joinPoint 切点       * @return 方法描述       * @throws Exception       */         public  static String getControllerMethodDescription(JoinPoint joinPoint)  throws Exception {            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 description = "";             for (Method method : methods) {                 if (method.getName().equals(methodName)) {                    Class[] clazzs = method.getParameterTypes();     //对比方法中参数的个数                     if (clazzs.length == arguments.length) {                        description = method.getAnnotation(SystemControllerLog. class).description();                         break;                    }                }            }             return description;        }       }

在这个类型,需要注意以下几点,@Pointcut("@annotation(com.planone.annotation.SystemControllerLog)"),这句话的意思是把当执行到这个注解的方法时作为一个切点,在执行到这个切点时,会有一系列的操作执行,下面就是定义在这个切点我们对用户的日志处理,也就是类中的
@AfterReturning("controllerAspect()")这个注解,他的意思是实行controllerAspect这个方法时想对应的后置通知操作,也就是对日志进行写入。而后面的getControllerMethodDescription()方法就是如何在controller层中匹配到想相对应的方法,其原理就是应用到反射的机制进行的,有些兴趣的伙伴们可以重新学习回来java的反射机制。紧接着在controller层中,直接加上我们想要的注释就可以实现用户日志了,如下:
@SystemControllerLog(description = "增加用户") public HarJson userAdd(@RequestBody User user){HarJson hj=new HarJson();logger.info(user.getName()+"............."+user.getUsername());user.setId(MyUIDUtils.getId10());try {userService.addUser(user);hj.setSuccess(true);hj.setMsg("success");} catch (Exception e) {e.printStackTrace();hj.setSuccess(false);hj.setMsg("error");}return hj;}

但是千万不要忘记在springmvc.xml文件中同样需要加入
<aop:aspectj-autoproxy/>
这样日志管理就成功完成了。
  这篇文章是我从菜鸟的角度来写的,或许当中有很多语言不通的地方和错误的地方,如果有大神发现错误,希望也可以出来指正,我会及时修改的。


                                             
0 0