Spring AOP

一、简介
     面向切面编程,刨解开封装的对象内部,将影响多个类的公共行为封装到一个可用模块,即为切面。将系统服务与业务分离,减少重复代码,降低模块间的耦合度,方便后期维护和操作。
    AOP运用的主要场景有:权限控制、错误处理、缓存、调试等
二、核心概念

  • Aspect 切面:由pointcount和advice组成,包含连接点和横向逻辑的定义,通过 pointcut 和 advice 定位到特定的 joinpoint ,在 advice 中编写切面代码.
  • advice 通知:将 aspect 添加到特定的 join point 的一段代码.指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕
  • join point 连接点:程序运行中的一些节点,例如某方法的执行
  • point cut 切点:Advice与point cut 关联, point cut 匹配 join point
  • Target 目标对象:执行 advice 的目标对象,注:指的不是原来的类, 而是织入 advice 所产生的代理类, 默认使用标准的 JDK 动态代理
  • introduction 引入:为一个类型添加额外的方法或字段
  • AOP proxy 代理类:类被 AOP 织入 advice, 就会产生结果类, 它是融合了原类和增强逻辑的代理类
  • Weaving 织入:将 aspect 和其他对象连接起来, 并创建 adviced object 的过程.

三、实现
1、定义 aspect 切面
    @Aspect 标注 Bean 后,被Spring 框架 添加到 Spring AOP 中

@Aspect   // 切面
@Component  // Bean
public class SysLogAspect {
}

2、声明 pointcut 切点

    // 定义切点为 被SysLog注解 的连接点
    @Pointcut("@annotation(com.example.demo.study.SysLog)")
    public void logPointCut() {

    }

    /** 匹配类型 **/
    // 匹配指定包中的所有的方法
    // execution(* com.example.service.*(..))

    // 匹配当前包中的指定类的所有方法
    // execution(* UserService.*(..))

    // 匹配指定包中的所有 public 方法
    // execution(public * com.example.service.*(..))

    // 匹配指定包中的所有 public 方法, 并且返回值是 int 类型的方法
    // execution(public int com.example.service.*(..))

    // 匹配指定包中的所有 public 方法, 并且第一个参数是 String, 返回值是 int 类型的方法
    // execution(public int com.example.service.*(String name, ..))

    /** 匹配方法 **/
    // 匹配指定包中的所有的方法, 但不包括子包
    // within(com.example.service.*)

    // 匹配指定包中的所有的方法, 包括子包
    // within(com.example.service..*)

    // 匹配当前包中的指定类中的方法
    // within(UserService)

    // 匹配一个接口的所有实现类中的实现的方法
    // within(UserDao+)

    /** 匹配Bean名字 **/
    // 匹配以指定名字结尾的 Bean 中的所有方法
    // bean(*Service)

    /** 被注解标注的方法 **/
    // @annotation(com.example.demo.study.SysLog)

    /** 表达式组合 **/
    // 匹配以 Service 或 ServiceImpl 结尾的 bean
    // bean(*Service || *ServiceImpl)

    // 匹配名字以 Service 结尾, 并且在包 com.example.service 中的 bean
    // bean(*Service) && within(com.example.service.*)

3、声明 advice

  
    // 定义切点前的 Advice
    @Before(value = "logPointCut()")
    public void beforeSyeLog() {
        System.out.println("这是切点前");
        // 逻辑
    }

    // 定义切点后的 Advice
    @AfterReturning(pointcut = "logPointCut()", returning = "response")
    public void saveSysLog(JoinPoint joinPoint, Object response) {
        if (dataSource == null) {
            log.error("未找到DataSource,无法记录操作日志。");
            return;
        }
        // 利用反射获取
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取请求类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求方法名
        String methodName = method.getName();
        methodName = className + "." + methodName;

        String operation = null;
        // 获取注解信息
        SysLog sysLog = method.getAnnotation(SysLog.class);
        if (sysLog != null) {
            operation = "[" + sysLog.module() + "]" + sysLog.operation();
        }

        // 参数信息
        StringJoiner sjArgsInfo = new StringJoiner(",");
        Parameter[] parameters = method.getParameters();
        Object[] args = joinPoint.getArgs();

        outer:
        for (int i = 0; i < args.length; i++) {
            try {
                String paramName = parameters[i].getName();
                if (sysLog != null) {
                    String[] ignoreParameters = sysLog.ignoreParameters();
                    if (ignoreParameters.length > 0) {
                        for (String ignore : ignoreParameters) {
                            if (paramName.equals(ignore)) {
                                continue outer;
                            }
                        }
                    }
                }
                String argInfo = null;
                if (sysLog != null && "toString".equalsIgnoreCase(sysLog.serializationType())) {
                    argInfo = args[i].toString();
                } else {
                    argInfo = mapper.writeValueAsString(args[i]);
                }
                sjArgsInfo.add(paramName + ":" + argInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        // return info
        String returnInfo = null;
        if (sysLog != null && !sysLog.ignoreResponse()) {
            try {
                if (sysLog != null && "toString".equalsIgnoreCase(sysLog.serializationType())) {
                    returnInfo = response.toString();
                } else {
                    returnInfo = mapper.writeValueAsString(response);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 定义切点周围的 Advice
    @Around(value = "logPointCut()")
    public Object aroundSyeLog(ProceedingJoinPoint pjp) {
        System.out.println("这是切点前");
        // 逻辑
        return null;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值