• Spring基础20——AOP基础


    1、什么是AOP

    AOP(Aspect-Oriented Programming)即面向切面编程,是一种新的方法论,是对那个传统OOP面向对象编程的补充。AOP的主要编程对象是切面(aspect),而切面模块化横切关注点,在应用AOP编程时,仍需要定义公共功能但可以明确功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就模块化到了特殊得对象(切面)里,那么什么是面向切面编程呢?

    下面我们通过一个例子来理解:

    我们需要实现一个计算器,并且有以下两个需求:

      需求1:日志,在程序执行期间追寻正在发生的活动。

      需求2:希望计算器只能处理正数的运算。

    首先我们来定义一个接口,来描述计算器的功能:

     1 public interface ArithmeticCalculator {
     2     /**
     3      * 加法
     4      * @param i
     5      * @param j
     6      * @return
     7      */
     8     int add(int i, int j);
     9 
    10     /**
    11      * 减法
    12      * @param i
    13      * @param j
    14      * @return
    15      */
    16     int sub(int i, int j);
    17 
    18     /**
    19      * 乘法
    20      * @param i
    21      * @param j
    22      * @return
    23      */
    24     int mul(int i, int j);
    25 
    26     /**
    27      * 除法
    28      * @param i
    29      * @param j
    30      * @return
    31      */
    32     int div(int i, int j);
    33 }

    之后我们实现这个接口并对方法进行实现:

     1 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
     2     @Override
     3     public int add(int i, int j) {
     4         int result = i + j;
     5         return result;
     6     }
     7     @Override
     8     public int sub(int i, int j) {
     9         int result = i - j;
    10         return result;
    11     }
    12     @Override
    13     public int mul(int i, int j) {
    14         int result = i * j;
    15         return result;
    16     }
    17     @Override
    18     public int div(int i, int j) {
    19         int result = i / j;
    20         return result;
    21     }
    22 }

    编写Main测试类对功能进行测试:

    1 public class Main {
    2     public static void main(String[] args) {
    3         ArithmeticCalculator calculator = new ArithmeticCalculatorImpl();
    4         System.out.println(calculator.add(1,2));
    5         System.out.println(calculator.sub(2,1));
    6         System.out.println(calculator.div(4,2));
    7         System.out.println(calculator.mul(2,4));
    8     }
    9 }

    我们可以观察到输出结果是正常的。

    在上面的基础上,修改ArithmeticCalculatorImpl 我们下面来完成上面两个需求:

     1 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
     2     @Override
     3     public int add(int i, int j) {
     4         if (i < 0 ||j < 0 ) {
     5             System.out.println("操作数必须为正数");
     6             return -1;
     7         }
     8         System.out.println("The add method begin [ " + i + ", " + j + " ]" );
     9         int result = i + j;
    10         System.out.println("The add method end [ " + result + " ]" );
    11         return result;
    12     }
    13     @Override
    14     public int sub(int i, int j) {
    15         if (i < 0 ||j < 0 ) {
    16             System.out.println("操作数必须为正数");
    17             return -1;
    18         }
    19         System.out.println("The sub method begin [ " + i + ", " + j + " ]" );
    20         int result = i - j;
    21         System.out.println("The sub method end [ " + result + " ]" );
    22         return result;
    23     }
    24     @Override
    25     public int mul(int i, int j) {
    26         if (i < 0 ||j < 0 ) {
    27             System.out.println("操作数必须为正数");
    28             return -1;
    29         }
    30         System.out.println("The mul method begin [ " + i + ", " + j + " ]" );
    31         int result = i * j;
    32         System.out.println("The mul method end [ " + result + " ]" );
    33         return result;
    34     }
    35     @Override
    36     public int div(int i, int j) {
    37         if (i < 0 ||j < 0 ) {
    38             System.out.println("操作数必须为正数");
    39             return -1;
    40         }
    41         System.out.println("The div method begin [ " + i + ", " + j + " ]" );
    42         int result = i / j;
    43         System.out.println("The div method end [ " + result + " ]" );
    44         return result;
    45     }
    46 }

    再次运行测试类:

    1 public class Main {
    2     public static void main(String[] args) {
    3         ArithmeticCalculator calculator = new ArithmeticCalculatorImpl();
    4         System.out.println(calculator.add(-1,2));
    5         System.out.println(calculator.sub(2,1));
    6         System.out.println(calculator.div(4,2));
    7         System.out.println(calculator.mul(2,4));
    8     }
    9 }

    输出结果:我们看到是实现了之前的需求。

    2.使用动态代理对程序进行改进

           我们用上面的代码实现了之前的两个需求,但是我们可以观察到,每个方法中都包含大量的重复代码,如果一旦需要修改日志或参数校验的规则,我们每个方法都要去修改,这显然是非常不利于维护的,那么我们可以采用——动态代理的方式来进行改进。代理模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象任何原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。

           

    代理类ArithmeticCalculatorLoggingProxy:

    public class ArithmeticCalculatorLoggingProxy {
        private ArithmeticCalculator calculator;
    
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator calculator) {
            this.calculator = calculator;
        }
    
        public ArithmeticCalculator getCalculatorProxy() {
            InvocationHandler handler = new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) {
                    System.out.println("The method " + method.getName() + " begin with " + Arrays.asList(args));
                    Object result = null;
                    try {
                        //前置通知
                        result = method.invoke(calculator, args);
                        //返回通知,可以访问到方法的返回值
                    } catch (Exception e) {
                        e.printStackTrace();
                        //异常通知,可以访问到方法出现的异常
                    }
                    //后置通知,方法可能会出异常,所以访问不到方法的返回值
                    System.out.println("The method add " + method.getName() +" with " + result);
                    return result;
                }
            };
            return (ArithmeticCalculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), handler);
        }
    }

    将ArithmeticCalculatorImpl类改到最初的版本,重新运行测试类,我们可以得到同样的结果

     3.AOP中的一些概念及AOP的好处

    AOP中的一些概念:

      切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象,如上图日志切面和验证切面(在后面还会讲spring aop切面的优先级)。

      通知(Advice):切面必须要完成的工作。(切面里的每一个方法,比如前置日志和后置日志)

      目标(Target):被通知的对象(被代理对象)

      代理(Proxy):向目标对象应用通知之后创建的对象(代理对象)

      连接点(JoinPoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示程序执行点;相对点表示的方位。例如ArithmethicCalculator#add()方法执行前的连接点执行点为ArithmethicCalculator#add()方位为该方法执行前位置。

      切点(Pointcut):每个类都拥有多个连接点:例如ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序中客观存在的事物,AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一一对应的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

    AOP的好处:

      每个事物逻辑位于一个位置,代码不分散,便于维护和升级

      业务模块更加简洁只包含核心业务代码。

  • 相关阅读:
    [转]Spring——jar包详解
    [转]Null value was assigned to a property of primitive type setter of"原因及解决方法
    [转]Spring3 MVC + jQuery easyUI 做的ajax版本用户管理
    [转]SpringMVC+Hibernate+Spring 简单的一个整合实例
    Spring MVC配置
    [转] 使用Spring MVC构建REST风格WEB应用
    NYOJ 467 中缀式变后缀式
    HBase源代码分析之HRegionServer上MemStore的flush处理流程(二)
    树中两个节点的最低公共祖先
    计算机图形学(一) 视频显示设备_1_CRT原理
  • 原文地址:https://www.cnblogs.com/fengyun2019/p/10877068.html
Copyright © 2020-2023  润新知