spring boot 开发—第六篇AOP记录系统关键操作日志

1、AOP简介

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。 

2、AOP核心概念

(1)、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

(2)、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

(3)、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

(4)、切入点(pointcut)

对连接点进行拦截的定义

(5)、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类:

Before(前) org.apringframework.aop.MethodBeforeAdvice 

After-returning(返回后) org.springframework.aop.AfterReturningAdvice 

After-throwing(抛出后) org.springframework.aop.ThrowsAdvice 

Introduction(引入) org.springframework.aop.IntroductionInterceptor 

Arround(环绕) org.aopaliance.intercept.MethodInterceptor 

(6)、目标对象

代理的目标对象

(7)、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

(8)、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

3、具体实例

3.1、定义注解,拦截controller

package com.vesus.springbootlog.annotation;

import java.lang.annotation.*;

/**
 * @Description:  定义注解,拦截controller
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午10:54
 * @Version: 1.0
 */
@Retention(RetentionPolicy.RUNTIME)//元注解,定义注解被保留策略,一般有三种策略
                                    //1、RetentionPolicy.SOURCE 注解只保留在源文件中,在编译成class文件的时候被遗弃
                                    //2、RetentionPolicy.CLASS 注解被保留在class中,但是在jvm加载的时候北欧抛弃,这个是默认的声明周期
                                    //3、RetentionPolicy.RUNTIME 注解在jvm加载的时候仍被保留
@Target({ElementType.METHOD}) //定义了注解声明在哪些元素之前
@Documented
public @interface SystemControllerLog {
    //定义成员
    String descrption() default "" ;//描述
    String actionType() default "" ;//操作的类型,1、添加 2、修改 3、删除 4、查询
}

3.2、定义注解,拦截service

package com.vesus.springbootlog.annotation;

import java.lang.annotation.*;

/**
 * @Description: 定义注解,拦截service
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午10:54
 * @Version: 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SystemServiceLog {
    //定义成员
    String decription() default "" ;
}

3.3、定义日志切入类

package com.vesus.springbootlog.aspect;

import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.annotation.SystemServiceLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.SystemLog;
import com.vesus.springbootlog.service.SystemLogService;
import com.vesus.springbootlog.util.ReturnCode;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Description: 定义日志切入类
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午11:05
 * @Version: 1.0
 */
@Aspect
@Component
@Order(-5)
public class SystemLogAspect {

    @Autowired
    private SystemLogService systemLogService;

