Jeecg中通过Spring_AOP+注解方式实现日志的管理

转载;https://blog.csdn.net/ma451152002/article/details/77234236
Jeecg中通过Spring_AOP+注解方式实现日志的管理

一、设计思路

通过spring的aop切面功能,拦截到请求的所有的符合切面表达式的方法,判断是否含有注解标志,生成日志对象,然后通过aop的后置通知进行日志的持久化。

二、代码实现

    1、工程结构:
    

2、pom.xml增加aop依赖:

 <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
          <version>1.5.3</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.4</version>
        </dependency>
        <dependency>
          <groupId>aopalliance</groupId>
          <artifactId>aopalliance</artifactId>
          <version>1.0</version>
        </dependency>
        <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>3.2.4</version>
        </dependency> 



3、定义我们的Log实体对象
package aop;
 
import java.util.Date;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
 
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.GenericGenerator;
import org.jeecgframework.core.common.entity.IdEntity;
 
@Entity
@Table(name="assess_log_test")
@DynamicInsert(true)
@DynamicUpdate(true)
@SuppressWarnings("serial")
public class Log  implements java.io.Serializable{
    /**
     * 日志id
     */
    private String id;
 
    /**
     * 当前操作人id
     */
    private String loginAccount;
 
    /**
     * 当前操作人ip
     */
    private String loginIp;
 
    /**
     * 操作请求的链接
     */
    private String actionUrl;
 
    /**
     * 执行的模块
     */
    private String module;
 
    /**
     * 执行的方法
     */
    private String method;
 
    /**
     * 执行操作时间
     */
    private Long actionTime;
 
    /**
     * 描述
     */
    private String description;
 
    /**
     * 执行的时间
     */
    private Date gmtCreate;
 
    /**
     * 该操作状态,1表示成功,-1表示失败!
     */
    private Short state;
 
    @Id
    @GeneratedValue(generator = "paymentableGenerator")
    @GenericGenerator(name="paymentableGenerator",strategy="uuid")
    @Column(name="id",nullable=false,length=32)
    public String getId() {
        return id;
    }
    @Column(name="login_account",length=32)
    public String getLoginAccount() {
        return loginAccount;
    }
    @Column(name="login_ip",length=32)
    public String getLoginIp() {
        return loginIp;
    }
    @Column(name="action_url",length=100)
    public String getActionUrl() {
        return actionUrl;
    }
    @Column(name="module",length=32)
    public String getModule() {
        return module;
    }
    @Column(name="method",length=32)
    public String getMethod() {
        return method;
    }
    @Column(name="action_time")
    public Long getActionTime() {
        return actionTime;
    }
    @Column(name="description",length=200)
    public String getDescription() {
        return description;
    }
    @Column(name="gmt_create")
    public Date getGmtCreate() {
        return gmtCreate;
    }
    @Column(name="state")
    public Short getState() {
        return state;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public void setLoginAccount(String loginAccount) {
        this.loginAccount = loginAccount;
    }
 
    public void setLoginIp(String loginIp) {
        this.loginIp = loginIp;
    }
 
    public void setActionUrl(String actionUrl) {
        this.actionUrl = actionUrl;
    }
 
    public void setModule(String module) {
        this.module = module;
    }
 
    public void setMethod(String method) {
        this.method = method;
    }
 
    public void setActionTime(Long actionTime) {
        this.actionTime = actionTime;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public void setGmtCreate(Date gmtCreate) {
        this.gmtCreate = gmtCreate;
    }
 
    public void setState(Short state) {
        this.state = state;
    }
 
    @Override
    public String toString() {
        return "Log [id=" + id + ", loginAccount=" + loginAccount
                + ", loginIp=" + loginIp + ", actionUrl=" + actionUrl
                + ", module=" + module + ", method=" + method + ", actionTime="
                + actionTime + ", description=" + description + ", gmtCreate="
                + gmtCreate + ", state=" + state + "]";
    }
    
    
 
}

4.定义注解对象
package aop;
 
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;
 
/**
 * 日志记录
 *  
 *  @author mgj 
 *    @date 2017-8-11 上午10:53:19
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
    String module() default "";
    String methods() default "";
}
5.定义aop界面拦截方法
 package aop;
 
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecgframework.core.util.ResourceUtil;
import org.jeecgframework.core.util.StringUtil;
import org.jeecgframework.web.system.pojo.base.TSUser;
import org.jeecgframework.web.system.service.SystemService;
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 antlr.StringUtils;
 
@Component
@Aspect
public class LogAopAction {
    private long BEGIN_TIME;
    private long END_TIME;
    private Log log = new Log();
    @Autowired
    private SystemService  systemService;
    
 
    //@Pointcut("execution(* vote.backmanage.teachermanage.controller.AssessTeacherInfoController.*(..))")
    //@Pointcut("execution(* vote.backmanage.teachermanage.controller.*.*(..))")
    //@Pointcut("execution(* vote.backmanage.teachermanage.controller..*.*(..))")
    @Pointcut("execution(* vote.backmanage.teachermanage.controller..*.*(..))")
    public  void controllerAspect(){}
    
    @Before("controllerAspect()")
    public  void doBefore(){
        BEGIN_TIME = new Date().getTime();
    }
    
    @AfterReturning("controllerAspect()")
    public  void doAfter(){
        if (log.getState() == 1 || log.getState() == -1) {
            log.setActionTime(END_TIME - BEGIN_TIME);
            log.setGmtCreate(new Date(BEGIN_TIME));
            
            System.out.println(log);
            System.out.println("存入到数据库");
            systemService.save(log);
            
        }else {
            System.out.println(log);
            System.out.println("不存到数据库里");
        }
    }
    @After("controllerAspect()")
    public  void after(){
        END_TIME = new Date().getTime();
    }
 
    @Around("controllerAspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
                                        .getRequest();
        //获得当前用户
        TSUser user = ResourceUtil.getSessionUserName();
        String name = user.getRealName();
        log.setLoginAccount(name);
        
        //拦截的实体类
        Object target = pjp.getTarget();
        //拦截的方法名
        String methodName = pjp.getSignature().getName();
        //拦截的方法参数
        Object[] args = pjp.getArgs();
        //拦截的放参数类型
        Signature sig = pjp.getSignature();
        
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        
        msig = (MethodSignature)sig;
        Class[] parameterTypes = msig.getMethod().getParameterTypes();
        
        Object object = null;
        Method method = null;
        
        try{
            method = target.getClass().getMethod(methodName, parameterTypes);
        }catch (Exception e) {
            e.printStackTrace();
        }
        
        if (null != method) {
            if (method.isAnnotationPresent(SystemLog.class)) {//判断是否包含我们自定义的注解
                SystemLog systemlog = method.getAnnotation(SystemLog.class);
                log.setModule(systemlog.module());
                log.setMethod(systemlog.methods());
                log.setLoginIp(getIp(request));
                log.setActionUrl(request.getRequestURI());
                
                try {
                    object = pjp.proceed();
                    log.setDescription("执行成功");
                    log.setState((short)1);
                } catch (Exception e) {
                    log.setDescription("执行失败");
                    log.setState((short)-1);
                    e.printStackTrace();
                }
                
            }else {//不包自定义注解
                object = pjp.proceed();
                log.setDescription("此操作不包含注解");
                log.setState((short)0);
            }
            
        }else {//不需要拦截
            object = pjp.proceed();
            log.setDescription("不需要拦截直接运行");
            log.setState((short)0);
        }
        
        return object;
    }
    
    /**
     *    获得ip地址
     *  @param request
     *  @return
     *  @author mgj 
     *    @date 2017-8-11 下午2:19:51
     */
    private String getIp(HttpServletRequest request){
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }
    
}


6.增加aop自动扫描配置

(1)打开spring-mvc.xml文件,增加aop上下文

