java代理(java设计模式)

 2025-08-09 13:39:02  阅读 930  评论 0

摘要:一、概述1、什么是代理模式?解释第一遍:代理模式主要由三个元素共同构成:  1)一个接口,接口中的方法是要真正去实现的。  2)被代理类,实现上述接口,这是真正去执行接口中方法的类。  3)代理类,同样实现上述接口,同时封装被代理类对象,帮助被代理类去实现方法

一、概述

1、什么是代理模式?

解释第一遍:代理模式主要由三个元素共同构成:

  1)一个接口,接口中的方法是要真正去实现的。

  2)被代理类,实现上述接口,这是真正去执行接口中方法的类。

  3)代理类,同样实现上述接口,同时封装被代理类对象,帮助被代理类去实现方法。

解释第二遍: 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理

(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。

解释第三遍(如图):

这样应该是明白了,当然代理模式又分为静态代理和动态代理,先看静态代理。

一、静态代理

直接举例(网上有个很不错的例子)

假如一个班的同学要向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长就是代理类(代理学生上交班费),学生就是被代理类或者理解为目标类。

首先,我们创建一个Teacher接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

/** * 创建Teacher接口 */public interface Teacher {    //上交班费    void giveMoney();}

Student类实现Teacher接口。Student可以具体实施上交班费的动作。

public class Student implements Teacher {    private String name;    public Student(String name) {        this.name = name;    }        @Override    public void giveMoney() {       System.out.println(name + "上交班费100元");    }}

StudentsProxy类,这个类也实现了Teacher接口,由于实现了Teacher接口,同时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

/** * 学生代理类,也实现了Teacher接口,保存一个学生实体,这样既可以代理学生产生行为*/public class StudentsProxy implements Teacher{    //被代理的学生    Student stu;    public StudentsProxy(Teacher stu) {        // 只代理学生对象        if(stu.getClass() == Student.class) this.stu = (Student) stu;    }    //代理上交班费,调用被代理 学生的上交班费行为    public void giveMoney() {        System.out.println("张三家里是土豪,应该比其它人交更多的班费!");        stu.giveMoney();        System.out.println("张三班费交的最多,你就是班长了!");    }}

下面测试类ProxyTest,看如何使用代理模式:

public class ProxyTest {    public static void main(String[] args) {        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成        Teacher zhangsan = new Student("张三");                //生成代理对象,并将张三传给代理对象        Teacher monitor = new StudentsProxy(zhangsan);                //班长代理上交班费        monitor.giveMoney();    }}

运行结果:

总结下:

这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式最主要的就是有一个公共接口(Teacher),一个具体的类(Student),

一个代理类(StudentsProxy),代理类持有具体类的实例,代为执行具体类实例方法。 同时可以看到,代理类里除了实现目标类的方法,而且可以在执行前后添加其它方法来起到增强功能。这不就是AOP(面向切面

思想),对于AOP的实现就是基于代理模式,只不过它采用的是动态代理模式。

二、动态代理模式

1、案例说明

public class MyProxy {    //一个接口    public interface IHello{        void sayHello();    }    //目标类实现接口    static class Hello implements IHello{        public void sayHello() {            System.out.println("sayHello......");        }    }    //自定义代理类需要实现InvocationHandler接口    static  class HWInvocationHandler implements InvocationHandler{        //目标对象        private Object target;        public HWInvocationHandler(Object target){            this.target = target;        }        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            System.out.println("------插入前置通知代码-------------");            //执行相应的目标方法            Object rs = method.invoke(target,args);            System.out.println("------插入后置处理代码-------------");            return rs;        }    }    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {       //获取动态代理类        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);        //获得代理类的构造函数,并传入参数类型InvocationHandler.class        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);        //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入        IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));        //通过代理对象调用目标方法        iHello.sayHello();    }}

运行结果:

Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,其方法签名为:newProxyInstance(ClassLoader loader,Class[] instance, InvocationHandler h),如下例:

(方式二)

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {              IHello  ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加载接口的类加载器               new Class[]{IHello.class},      //一组接口               new HWInvocationHandler(new Hello())); //自定义的InvocationHandler       ihello.sayHello();   }

看到这里我们应该思考三个问题?

1)动态代理类是如何创建的?

2) 它是如何调用MyProxy中的invoke的方法的?

3) MyProxy中invoke中的invoke方法又是啥含义?

带着这三问题我们具体分析下。

2、动态代理类是如何创建的?

具体源码我就不分析了,我们用上面的第一种方式,进行断点查看(括号中的就是通过IDEA断点显示的信息)。

        //1、获取动态代理类(proxyClazz:class com.sun.proxy.$Proxy0 )        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);        //2、获得代理类的构造函数(constructor: public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);        //3、通过构造函数来创建动态代理对象(iHello:cn.edu.zju.grs.alufer.albert.jz.dao.MyProxy$Hello@16b4a017)        IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));

通过断点发现:

(1)第一步已经获得了代理对象,代理对象的名称是:$Proxy0

(2)第二步我们发现代理类的构造函数需要传入一个java.lang.reflect.InvocationHandler类

(3)第三步通过构造函数创建对象,构造函数里放到就是HWInvocationHandler类,而它是实现InvocationHandler接口的,所以没毛病。

总的来讲这三步都是通过类的反射机制来实现创建类的。

3、如何调用MyProxy中的invoke的方法

这里就需要看下$Proxy0.class进行反编译后的代码

//代理类名为$Proxy0,继承Proxy,实现IHello接口public final class $Proxy0 extends Proxy implements IHello {       //变量,都是private static Method  XXX    private static Method m3;      private static Method m1;    private static Method m0;    private static Method m2;    //代理类的构造函数,其参数正是是InvocationHandler实例,Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的    public $Proxy0(InvocationHandler var1)  {        super(var1);    }    //接口代理方法    public final void sayHello()  {        try {                        //这里才是关键,通过这里终于知道是如何调用MyProxy中的invoke方法的            super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    //以下Object中的三个方法    public final boolean equals(Object var1)  {        try {            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final int hashCode()  {        try {            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final String toString()   {        try {            return (String)super.h.invoke(this, m2, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    //对变量进行一些初始化工作    static {        try {            //我们发现m3就代表着sayHello()方法            m3 = Class.forName("com.mobin.proxy.IHello").getMethod("sayHello", new Class[0]);            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

看到这里是不是和前面断点的代码对象的获得的信息是一样的。(比如代理类名就叫$Proxy0,构造方法也和上面分析的一样)。总算明白了。

4、MyProxy中invoke中的invoke方法又是啥含义?

其实到这一步就已经变得简单了 Object rs = method.invoke(target,args);执行的方法就是目标类中的sayHello()方法,这个也就一个反射知识。

我针对这个举例:

有一个A类:

package com.jincou.study;public class A {        public void foo(String name) {    System.out.println("Hello, " + name);    }}

TestClassLoad类来反射调用A上的方法

public class TestClassLoad {        public static void main(String[] args) throws Exception {              //获得代理类        Class clz = Class.forName("com.jincou.study.A");        //获得代理类对象        Object o = clz.newInstance();        //获得foo方法         Method m = clz.getMethod("foo", String.class);        //执行foot方法         m.invoke(o,"张三");   }}

运行结果:

那么到这里你就应该明白了,在MyProxy中invoke中的

    //这个method就是sayHello()方法,target是指new Hello()对象(也就是目标类对象),参数为null    Object rs = method.invoke(target,args);

是不是和上面最后一步一个意思,总算明白了。

这里顺便看下invoke源码:

 //这个源码只要知道一点就是Object... args,它表示的是可变参数所以他可以是空或者多个     @CallerSensitive        public Object invoke(Object obj, Object... args)            throws IllegalAccessException, IllegalArgumentException,               InvocationTargetException        {            if (!override) {                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                    Class caller = Reflection.getCallerClass();                    checkAccess(caller, clazz, obj, modifiers);                }            }            MethodAccessor ma = methodAccessor;             // read volatile            if (ma == null) {                ma = acquireMethodAccessor();            }            return ma.invoke(obj, args);        }

总算理解完了,这篇文章并没有很深入的讲底层源码,看源码自己都会被绕进去,到头来可能还是一头雾水。这样的分析还是蛮清晰的。

最后总结下使用动态代理的五大步骤:

1)通过实现InvocationHandler接口来自定义自己的InvocationHandler;

2)通过Proxy.getProxyClass获得动态代理类

3)通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)

4)通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入

5)通过代理对象调用目标方法

版权声明:我们致力于保护作者版权,注重分享,被刊用文章【java代理(java设计模式)】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!;

原文链接:https://www.yxiso.com/zhishi/2038785.html

标签:java代理

发表评论:

关于我们
院校搜的目标不仅是为用户提供数据和信息,更是成为每一位学子梦想实现的桥梁。我们相信,通过准确的信息与专业的指导,每一位学子都能找到属于自己的教育之路,迈向成功的未来。助力每一个梦想,实现更美好的未来!
联系方式
电话:
地址:广东省中山市
Email:beimuxi@protonmail.com

Copyright © 2022 院校搜 Inc. 保留所有权利。 Powered by BEIMUCMS 3.0.3

页面耗时0.1656秒, 内存占用1.93 MB, 访问数据库24次

陕ICP备14005772号-15