zoukankan      html  css  js  c++  java
  • 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;
    
    }
    

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

     
     
  • 相关阅读:
    解决QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'问题
    error while loading shared libraries:libQt5SerialPort.so问题解决
    洛谷P4343 [SHOI2015]自动刷题机
    十一届山东省大学生程序设计竞赛 部分题解
    洛谷P4185 [USACO18JAN]MooTube G 题解
    洛谷P4588 [TJOI2018]数学计算
    洛谷P2085《最小函数值》
    多项式小记
    CF932F 【Escape Through Leaf】
    'ipconfig' 不是内部或外部命令,也不是可运行的程序 或批处理文件。持续思考ing
  • 原文地址:https://www.cnblogs.com/yhtboke/p/5749148.html
Copyright © 2011-2022 走看看