【Java】 解读Spring AOP 实现原理 模拟 Spring 代理工厂代码实现

说到Spring作为Java程序员大家都了解,每天都在用,但你真的知道它的底层实现原理吗?今天我们就来探讨一下Spring AOP的底层实现原理

概念 什么是AOP (这里我只截取有用部分不码太概念的东西)

AOP Aspect Oriented Programming 的缩写 意为面向切面编程

可以在不修改源码的前提下,对程序进行增强

Spring框架的AOP底层实现

1.Spring框架的AOP技术底层也是采用的代理技术,代理方式提供了两种

2.JDK的动态代理
    必须是面向接口的,只有实现了具体接口的类才能生成代理对象
3.CGLib动态代理
    对没有实现接口的类 可以产生代理 产生这个类的子类的方式

因为cglib需要Spring的环境 包括我们的测试需要Junit这里我们在pom加入如下依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>5.2.0.RELEASE</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.0.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

JDK动态代理

首先我们要创建一个接口(Person.class) 一个类(Student.class)  类实现接口并重写里面的方法 代码如下

package com.it520.springAop.JDK;

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public interface Person {

    void studyJava();

    void studyPython();
}
package com.it520.springAop.JDK;

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public class Student implements Person {

    @Override
    public void studyJava() {
        System.out.println("学习Java");
    }

    @Override
    public void studyPython() {

        System.out.println("学习Python");

    }
}

如上 我们要对 studyJava()方法进行增强 比如我在学习Java之前要先吃饭 学习完Java之后要睡觉 这时候我们就要对目标对象的方法进行增强 代码如下 (注释写的很详细)

package com.it520.springAop.JDK;

import org.junit.jupiter.api.Test;

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

/**
 * @author 玛丽莲梦明
 * @描述 JDK动态代理测试类
 * @date 2020-09-02
 */
public class JdkProxyTest {


    @Test
    public void TEST_01() throws Exception {

        // 这个就是目标对象 我们要对它进行增强
        Person person = new Student();

        // classLoader 后面会专门写一篇博客介绍ClassLoader的工作原理
        ClassLoader classLoader = this.getClass().getClassLoader();

        // 通过反射获取该类实现的所有接口
        Class[] interfaces = person.getClass().getInterfaces();

        // 将目标对象作为参数传给 MyInvocationHandler
        MyInvocationHandler handler = new MyInvocationHandler(person);
        
        // 生成代理对象 该对象已经增强
        Person o = (Person) Proxy.newProxyInstance(classLoader, interfaces, handler);
        o.studyJava();


    }

    /**
     * 创建内部类 去实现 InvocationHandler
     */
    class MyInvocationHandler implements InvocationHandler {

        // 需要传入的目标对象 这里我们使用接口 因为接口更加灵活
        Person person;

        // 创建构造方法 并传入目标对象
        public MyInvocationHandler(Person person) {

            this.person = person;
        }


        /**
         * @param proxy  是当前对象即代理对象
         * @param method 当前调用的方法 (目标方法)
         * @param args   实参
         * @return 代理对象
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            System.out.println("我先吃饭");
            this.person.studyJava();
            System.out.println("我要睡觉");


            return null;
        }
    }

}

输出结果如下

我先吃饭
学习Java
我要睡觉

这里我们看到 我们的目标对象 person实现了功能的增强

Cglib动态代理

不多解释 代码如下

package com.it520.springAop.Cglib;

import com.it520.springAop.JDK.Student;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author 玛丽莲梦明
 */
public class CglibProxyTest {


    @Test
    public void TEST_01() {

        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(Student.class);

        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {

            @Override // 代理对象方法执行 回调函数就会执行
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                // 通过反射获取方法名
                if (method.getName().equals("studyJava")) {

                    System.out.println("我先吃饭");
                }
                // MethodProxy底层对Method进行了一层代理
                Object invoke= methodProxy.invokeSuper(o, objects);

                System.out.println("我要睡觉");
                
                return invoke;
            }
        });

        // 生成代理对象
        Student o = (Student) enhancer.create();

        o.studyJava();
    }
}

输入结果如下

我先吃饭
学习Java
我要睡觉

