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的好处:

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

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

 

转载于:https://www.cnblogs.com/fengyun2019/p/10877068.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值