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;

}

原文地址:https://www.cnblogs.com/liuxiaofu/p/12005083.html

时间: 2024-10-09 10:17:09

Spring AOP 实现写事件日志功能的相关文章

(转)利用Spring AOP自定义注解解决日志和签名校验

一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!isValid) return ErrorUtil.buildError(ErrorUtil.ERR_CODE_COM

利用Spring AOP自定义注解解决日志和签名校验

转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!

spring AOP自定义注解 实现日志管理

今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven />     <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->     <context:component-scan base-pac

【.NET】AOP思想下的日志功能

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programming,面向对象编程)的补充和完善. 我们说了这么久的OOP,对于OO的理解也越来越深入,OO的伟大让我们一度折服.OOP引入封装.继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合.可当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力. 也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系.例如日志功能.日

Spring AOP进行日志记录

在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用.然后进行日志记录.使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志.但是实现起来有点麻烦. 另外一种就是使用Spring的AOP了.这种方式实现起来非常简单,只要配置一下配置文件就可以了.可是这种方式会拦截下所有的对action的每个操作.使得效率比较低.不过想做详细日志

彻底征服 Spring AOP 之 实战篇

接上一小节彻底征服 Spring AOP 之 理论篇 Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个具体的例子吧.下面的几个例子是我在工作中所遇见的比较常用的 Spring AOP 的使用场景, 我精简了很多有干扰我们学习的注意力的细枝末节, 以力求整个例子的简洁性. 下面几个 Demo 的源码都可以在我的 Github 上下载到. HTTP 接口

spring aop拦截controller方法

背景 开发的web应用程序涉及到校验采用的spring校验框架,在controller的方法中到处都要写校验处理,异常处理,能否减少这部分冗余代码. 问题: 这是表单提交的处理 1 @RequestMapping(value = "/edit", method = RequestMethod.POST) 2 public String edit(@Valid FormBean formBean, BindingResult bindingResult, Model model) { 3

如何在通用权限管理系统中集成log4net日志功能

开发人员都知道,在系统运行中要记录各种日志,自己写一个日志功能,无论是在效率还是功能扩展上来说都不是很好,目前大多用的是第三方的日志系统,其中一个非常有名,用的最多的就是log4net.下面是关于这个组件的介绍 ”Log4net是基于.net开发的一款非常著名的记录日志开源组件.最早是2001年7月由NeoWorks Limited启动的项目,基本的框架源, 于另外的一个非常著名的姐妹组件-log4j.现由Apache组织开发与维护.此日志架构是可灵活扩展,且通过配置文件来设置日志的属性及输出,

利用Spring AOP与JAVA注解为系统增加日志功能

Spring AOP一直是Spring的一个比较有特色的功能,利用它可以在现有的代码的任何地方,嵌入我们所想的逻辑功能,并且不需要改变我们现有的代码结构. 鉴于此,现在的系统已经完成了所有的功能的开发,我们需要把系统的操作日志记录起来,以方便查看某人某时执行了哪一些操作.Spring AOP可以方便查看到某人某时执行了哪一些类的哪一些方法,以及对应的参数.但是大部分终端用户看这些方法的名称时,并不知道这些方法名代码了哪一些操作,于是方法名对应的方法描述需要记录起来,并且呈现给用户.我们知道,AO