likes
comments
collection
share

Spring AOP保姆级超详细解析(上)

作者站长头像
站长
· 阅读数 45

前言

经过我们之前的学习我们对IoC有了一定的了解,并已经学会了IoC的基本使用。接下来,我们将要学习Spring另外一个核心机制————AOP。

AOP

为什么要学习AOP?

AOP全称是Aspect Oriented Programming,意思是面向切面编程

AOP是对面向对象编程的一个补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP。

AOP的优点:

  • 使系统更加容易扩展。
  • 更好的提高代码的复用性。
  • 降低代码模块之间的耦合度。
  • 使业务代码更加简洁纯粹,不参杂其他的非业务代码的影响。
  • 使非业务代码更加集中,与业务代码区分开来,不分散,便于统一管理。

案例分析

我们先写一个不使用AOP的案例

1.首先创建一个Maven工程。

2.在pom.xml文件中引入Spring的AOP依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.11.RELEASE</version>
    </dependency>
</dependencies>

3.定义一个实现两个数加减的接口。

package com;

public interface Cal {
    public int add(int num1,int num2);
    public int sub(int num1,int num2);
}

4.实现方法。

package com.impl;

import com.Cal;

public class CalImpl implements Cal {
    public int add(int num1, int num2) {
        System.out.println("执行加法,参数是("+num1+","+num2+")");
        int result = num1 + num2;
        System.out.println("计算结果是:"+result);
        return result;
    }

    public int sub(int num1, int num2) {
        System.out.println("执行减法,参数是("+num1+","+num2+")");
        int result = num1 - num2;
        System.out.println("计算结果是:"+result);
        return result;
    }
}

5.调用方法。

Cal cal = new CalImpl();
cal.add(1,2);
cal.sub(20,10);

打印结果:

执行加法,参数是(1,2)
计算结果是:3
执行减法,参数是(2010)
计算结果是:10

这样是我们不使用AOP的一个案例,这样我们的业务代码和打印日志的代码就会混杂在一起,并且两个方法有重复的部分,这时候我们就可以使用AOP将其打印日志的代码提取出来统一管理。这就是面向切面编程的一个思想。

如何实现AOP?

使用动态代理来实现AOP,日志信息的打印可以交给代理去做,由代理统一管理,提高代码的可扩展性维护性

代理就类似于我们现实生活中的中介。

什么是动态代理呢?动态代理就是在我们程序运行过程中,动态创建的一个代理类。

我们接下来用动态代理类来实现AOP。

1.创建MyInvocationHandler类。

package com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {
    //接受委托对象
    private Object object = null;

    //返回代理对象
    public Object bind(Object object){
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }

    public Object invoke(Object prox, Method method,Object[] args)throws Throwable{
        System.out.println(method.getName()+"方法的参数是"+ Arrays.toString(args));
        Object result = method.invoke(this.object,args);
        System.out.println(method.getName()+"方法的结果是"+result);
        return result;
    }
}

MyInvocationHandler并不是我们上面所说的动态代理类,这个类是用来创建我们动态代理类的一个类。这里可能会有点绕。我们这里通过实现InvocationHandler这个接口来完成动态代理的功能。

Spring使用Proxy.newProxyInstance类加载器来实现动态代理类的创建。使用object.getClass().getInterfaces()获取委托对象的全部方法。

invoke方法介绍

在代理实例上处理方法调用并返回结果, 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。我们的日志打印信息就写在invoke方法之中。

参数

  • proxy:在其上调用方法的代理实例,也就是动态代理类。
  • method: 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
  • args:包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

2.通过操作代理对象来调用。

Cal cal = new CalImpl();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Cal cal1 = (Cal) myInvocationHandler.bind(cal);
cal1.add(1,2);
cal1.sub(20,10);

通过myInvocationHandler中的bind方法,我们传一个委托对象进去,会返回一个代理对象。从而实现调用代理对象的方法。

3.将业务代码中之前的日志打印信息删除。

打印结果:

add方法的参数是[2, 5]
add方法的结果是7
sub方法的参数是[7, 4]
sub方法的结果是3

这样我们就使用AOP实现了相同功能,并且将业务代码和非业务代码分开,模块之间的耦合性降低了,代码也更加的简洁。

总结

我们面向切面编程是通过反射机制来实现的,我们学习Spring的IoC和AOP更多的是学习一种编程思想,理解了这种思想,技术自然就水到渠成。这就是通过动态代理的方式来实现AOP的过程了。下一篇文章讲解下通过面向对象的方式来实现AOP。

转载自:https://juejin.cn/post/7151432166486851620
评论
请登录