以上代码并没有什么难度,但看懂以后,恭喜你对AOP的实现原理已经肆意妄为了

代理工厂 (ProxyFactory)

AOP 中 ProxyFactory 的子类有 ProxyCreatorSupport AdvisedSupport ProxyConfig 其中核心是 ProxyCreatorSupport 此类主要初始化了具体动态代理方案 其他 AdvisedSupport ProxyConfig 主要是围绕 AOP 相关配置进行封装

上面那句话是我在别的博客抄的 但很能体现出ProxyFactory在AOP中的地位,直接贴源码看着乱七八糟的,我们来简单的模拟一下ProxyFactory的实现

首先定义 前置通知接口 BeforeAdvise

package com.it520.springAop.factory;

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public interface BeforeAdvise {

    /**
     * 前置通知
     */
    void doBefore();
}

然后定义后置通知接口

package com.it520.springAop.factory;

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public interface AfterAdvise {

    /**
     * 后置通知
     */
    void doAfter();
}

再然后定义我们的主类 ProxyFactory

package com.it520.springAop.factory;

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

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public class ProxyFactory {

    // 这是一个目标对象 即万物
    private Object target;
    // 前置通知
    private BeforeAdvise beforeAdvise;
    // 后置通知
    private AfterAdvise afterAdvise;


    // 这就是我们创建代理对象的主要方法
    public Object createProxyObject() {

        ClassLoader classLoader = this.getClass().getClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                // 开启前置通知
                if (beforeAdvise != null) {
                    beforeAdvise.doBefore();
                }
                /**
                 * 执行目标方法 并拿到返回值 作为该方法的返回值
                 * @param target 目标对象
                 * @param args 实参
                 */
                Object invoke = method.invoke(target, args);

                // 开启后置通知
                if (afterAdvise != null) {

                    afterAdvise.doAfter();
                }
                
                // 返回代理对象
                return invoke;
            }
        };

        /**
         * 这个就是生成的代理对象
         */
        Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, handler);

        return newProxyInstance;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public BeforeAdvise getBeforeAdvise() {
        return beforeAdvise;
    }

    public void setBeforeAdvise(BeforeAdvise beforeAdvise) {
        this.beforeAdvise = beforeAdvise;
    }

    public AfterAdvise getAfterAdvise() {
        return afterAdvise;
    }

    public void setAfterAdvise(AfterAdvise afterAdvise) {
        this.afterAdvise = afterAdvise;
    }
}

定义完成 我们来测试一下 代码中注解写的很全面我就不再赘述了

package com.it520.springAop.factory;

import com.it520.springAop.JDK.Person;
import com.it520.springAop.JDK.Student;
import org.junit.jupiter.api.Test;

/**
 * @author 玛丽莲梦明
 * @描述
 * @date 2020-09-02
 */
public class ProxyFactoryTest {


    @Test
    public void TEST_01() {

        ProxyFactory factory = new ProxyFactory();

        // 设置目标对象 这里我们还用之前JDK动态代理的那个Student对象
        factory.setTarget(new Student());
        
        // 设置前置通知内容 可以是任意方法 比如记录日志
        factory.setBeforeAdvise(new BeforeAdvise() {
            @Override
            public void doBefore() {
                System.out.println("我是前置通知");
            }
        });
        // 设置后置通知内容
        factory.setAfterAdvise(new AfterAdvise() {
            @Override
            public void doAfter() {
                System.out.println("我是后置通知");
            }
        });

        // 必须用接口去接收返回值
        Person proxyObject = (Person) factory.createProxyObject();
        
        // 执行接口中方法 代理方法就会执行
        proxyObject.studyJava();
        proxyObject.studyPython();

    }
}

执行后的打印结果如下

我是前置通知
学习Java
我是后置通知
我是前置通知
学习Python
我是后置通知

解释

ProxyFactory类并没有指定具体要增强哪个对象,全部都是动态获取,也就是说我们定义的任何类传入都会得到增强,但增强的内容需要我们自己定义,这也就是Spring ProxyFactory实现的原理

另外说一点 对哪个方法增强 我们可以在invoke方法中使用 method.getName()获取对应的方法(参照Cglib增强部分代码) 选择性增强

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值