Jeecg中通過Spring_AOP+註解方式實現日誌的管理
阿新 • • 發佈:2018-11-11
Jeecg中通過Spring_AOP+註解方式實現日誌的管理
一、設計思路
二、程式碼實現
1、工程結構:
四、思考
一、設計思路
通過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.定義註解物件5.定義aop介面攔截方法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 ""; }
如下: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
三、注意事項 1.增加aop自動掃描包時,必須寫到spring-mvc.xml內,不可寫到spring-mvc-aop.xml檔案中。因為spring-mvc.xml會比spring-mvc-aop.xml文先執行。 2.持久化Log實體,使用自動掃描class的形式進行配置時,規則如下<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)<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 '操作日誌表';