JAVA代理,静态,动态详解

目录
  • 代理
    • 静态代理
    • 动态代理
      • JDK动态代理
      • CGLib动态代理
  • 总结

代理

其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象 (委托类) 中介作用。

按照代理的创建时期,代理类可以分为两种:

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态:在程序运行时运用反射机制动态创建而成。

静态代理

Subject: 代理类和被代理类实现同样的接口

Proxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理

RealSubject:被代理类,可以在其内做一些访问权限控制,额外的业务处理

Client:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性

代码实现

UserDAO 代理和被代理的公共的接口(Subject)

public interface ProxyDao {
    boolean insert(String name);
}

UserDAOImpl 被代理类(RealSubject)

public class ProxyDaoImpl implements ProxyDao {
    @Override
    public boolean insert(String name) {
        System.out.println("insert name=" + name);
        return true;
    }
}

ProxyByInterface 代理类,通过实现接口方式实现代理方式(Proxy)

public class ProxyByInterface implements ProxyDao {
    private ProxyDao proxyDao;
    public ProxyByInterface(ProxyDao proxyDao) {
        this.proxyDao = proxyDao;
    }
    @Override
    public boolean insert(String name) {
        System.out.println("before insert by interface");
        return proxyDao.insert(name);
    }
}

ProxyByExtend 代理类,通过继承被代理类实现的代理方式(Proxy)

public class ProxyByExtend extends ProxyDaoImpl{
    private ProxyDaoImpl proxyDao;
    public ProxyByExtend(ProxyDaoImpl proxyDao) {
        this.proxyDao = proxyDao;
    }
    @Override
    public boolean insert(String name) {
        System.out.println("before insert by extend");
        return proxyDao.insert(name);
    }
}

获取代理对象客户端(Client)

public class Client {
    public static void main(String[] args) {
        ProxyDaoImpl proxyDao = new ProxyDaoImpl();
        //和被代理类实现同个接口方式进行代理
        ProxyByInterface proxyByInterface = new ProxyByInterface(proxyDao);
        proxyByInterface.insert("zc-Interface");
        //通过继承被代理类方式进行代理
        ProxyByExtend proxyByExtend = new ProxyByExtend(proxyDao);
        proxyByExtend.insert("zc-Extend");
    }
}

好处:

可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。

真正的业务还是交给被代理对象处理的,无须修改原来的类就可以使用代理进行实现。

缺点:

出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态代理

JDK动态代理

Jdk动态代理,针对的是实现接口的类

要求目标对象必须实现接口,因为它创建代理对象的时候是根据接口创建的。被代理对象可以可以实现多个接口,创建代理时指定创建某个接口的代理对象就可以调用该接口定义的方法了。

需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持

//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

代码实现

NameDao 姓名-接口(Subject)

public interface NameDao {
    public boolean addName(String name);
}

AgeDao 年龄-接口(Subject)

public interface AgeDao {
    public boolean addAge(Integer age);
}

NameAndAgeDaoImpl 姓名、年龄实现类(RealSubject)

public class NameAndAgeDaoImpl implements NameDao,AgeDao {
    @Override
    public boolean addName(String name) {
        System.out.println("NameDaoImpl----->" + name);
        return true;
    }
    @Override
    public boolean addAge(Integer age) {
        System.out.println("AgeDaoImpl----->" + age);
        return true;
    }
}

MyInvocationHandler,对接口提供的方法进行增强(Proxy)

public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----------before----------");
        System.out.println("Proxy=" + proxy.getClass());
        System.out.println("method=" + method);
        System.out.println("args=" + Arrays.toString(args));
        //执行目标方法对象
        Object result = method.invoke(target, args);
        System.out.println("----------after----------");
        return result;
    }
}

ProxyFactory 代理工厂

public class ProxyFactory {
    public static Object getProxy(Object proxyObj) {
        /**
         * loader 指定加载jvm运行时动态生成的代理对象的加载器
         * interface 真实对象实现的所有接口
         * h 实现InvocationHandler接口对象
         */
      // return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(),
      // proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj));
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj));
    }
    public static void main(String[] args) {
        NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl());
        AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl());
        nameDao.addName("zc");
        ageDao.addAge(20);
    }
}

思考 为什么需要 实现接口的类,而不是 类

main函数中,运行该语句:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
public static void main(String[] args) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl());
    AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl());
    nameDao.addName("zc");
    ageDao.addAge(20);
}

可以查看 $Proxy0 类:

会发现他已经继承了 Proxy , 之后才是创建的一个(多个)接口;而由于java是 单继承、多接口 的特性,所以JDK动态代理,需要实现接口的类。

CGLib动态代理

CGLIB实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。

所以被代理类一定不能定义为final class并且对于final 方法不能被代理。

