Spring AOP 实现写事件日志功能

来源:互联网 发布:虚拟充值软件 编辑:程序博客网 时间:2024/05/21 18:33

什么是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)@Documentedpublic @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;@Servicepublic 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;}

参考博文:Spring Aop 日志拦截应用
参考博文:基于配置的Spring AOP
参考博文:Spring AOP 详解

0 0
原创粉丝点击