设为首页 加入收藏

TOP

时隔多年,这次我终于把动态代理的源码翻了个地儿朝天(一)
2023-07-25 21:44:05 】 浏览:119
Tags:时隔多 终于把

本文内容整理自 博学谷狂野架构师

file

动态代理简介

? Proxy模式是常用的设计模式,其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

用户可以更加结构图,自己编码完成Proxy模式。这种实现称为静态代理。

? Java提供了java.lang.reflect.Proxy类与InvocationHandler接口,配合反射,可以实现动态代理。静态代理的代理类与代理操作,都是事先编码,运行过程种无法修改代理结构。动态代理的代理与代理操作,都是在运行过程中,动态生成,可以在运行过程中,修改代理结构,符合面向对象的开闭原则。

? 最最最主要的原因就是,在不改变目标对象方法的情况下对方法进行增强,比如,我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截,等等...

? 动态代理用于将在不需要修改原代码的情况下进行代码的增加,spring中的AOP,事务,都是使用动态代理来实现的,我们天天都在使用动态代理只是自己不知道而已。

动态代理三大要素

  1. 需要定义一个接口,java动态代理类只能代理接口(不支持抽象类),如果没有接口就要使用cjlib

  2. 需要一个实现类继承这个接口

  3. 编写一个增强类实现 InvocationHandler接口,代理类都需要实现InvocationHandler接口的invoke方法

一个例子

先定义一个接口

定义一个海外代购的接口

/**
 * 海外代购
 */
public interface Buying {
    public String buy();
}

编写一个实现类

实现类实现接口

public class BuyingImpl implements Buying {
    @Override
    public String buy() {
        System.out.println("开始逻辑处理");
        return "买了个锤子";
    }
}

编写一个增将类

编写一个增强类,主要要包裹一个需要需要增强的对象也就是我们的BuyingImpl,并实现InvocationHandler接口,在invoke方法中写增强实现

/**
 * 海外代购增强类
 * 注意实现 InvocationHandler
 *  动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。
 *  该invoke方法就是调用被代理接口的所有方法时需要调用的 。
 */
public class BuingHandler implements InvocationHandler {

    /**
     * 包裹一个需要增强的目标对象
     */
    private Object targetObject;

    public BuingHandler(Object targetObject){
        this.targetObject = targetObject;
    }
    /**
     * 获取代理类
     *
     * @return
     */
    public Object getProxy() {
        /**
         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
         * 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
         * 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
         * 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
         * 根据传入的目标返回一个代理对象
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(), this);
    }

    /**
     * 关联的这个实现类的方法被调用时将被执行
     * InvocationHandler接口的方法
     *
     * @param proxy 表示代理对象
     * @param method 示原对象被调用的方法
     * @param args 表示方法的参数
     * @return 返回的是对象的一个接口
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强");
        //反射调用原始的需要增强的方法
        Object value = method.invoke(targetObject, args);
        System.out.println("后置增强");
        return value;
    }
}

这里面要注意 method 是我们需要增强的方法,args 是我们需要增强的参数数组

编写Main方法

public static void main(String[] args) {
        //创建BuingHandler 类
        BuingHandler buingHandler = new BuingHandler(new BuyingImpl());
        //获取代理对象
        Buying buying = (Buying) buingHandler.getProxy();
        //调用具体接口
        String value = buying.buy();
        System.out.println(value);
    }

输出

前置增强
开始逻辑处理
后置增强
买了个锤子

我们就这样实现了动态代理,我们没有修改原有代码的情况下做了增强

我们实现了 其那只以及后置增强

我们运行下看下接口对象

file
我们看到实际对象是$Proxy0,我们发现动态代理给我们换了一个对象,我们要研究下他是怎么实现的

源码实现

读源码首先找到入口,没有不得入口就像无头的苍蝇,苍蝇还不叮无缝的蛋呢

下面内容有点多,也有点绕,请跟着思路来一点点解析

1、首先找到入口

我们创建代理对象调用的是

Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(), this);

所以我们先从Proxy.newProxyInstance开始入手

2、newProxyInstance方法

进入newProxyInstance方法内部

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException {
        //增强实现不能为空,为空就抛出异常
        Objects.requireNonNull(h);
        //对接口数组进行clone
        final Class<?>[] intfs = interfaces.clone();
        //进项权限检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇集合 P1 集合 下一篇Java开发工具IntelliJ IDEA 2020...

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目