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
另外再附加 在未登录时禁止访问页面的功能 采用拦截器
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; }
相关推荐
zmysna 2020-06-25
itjavashuai 2020-05-27
neweastsun 2020-05-05
MicroBoy 2020-05-04
方志朋 2020-05-01
横云断岭 2020-04-17
方志朋 2020-03-01
smalllove 2020-02-14
csuzxm000 2020-01-09
neweastsun 2019-12-29
82296830 2014-06-07
Julywhj 2019-12-04
JudeJoo 2019-11-19
也许不会看见 2020-06-28
smalllove 2020-05-09
itjavashuai 2020-02-14
justlike 2020-09-02
牧场SZShepherd 2020-07-19