Spring AOP 实现写事件日志功能

什么是AOP?AOP使用场景?AOP相关概念?Spring AOP组件?如何使用Spring AOP?等等这些问题请参考博文:Spring AOP 实现原理

下面重点介绍如何写事件日志功能,把日志保存到数据库中。 
事件日志是与主业务功能无关的逻辑,用AOP实现是再好不过了,其中因为有些数据库日志表中的字段参数需要传递,所以会用到自定义注解,将这些参数用自定义注解传递过来。

1.自定义注解Operation

package com.jykj.demo.filter;

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; /** * @Descrption该注解描述方法的操作类型和方法的参数意义 */ @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) @Documented public @interface Operation { /** * @Description描述操作类型 为必填项,1:登录日志2:操作日志 */ int type(); /** * @Description描述操作意义 比如申报通过或者不通过等 */ String desc() default ""; /** * @Description描述操作方法的参数意义 数组长度需与参数长度一致,否则会参数与描述不一致的情况 */ String[] arguDesc() default {}; } 

2.切面类EventLogAspect

package com.jykj.demo.filter; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import com.jykj.demo.service.SysEventService; //事件日志 切面,凡是带有 @Operation 注解的service方法都将会写日志 public class EventLogAspect { @Autowired SysEventService sysEventService; public void doAfterReturning(JoinPoint jp) { System.out.println( "log Ending method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod(); Method soruceMethod; try { soruceMethod = jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes()); Operation oper = soruceMethod.getAnnotation(Operation.class); if (oper != null) { System.out.println(oper.desc()); // 解析参数 sysEventService.insertEventLog(oper.type(),oper.desc()+"("+extractParam(jp.getArgs(),oper.arguDesc())+") 成功"); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } } public void doAfterThrowing(JoinPoint jp, Throwable ex) { Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod(); Method soruceMethod; try { soruceMethod = jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes()); Operation oper = soruceMethod.getAnnotation(Operation.class); if (oper != null) { System.out.println(oper.desc()); sysEventService.insertEventLog(oper.type(),oper.desc()+ "("+extractParam(jp.getArgs(),oper.arguDesc())+" 出现异常:"+ex.getMessage()); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } private String extractParam(Object[] objParam, String[] arguDesc) { StringBuilder sb = new StringBuilder(); int len = objParam.length<arguDesc.length?objParam.length:arguDesc.length;//取最小值 for (int i = 0; i < len; i++) { //空字符串将不解析 if(arguDesc[i]!=null && !arguDesc[i].isEmpty()){ sb.append(arguDesc[i]+":"+objParam[i]+","); } } String rs = sb.toString(); return rs.substring(0,rs.length()-1); } } 

3.切入点 主业务逻辑类FrmAppsService

package com.jykj.demo.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.jykj.demo.dao.FrmAppsMapper; import com.jykj.demo.entity.FrmApps; import com.jykj.demo.filter.Operation; import com.jykj.demo.mapper.AbstractService; @Service public class FrmAppsService { @Autowired FrmAppsMapper mapper; @Operation(type=1,desc="更新框架应用",arguDesc={"","操作类型"}) public int access(FrmApps app,String action){ return mapper.access(app,action); } }

4.Spring xml对AOP的配置

    <bean id="aspectEventLog" class="com.jykj.demo.filter.EventLogAspect" /> <!-- <aop:aspectj-autoproxy /> --> <!--配置service包下所有类或接口的所有方法--> <aop:config proxy-target-class="true"> <aop:aspect id="EventLogAspect" ref="aspectEventLog"> <aop:pointcut id="myService" expression="execution(* com.jykj.demo.service.*.*(..)) " /> <aop:after-returning pointcut-ref="myService" method="doAfterReturning"/> <aop:after-throwing pointcut-ref="myService" method="doAfterThrowing" throwing="ex"/> </aop:aspect> </aop:config> <!-- 控制器 --> <context:component-scan base-package="com.jykj.demo.controller" /> <!-- service --> <context:component-scan base-package="com.jykj.demo.service" /> <context:component-scan base-package="com.jykj.demo.filter" />

需要注意的是 proxy-target-class=”true” ,如果没有这个,目标类若是实现了接口,将会以JDK代理的方式,否则采用的是CGLib代理的方式,如果启动项目报错了,会报代理类无法转换的问题,这时候把这个配置写上就可以了。

这样就实现了写日志的功能,只要在需要写日志的service类的方法上用自定义注解Operation注解即可

5.输出 (编辑一个记录时)

log Ending method: com.jykj.demo.service.FrmAppsService.access
更新框架应用
log Ending method: com.jykj.demo.service.SysEventService.insertEventLog

这里写图片描述

扫描二维码关注公众号,回复: 8113244 查看本文章

另外再附加 在未登录时禁止访问页面的功能 采用拦截器 
SecurityInterceptor类

package com.jykj.demo.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.jykj.demo.util.Helper; public class SecurityInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); if (session.getAttribute(Helper.SESSION_USER) == null) { throw new AuthorizationException(); } else { return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } } 

对应的拦截器的配置 spring-mvc.xml

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/*"/> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/signIn"/> <mvc:exclude-mapping path="/register"/> <bean class="com.jykj.demo.filter.SecurityInterceptor"> </bean> </mvc:interceptor> </mvc:interceptors> <!-- bean 处理未登录重定向到登录界面 --> <bean id="handlerExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="com.jykj.demo.filter.AuthorizationException">redirect:login</prop> </props> </property> </bean>

异常类AuthorizationException

package com.jykj.demo.filter;

public class AuthorizationException extends Exception{ private static final long serialVersionUID = 1L; } 

猜你喜欢

转载自www.cnblogs.com/liuxiaofu/p/12005083.html
今日推荐