0

0

Java动态代理的原理

尚

发布时间:2019-11-29 17:06:40

|

2203人浏览过

|

来源于博客园

转载

Java动态代理的原理

Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。(推荐:java视频教程

代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。下面我们开始动态代理的学习。

动态代理的简要说明

java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class)。

立即学习Java免费学习笔记(深入)”;

一、 InvocationHandler(interface)的描述:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用 一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

这个方法接收三个参数和返回一个Object类型,它们分别代表的意思如下:

proxy: 指代我们所代理的那个真实对象

method: 指代的是我们所要调用真实对象的方法的Method对象

args: 指代的是调用真实对象某个方法时接受的参数

返回的Object是指真实对象方法的返回类型,以上会在接下来的例子中加以深入理解。

the value to return from the method invocation on the proxy instance.

二、 Proxy(Class)的描述:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

Proxy这个类的作用就是用来动态创建一个代理对象。我们经常使用的是newProxyInstance这个方法:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

 参数的理解:

// 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
loader - the class loader to define the proxy class  
// 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
interfaces - the list of interfaces for the proxy class to implement 
// 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
h - the invocation handler to dispatch method invocations to

返回结果的理解: 一个代理对象的实例

a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces 

简单的Java代理

我们创建一个Java项目用于对动态代理的测试与理解,项目结构如下:

1.jpg

动力先锋仿阿里巴巴B2B电子商务系统
动力先锋仿阿里巴巴B2B电子商务系统

前台功能介绍:1、网页首页显示有高级会员推荐,精品推荐,商业机会分类列表,最新供求信息,网站动态,推荐企业,行业动态等;2、商业机会栏目功能有:二级分类,已经带有详细分类的数据库,后台可以更改增加操作,并可以推荐公司,栏目分为分类显示信息,最新的采购、供应、合作和代理信息,搜索时同样按分类,信息,时间,交易类型等搜索;3、展厅展品栏目功能:二级分类,已经带有详细分类的数据库,后台可以更改增加操作,

下载

一、 先定义一个接口Interface,添加两个方法。

package com.huhx.proxy;

public interface Interface {
    void getMyName();

    String getNameById(String id);
}

二、 定义一个真实的实现上述接口的类,RealObject:

package com.huhx.proxy;

public class RealObject implements Interface {
    @Override
    public void getMyName() {
        System.out.println("my name is huhx");
    }

    @Override
    public String getNameById(String id) {
        System.out.println("argument id: " + id);
        return "huhx";
    }
}

三、 定义一个代理对象,也实现了上述的Interface接口:

package com.huhx.proxy;

public class SimpleProxy implements Interface {
    private Interface proxied;

    public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void getMyName() {
        System.out.println("proxy getmyname");
        proxied.getMyName();
    }

    @Override
    public String getNameById(String id) {
        System.out.println("proxy getnamebyid");
        return proxied.getNameById(id);
    }
}

四、 SimpleMain在Main方法中,测试上述的结果:

package com.huhx.proxy;

public class SimpleMain {
    private static void consume(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) {
        consume(new RealObject());
        System.out.println("========================================================");
        consume(new SimpleProxy(new RealObject()));
    }
}

五、 运行的结果如下:

my name is huhx
argument id: 1
name: huhx
========================================================
proxy getmyname
my name is huhx
proxy getnamebyid
argument id: 1
name: huhx

Java的动态代理

完成了上述简单的Java代理,现在我们开始学习Java的动态代理,它比代理的思想更向前一步,因为它可以动态地创建代理并动态的处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。下面我们通过案例来加深Java动态代理的理解:

一、 创建一个继承了InvocationHandler的处理器:DynamicProxyHandler

package com.huhx.dynamicproxy;

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

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        System.out.println("dynamic proxy handler constuctor: " + proxied.getClass());
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("dynamic proxy name: " + proxy.getClass());
        System.out.println("method: " + method.getName());
        System.out.println("args: " + Arrays.toString(args));
        
        Object invokeObject = method.invoke(proxied, args);
        if (invokeObject != null) {
            System.out.println("invoke object: " + invokeObject.getClass());
        } else {
            System.out.println("invoke object is null");
        }
        return invokeObject;
    }
}

 二、 我们写一个测试的Main方法,DynamicProxyMain:

package com.huhx.dynamicproxy;

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

import com.huhx.proxy.Interface;
import com.huhx.proxy.RealObject;

public class DynamicProxyMain {
    public static void consumer(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) throws Exception, SecurityException, Throwable {
        RealObject realObject = new RealObject();
        consumer(realObject);
        System.out.println("==============================");

        // 动态代理
        ClassLoader classLoader = Interface.class.getClassLoader();
        Class[] interfaces = new Class[] { Interface.class };
        InvocationHandler handler = new DynamicProxyHandler(realObject);
        Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler);

        System.out.println("in dynamicproxyMain proxy: " + proxy.getClass());
        consumer(proxy);
    }
}

三、 运行结果如下:

my name is huhx
argument id: 1
name: huhx
==============================
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getNameById
args: [1]
argument id: 1
invoke object: class java.lang.String
name: huhx

从以上输出结果,我们可以得出以下结论:

与代理对象相关联的InvocationHandler,只有在代理对象调用方法时,才会执行它的invoke方法

invoke的三个参数的理解:Object proxy是代理的对象, Method method是真实对象中调用方法的Method类, Object[] args是真实对象中调用方法的参数

Java动态代理的原理

一、 动态代理的关键代码就是Proxy.newProxyInstance(classLoader, interfaces, handler),我们跟进源代码看看:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  // handler不能为空
    if (h == null) {
        throw new NullPointerException();
    }

    final Class[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
  // 通过loader和接口,得到代理的Class对象
    Class cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        final Constructor cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
            // create proxy instance with doPrivilege as the proxy class may
            // implement non-public interfaces that requires a special permission
            return AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return newInstance(cons, ih);
                }
            });
        } else {
       // 创建代理对象的实例
            return newInstance(cons, ih);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
}

二、 我们看一下newInstance方法的源代码:

private static Object newInstance(Constructor cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}

三、 当我们通过代理对象调用 一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

体现这句话的代码,我在源码中没有找到,于是我在测试类的main方法中加入以下代码:

if (proxy instanceof Proxy) {
    InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
    invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null);
    System.out.println("--------------------------------------");
}

这段代码的输出结果如下,与上述中调用代理对象中的getMyName方法输出是一样的,不知道Jvm底层是否是这样判断的:

dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
--------------------------------------

更多java知识请关注java基础教程栏目。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

6

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.8万人学习

Java 教程
Java 教程

共578课时 | 46.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号