1. 程式人生 > >Jeecg中通過Spring_AOP+註解方式實現日誌的管理

Jeecg中通過Spring_AOP+註解方式實現日誌的管理

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 '操作日誌表';