spring——基于注解的AOP

文章介绍了如何在Java中使用AOP(面向切面编程)实现接口Calculator的扩展,以添加日志功能,同时讨论了代码解耦的问题和基于注解的SpringAOP配置。作者演示了如何在Calculator接口的实现类中加入日志通知,并展示了如何使用@Aspect和@Pointcut注解进行切面编程。
摘要由CSDN通过智能技术生成

3.1、场景模拟

3.1.1、声明接口

声明计算器接口Calculator,包含加减乘除的抽象方法

public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}
3.1.2 、创建实体类
public class CalculatorPureImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}
3.1.3、改变需求

为了提高系统安全,现需要加入日志功能,用户的每一步操作,都需要计入日志里。

传统做法:

public class CalculatorLogImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        System.out.println("[日志] 用户xxx操作 add 方法开始了,参数是:" + i + "," + j);
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] add 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        System.out.println("[日志]用户xxx操作 sub 方法开始了,参数是:" + i + "," + j);
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] sub 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        System.out.println("[日志]用户xxx操作 mul 方法开始了,参数是:" + i + "," + j);
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] mul 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        System.out.println("[日志]用户xxx操作 div 方法开始了,参数是:" + i + "," + j);
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] div 方法结束了,结果是:" + result);
        return result;
    }
}
3.1.4、 提出问题
①现有代码缺陷

针对带日志功能的实现类,我们发现有如下缺陷:

  • 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力

  • 附加功能分散在各个业务功能方法中,不利于统一维护

②解决思路

解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。

③困难

解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术。

3.2、AOP概念以及相关术语

3.2.1、概述

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

①切面

需要添加额外功能的类

②通知

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

  • 前置通知:在被代理的目标方法执行

  • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝

  • 异常通知:在被代理的目标方法异常结束后执行(死于非命

  • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论

  • 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所

③切点

切面的功能插入到哪里去执行的路径。

3.2.2、作用
  • 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。

  • 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

3.3 基于注解的AOP

3.3.1 配置工作
  1. 需要的依赖

    aspectjrt-1.5.3.jar

    aspectjweaver-1.9.9.jar

    spring-aspects-5.2.22.RELEASE.jar

  2. 配置

    创建 applicationContext.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 开始基于注解的AOP -->
    <context:component-scan base-package="com.AOP.test"></context:component-scan>   
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  </beans>
  1. 功能编码

    • 功能接口

      package com.AOP.test;
      ​
      public interface Calculator {
          int add(int i, int j);
          int sub(int i, int j);
          int mul(int i, int j);
          int div(int i, int j);
      }
    • 实现类

      package com.AOP.test;
      ​
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.context.annotation.EnableAspectJAutoProxy;
      import org.springframework.stereotype.Component;
      ​
      @Component
      public class CalculatorImpl implements Calculator{
          @Override
          public int add(int i, int j) {
              int result = i + j;
              System.out.println("方法内部 result = " + result);
              return result;
          }
          @Override
          public int sub(int i, int j) {
              int result = i - j;
              System.out.println("方法内部 result = " + result);
              return result;
          }
          @Override
          public int mul(int i, int j) {
              int result = i * j;
              System.out.println("方法内部 result = " + result);
              return result;
          }
          @Override
          public int div(int i, int j) {
              int result = i / j;
              System.out.println("方法内部 result = " + result);
              return result;
          }
      }
  2. 切面编码 

    //@Aspect表示这个类是一个切面类
    @Aspect
    //@Component注解保证这个切面类能够放入IOC容器  也可以换成@controller等
    @Component
    public class LoggerAspect {
        
      //切点  要添加改切面功能的类的路径
        @Pointcut("execution(* com.AOP.test.CalculatorImpl.*(..))")
        public void poincut() {
            
        }
        
      //前置通知
        @Before("poincut()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            String args = Arrays.toString(joinPoint.getArgs());
            System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
            }
      //后置通知
        @After("poincut()")
         public  void afterMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            String args = Arrays.toString(joinPoint.getArgs());
            System.out.println("Logger-->后置通知,方法名:"+methodName+",参数:"+args);
        }
        //返回通知
        @AfterReturning(value = "poincut()",returning = "result")
        public void afterReturningMethod(JoinPoint joinPoint, Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
        } 
      //异常通知
      @AfterThrowing(value = "poincut()", throwing = "ex")
        public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
        }
        
          //环绕通知
        @Around("poincut()")
        public Object aroundMethod(ProceedingJoinPoint joinPoint){
               String methodName = joinPoint.getSignature().getName();
            String args = Arrays.toString(joinPoint.getArgs());
            Object result = null;
               try {
                   System.out.println("环绕通知-->目标对象方法执行之前");
                   //目标对象(连接点)方法的执行
                   result = joinPoint.proceed();
                   System.out.println("环绕通知-->目标对象方法返回值之后");
            } catch (Throwable throwable) {
                   throwable.printStackTrace();
                   System.out.println("环绕通知-->目标对象方法出现异常时");
            } finally {
                System.out.println("环绕通知-->目标对象方法执行完毕");
            }
            return result;
        }
        
  3. 测试

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext3.xml");
            Calculator calculator = ac.getBean(Calculator.class);
            calculator.div(1, 3);//连接点
        }
  4. 结果

    环绕通知-->目标对象方法执行之前
    Logger-->前置通知,方法名:div,参数:[1, 3]
    方法内部 result = 0
    环绕通知-->目标对象方法返回值之后
    环绕通知-->目标对象方法执行完毕
    Logger-->后置通知,方法名:div,参数:[1, 3]
    Logger-->返回通知,方法名:div,结果:0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈(chen)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值