实现需要

//MethodInterceptor接口的intercept方法
/**
*obj 代理对象
*method 委托类方法,被代理对象的方法字节码对象
*arg 方法参数
*MethodProxy 代理方法MethodProxy对象,每个方法都会对应有这样一个对象
*/
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
Ehancer enhancer = new Enhancer() //Enhancer为字节码增强器,很方便对类进行扩展
enhancer.setSuperClass(被代理类.class);
enhancer.setCallback(实现MethodInterceptor接口的对象)
enhancer.create()//返回代理对象,是被代理类的子类

代码实现

UserDaoImpl 用户实现类(RealSubject)

public class UserDaoImpl {
    public boolean insert(String name) {
        System.out.println("insert name=" + name);
        return true;
    }
    public final boolean insert1(String name) {
        System.out.println("final insert name=" + name);
        return true;
    }
}

CglibProxy CGLIB代理类(Proxy)

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----------before----------");
        System.out.println("Proxy=" + o.getClass());
        System.out.println("method=" + method);
        System.out.println("args=" + Arrays.toString(objects));
        System.out.println("methodProxy=" + methodProxy);
        //执行目标方法对象
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("----------after----------");
        return result;
    }
}

ProxyFactory 代理工厂

public class ProxyFactory {
    private static Enhancer enhancer = new Enhancer();
    private static CglibProxy cglibProxy = new CglibProxy();
    public static Object getProxy(Class cls) {
        enhancer.setSuperclass(cls);
        enhancer.setCallback(cglibProxy);
        return enhancer.create();
    }
    public static void main(String[] args) {
        UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class);
        userDao.insert("zc");
    }
}

思考

为什么这里面使用 invokeSuper() ,不使用 invoke()

1.Method method 是被代理对象的方法字节码对象。

2.MethodProxy methodProxy 是代理对象的方法字节码对象。

使用 MethodProxy 的好处:

不需要给代理对象传入被代理对象,效率更高。不会出现死循环的问题。

第一点查看代码就可以看出,对第二点进行讲解:

如何出现死循环的现象:

	Proxy.newProxyInstance(xxx, xxx,
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    	...
                      //加入这一句
                    	proxy.toString();
                    	...
                    }
                });

原因:代理对象方法的时候,都会经过拦截器方法。因此,如果在拦截器中再调用代理对象的方法,就会再次进入拦截器,这样就形成了死循环。