   如下:
 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
         http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd 
        http://www.springframework.org/schema/aop  
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  ">
2)增加aop自动扫描并实例化bean
 <aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="logAopAction" class="aop.LogAopAction"></bean>

7.持久化Log实体的xml配置,使用自动扫描class的形式进行配置。打开spring-mvc-hibernate.xml文件,增加<value>aop.</value>
<bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="entityInterceptor" ref="hiberAspect" />
        <property name="hibernateProperties">
            <props>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> -->
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            </props>
        </property>
        <!-- 注解方式配置 -->
        <property name="packagesToScan">
            <list>
                <value>org.jeecgframework.web.system.pojo.*</value>
                <value>org.jeecgframework.web.demo.entity.*</value>
                <value>org.jeecgframework.web.test.entity.*</value>
                <value>org.jeecgframework.web.cgform.entity.*</value>
                <value>org.jeecgframework.web.cgreport.entity.*</value>
                <value>aop.</value>
            </list>
        </property>
</bean>

8.使用注解方式,配置日志,在访问的controller方法上,增加@SystemLog(module="区教师库登录",methods="区教师库的assessTeacherInfo()方法")配置
    /**
     * 教师库管理列表 页面跳转
     * 
     * @return
     */
    @RequestMapping(params = "assessTeacherInfo")
    @SystemLog(module="区教师库登录",methods="区教师库的assessTeacherInfo()方法")
    public ModelAndView assessTeacherInfo(HttpServletRequest request) {        
        return new ModelAndView("vote/backmanage/teachermanage/assessTeacherInfoList");
    }

9.效果



三、注意事项
         1.增加aop自动扫描包时,必须写到spring-mvc.xml内,不可写到spring-mvc-aop.xml文件中。因为spring-mvc.xml会比spring-mvc-aop.xml文先执行。
         2.持久化Log实体,使用自动扫描class的形式进行配置时,规则如下
(1)<value>aop.</value>,会解析为aop/*.class 或者 aop/xxx/*.class。即aop的包以及子包下的所有class。
            (2)<value>aop</value>,会解析为aop/*.class 。即aop的包下的所有class。
            (3)<value>aop.*</value>,会解析为 aop/xxx/*.class。即aop的子包下的所有class。

四、思考
1.需要深刻理解spring_mvc.xml文件的执行顺序。
2.需要深刻理解使用自动扫描class的形式的配置规则。


---------------------------------------------------------------------------

附录:

Log实体创建的mysql脚本:

drop table if exists assess_log_test;
 
create table  assess_log_test (
    id varchar(32) not null COMMENT '主键id',
    login_account varchar(32) default null comment '当前操作人',
    login_ip varchar(32) default null comment '登录ip',
    action_url varchar(100) default null comment '请求url',
    module varchar(32) default null comment '执行模块',
    method varchar(32) default null comment '执行方法',
    action_time bigint default 0 comment '执行操作时间',
    description varchar(200)    default null comment '描述',
    gmt_create  datetime default null comment '执行时间',
    state smallint(6) default null comment '操作状态',
  primary key (id)
)engine=innodb default charset=utf8  comment '操作日志表';

 

posted @ 2019-08-06 17:28  DarJeely  阅读(643)  评论(0编辑  收藏  举报