    /***
     * 定义service切入点拦截规则,拦截SystemServiceLog注解的方法
     */
    @Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemServiceLog)")
    public void serviceAspect(){}

    /***
     * 定义controller切入点拦截规则,拦截SystemControllerLog注解的方法
     */
    @Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemControllerLog)")
    public void controllerAspect(){}

    /***
     * 拦截控制层的操作日志
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("controllerAspect()")
    public ExecutionResult recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        SystemLog systemLog = new SystemLog();
        Object proceed = null ;
        //获取session中的用户
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.getSession().getAttribute("user");
        systemLog.setUserid("vesus");
        //获取请求的ip
        String ip = request.getRemoteAddr();
        systemLog.setRequestip(ip);
        //获取执行的方法名
        systemLog.setActionmethod(joinPoint.getSignature().getName());

        //获取方法执行前时间
        Date date=new Date();
        systemLog.setActiondate(date);

        proceed = joinPoint.proceed();
        //提取controller中ExecutionResult的属性
        ExecutionResult result = (ExecutionResult) proceed;

        if (result.getResultCode().equals(ReturnCode.RES_SUCCESS)){
            //设置操作信息
            systemLog.setType("1");
            //获取执行方法的注解内容
            systemLog.setDescription(getControllerMethodDescription(joinPoint)+":"+result.getMsg());
        }else{
            systemLog.setType("2");
            systemLog.setExceptioncode(result.getMsg());
        }

        Object[] params = joinPoint.getArgs() ;
        String returnStr = "" ;
        for (Object param : params) {
            if (param instanceof String){
                returnStr+= param ;
            }else if (param instanceof Integer){
                returnStr+= param ;
            }
        }
        systemLog.setParams(returnStr);

        systemLogService.saveUser(systemLog);

        return result ;
    }

    //异常处理
    @AfterThrowing(pointcut = "controllerAspect()",throwing="e")
    public void doAfterThrowing(JoinPoint joinPoint,Throwable e) throws Throwable{
        SystemLog systemLog = new SystemLog();
        Object proceed = null ;
        //获取session中的用户
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.getSession().getAttribute("user");
        systemLog.setUserid("vesus");
        //获取请求的ip
        String ip = request.getRemoteAddr();
        systemLog.setRequestip(ip);
        systemLog.setType("2");
        systemLog.setExceptioncode(e.getClass().getName());
        systemLog.setExceptiondetail(e.getMessage());
        systemLogService.saveUser(systemLog);
    }


    /***
     * 获取service的操作信息
     * @param joinpoint
     * @return
     * @throws Exception
     */
    public String getServiceMethodMsg(JoinPoint joinpoint) throws Exception{
        //获取连接点目标类名
        String className =joinpoint.getTarget().getClass().getName() ;
        //获取连接点签名的方法名
        String methodName = joinpoint.getSignature().getName() ;
        //获取连接点参数
        Object[] args = joinpoint.getArgs() ;
        //根据连接点类的名字获取指定类
        Class targetClass = Class.forName(className);
        //拿到类里面的方法
        Method[] methods = targetClass.getMethods() ;

        String description = "" ;
        //遍历方法名,找到被调用的方法名
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes() ;
                if (clazzs.length==args.length){
                    //获取注解的说明
                    description = method.getAnnotation(SystemServiceLog. class).decription();
                    break;
                }
            }
        }
        return description ;
    }

    /***
     * 获取controller的操作信息
     * @param point
     * @return
     */
    public String getControllerMethodDescription(ProceedingJoinPoint point) throws  Exception{
        //获取连接点目标类名
        String targetName = point.getTarget().getClass().getName() ;
        //获取连接点签名的方法名
        String methodName = point.getSignature().getName() ;
        //获取连接点参数
        Object[] args = point.getArgs() ;
        //根据连接点类的名字获取指定类
        Class targetClass = Class.forName(targetName);
        //获取类里面的方法
        Method[] methods = targetClass.getMethods() ;
        String description="" ;
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == args.length){
                    description = method.getAnnotation(SystemControllerLog.class).descrption();
                    break;
                }
            }
        }
        return description ;
    }

}

3.4、定义控制器controller

package com.vesus.springbootlog.controller;

import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.User;
import com.vesus.springbootlog.service.UserService;
import com.vesus.springbootlog.util.ReturnCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    UserService userService ;

    @RequestMapping(value = "/userlist")
    @SystemControllerLog(descrption = "查询用户信息",actionType = "4")
    public ExecutionResult getUserList(String id) throws Exception{

        ExecutionResult result = new ExecutionResult();
        try {
            List<User> users = userService.findAll() ;
            result.setTotal(users.size());
            result.setResultCode(ReturnCode.RES_SUCCESS);
            result.setFlag(true);
            result.setData(users);
            result.setMsg("查询成功!");
            //异常处理
            int aa= 5/0;
        }catch (Exception e){
            result.setFlag(true);
            result.setData(null);
            result.setResultCode(ReturnCode.RES_FAILED);
            result.setMsg("查询失败!");
            throw e ;
        }
        return result ;
    }

}

3.5、启动

访问http://localhost:8080//userlist?id=123456,可以看到数据库增加了日志文件。

1   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
2   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
3   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
4   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
5   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 

源码地址:https://gitee.com/vesus198/springboot-demo/tree/master/springboot-log

  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值