**invokeSuper()**方法,可以使用代理对象父类的方法(就是被代理对象)而不必经过拦截器-----详情可以学习:《类加载机制》、《双亲委派模型》

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java静态代理和动态代理的深入讲解

    代理模式 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问. 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层. 代理模式的元素是:共同接口.代理对象.目标对象. 代理模式的行为:由代理对象执行目标对象的方法.由代理对象扩展目标对象的方法. 代理模式的

  • java代理模式(静态代理、动态代理、cglib代理)

    目录 代理模式 静态代理 代码 接口 被代理对象 代理对象 测试 动态代理 代码: 接口 目标对象 代理对象 测试 cglib代理 代码: 目标对象 代理对象 测试 应用 总结 代理模式 代理模式(Proxy Pattern)是一种结构性模式.代理模式为一个对象提供了一个替身,以控制对这个对象的访问.即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 被代理的对象可以是远程对象.创建开销答得对象或需要安全控制得对象.代理模式主要有三种形式,分别

  • Java代理模式实例详解【静态代理与动态代理】

    本文实例讲述了Java代理模式.分享给大家供大家参考,具体如下: 即Proxy Pattern,23种java常用设计模式之一.代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问. Java的代理模式是Java中比较常用的设计模式,分为2中代理:静态代理与动态代理(JDK动态代理和cglib动态代理) 优点: 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可. 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动. 缺点: 很明显的一点

  • Java静态代理与动态代理案例详解

    代理模式 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问. 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层. 代理模式的元素是:共同接口.代理对象.目标对象. 代理模式的行为:由代理对象执行目标对象的方法.由代理对象扩展目标对象的方法. 代理模式的

  • 代理模式:JAVA静态代理和动态代理的实例和实现详解

    目录 前言 静态代理 实现简述 创建human接口 创建接口实现类 创建针对接口实现增强操作的代理 代理实现效果 动态代理 实现简述 要点:向上转型 创建YoungMan接口 创建两个接口实现类 创建动态代理实例对象 代理实现效果 要点:InvocationHandler补充 代理模式和修饰模式的区别 总结 前言 代理模式,我们这里结合JAVA的静态代理和动态代理来说明,类比Spring AOP面向切面编程:增强消息,也是代理模式. 而我们的静态代理和动态代理,与(service)接口和(ser

  • Java动态代理静态代理实例分析

    代理模式:为其他对象提供一种代理以控制某个对象的访问.用在:在某些情况下,一个客户不想或者不能直接访问另一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用,代理对象还可以完成它附加的操作. 例子:就像房东.租客.中介的关系.中介(代理对象)为房东(真实对象)出租房子,租客(客户)通过中介(代理对象)来找房子租房子,中介完成了租房以后可以收取中介费(附加操作). 先看看静态代理模式,通过上面对代理模式的理解,可以了解到代理模式:即不直接通过new一个真实对象来调用方法,而是通过代理对象来

  • JAVA代理,静态,动态详解

    目录 代理 静态代理 动态代理 JDK动态代理 CGLib动态代理 总结 代理 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象 (委托类) 中介作用. 按照代理的创建时期,代理类可以分为两种: 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译.在程序运行前代理类的.class文件就已经存在了. 动态:在程序运行时运用反射机制动态创建而成. 静态代理 Subject: 代理类和被代理类实现同样的接口 Proxy:代理

  • Java中的引用和动态代理的实现详解

    我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的.但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础? 如果去看动态代理的源代码(java.lang.reflect.Proxy),会发现其原理很简单(真正二进制类文件的生成是在本地方法中完成,源代码中没有),但其中用到了一个缓冲类java.lang.reflect.WeakCache<ClassLoader,Class<?>[],Class<?>

  • Java动态代理的示例详解

    目录 定义 分类 案例 需求 方案一:jdk动态代理 方案二:cglib动态代理 分析 总结 定义 动态代理指的是,代理类和目标类的关系在程序运行的时候确定的,客户通过代理类来调用目标对象的方法,是在程序运行时根据需要动态的创建目标类的代理对象. 分类 jdk动态代理 cglib动态代理 案例 需求 苹果公司通过苹果代理商来卖手机 方案一:jdk动态代理 定义抽象接口 /** * 售卖手机的接口(代理模式--抽象角色) * @author:liyajie * @createTime:2022/2

  • Spring JDK动态代理实现过程详解

    这篇文章主要介绍了Spring JDK动态代理实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1. 创建项目 在 MyEclipse 中创建一个名称为 springDemo03 的 Web 项目,将 Spring 支持和依赖的 JAR 包复制到 Web 项目的 WEB-INF/lib 目录中,并发布到类路径下. 2. 创建接口 CustomerDao 在项目的 src 目录下创建一个名为 com.mengma.dao 的包,在该包下

  • Java结构型设计模式中代理模式示例详解

    目录 代理模式 分类 主要角色 作用 静态代理与动态代理的区别 静态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 JDK动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB与JDK动态代理区别 1.执行条件 2.实现机制 3.性能 代理模式 代理模式(Proxy Pattern)属于结构型模式. 它是指为其他对象提供一种代理以控制对这个对象的

  • Java 反射机制实例详解

    Java 反射机制实例详解 一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,Java和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反射.通过反射,Java可以于运行时加载.探知和使用编译期间完全求和的类.生成其对象实体,调用其方法或者对属性设值.所以Java算是一个半动态的语言吧. 反射的概念: 在Java中的反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对

  • Java 爬虫工具Jsoup详解

    Java 爬虫工具Jsoup详解 Jsoup是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址.HTML 文本内容.它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据. jsoup 的主要功能如下: 1. 从一个 URL,文件或字符串中解析 HTML: 2. 使用 DOM 或 CSS 选择器来查找.取出数据: 3. 可操作 HTML 元素.属性.文本: jsoup 是基于 MIT 协议发布的,可放心使用于商业项目. js

  • Java设计模式——工厂设计模式详解

    工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类. 工厂模式的形态 工厂模式主要用一下几种形态: 1:简单工厂(Simple Factory). 2:工厂方法(Factory Method). 3:抽象工厂(Abstract Factory). 简单工厂(Simple Factory) 又叫静态工厂,是工厂模式三中状态中结构最为简单的.主要有一个静态方法,用来接受参数,并根据参数来决定返回实现同一接口的不同类的实例.我们来看一个具体的例子: 假设一家工厂,几生产洗衣

  • Java基础之数组详解

    前言 我们了解数组这个概念之前,我们先思考下面几个问题. 如果我们需要两个数据,那么直接创建两个变量即可 int a; int b; 如果需要五个数据,那么可以创建五个变量 int a; int b; int c; int d; int f; 但如果我们需要100个甚至是1万个数据,那么我们创一万个变量?显然这是不现实的.这个时候就需要我们的数组来起作用!帮我们"批量"创建变量. 由上可以得出:数组的本质就是让我们能"批量"创建相同类型的变量! 一.数组的概念 数组

随机推荐