当前位置:首页 > CN2资讯 > 正文内容

由反射引出的Java动态代理与静态代理

18小时前CN2资讯

写在开头

在《深入剖析Java中的反射,由浅入深,层层剥离!》这篇文章中我们讲反射时,曾提到过Java的动态代理中使用了反射技术,那么好,今天我们要就着反射的索引,来学习一下Java中的代理!

代理模式

在Java中有多达23种的设计模式(后面Java基础更新完后,会找个时间详细的去写写这些设计模式),恰当的设计模式的使用能够提升代码的效率,简化代码的复杂性。

而今天我们要说的代理模式就是其中之一,所谓代理是为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

大白话:买房的(客户方),房产销售(代理方),卖房的(委托方) 在Java中有静态代理和动态代理两种实现方式,继续放下看!!!

静态代理

所谓静态代理,一般是针对编译期就已经完成了接口,实现类,代理类的定义,我们对目标对象的增强需要手工去完成,一个目标对象就要有个代理类,非常不灵活。

静态代理的实现步骤

1,因为代理类与被目标对象有相似的行为(共同),所以我们先创建一个接口。

public interface SaleHouse { String saleHouse(); }

2,提供接口的实现类,当做目标对象

public class SaleHouseImpl implements SaleHouse{ @Override public String saleHouse() { return "我要卖房子啦!!!"; } }

3,代理类同样也要实现接口,并在目标方法前后做一些控制操作

public class SaleHouseProxy implements SaleHouse{ private SaleHouse saleHouse; //提供一个包含目标对象的有参构造 public SaleHouseProxy(SaleHouse saleHouse) { this.saleHouse = saleHouse; } @Override public String saleHouse() { //调用方法前,我们可以加一些自己的控制 System.out.println("我要收代理费!!!"); System.out.println("--------------------"); String s = saleHouse.saleHouse(); System.out.println(s); System.out.println("--------------------"); //调用方法后,我们依旧可以操作 System.out.println("我要拿提成!!!"); return "这就是静态代理"; } }

4,客户端调用代理类,并传入目标对象

public class Test { public static void main(String[] args) throws FileNotFoundException { //客户端调用静态代理 SaleHouse saleHouse = new SaleHouseImpl(); SaleHouseProxy saleHouseProxy = new SaleHouseProxy(saleHouse); saleHouseProxy.saleHouse(); } }

5,控制台查看打印结果

我要收代理费!!! -------------------- 我要卖房子啦!!! -------------------- 我要拿提成!!!

动态代理

其实无论是静态代理还是静态代理,在我们的日常开发中,使用的都是很多,但对于SpringAop、RPC等框架来说,动态代理发挥着相当大的作用,动态代理具有:运行时控制,灵活性更好的特点。 那怎么实现动态代理呢? 如下三种方式:

JDK 动态代理 CGLib 动态代理 使用 Spring aop 模块完成动态代理功能 //今天先不说这个

JDK动态代理

实现步骤: 1,定义一个接口及其实现类; 代码同静态代理中步骤1,步骤2;

2,自定义 InvocationHandler (调用处理器)并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

public class JdkDynamicInvocationHandler implements InvocationHandler { //代理类中的真实对象 private final Object target; public JdkDynamicInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用方法前,我们可以加一些自己的控制 System.out.println("我要收代理费!!!"); Object invoke = method.invoke(target, args); //调用方法后,我们依旧可以操作 System.out.println("我要拿提成!!!"); return invoke; } }

3,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;其实,这一步也可以写在第2步的代码里,不过为了代码的可读性,我们进行解耦实现! 3.1,定义一个工厂类,在工厂类中通过Proxy.newProxyInstance()方法获取某个类的代理对象

public class JdkDynamicProxyFactory { public static Object getProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标类的类加载器 target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个 new JdkDynamicInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler ); }

3.2,客户端传入目标对象,实现代理扩展

//客户端调用静态代理 SaleHouse proxySaleHouse = (SaleHouse) JdkDynamicProxyFactory.getProxy(new SaleHouseImpl()); proxySaleHouse.saleHouse();

4,控制台输出

我要收代理费!!! 我要卖房子啦!!! 我要拿提成!!!

【扩展】 关于Proxy类的静态工厂方法newProxyInstance()如何创建代理实例的过程,感兴趣的可以去读源码,或者参考下面这篇文章《代理模式在开源代码中的应用》

CGLIB 动态代理

其实在JDK动态代理中有一个弊端,那就是只能代理接口或接口的实现类,那么未实现任何接口的类就不能代理了吗?答案是否定的,因为咱们有CGLIB!

CGLIB (Code Generation Library) 是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,CGLIB 通过继承方式实现代理。

实现步骤: 1,引入cglib依赖 因为是第三方实现的动态代理,所以在使用前先引入依赖包

<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>

2,定义一个类;

public class Person { public void eat(){ System.out.println("我在吃饭!!!"); } }

3,自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

public class CglibMethodInterceptor implements MethodInterceptor { /** * @param o 被代理的对象(需要增强的对象) * @param method 被拦截的方法(需要增强的方法) * @param args 方法入参 * @param methodProxy 用于调用原始方法 */ @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //调用方法前,我们可以加一些自己的控制 System.out.println("饭前先洗手"); Object object = methodProxy.invokeSuper(o, args); //调用方法前,我们可以加一些自己的控制 System.out.println("饭后要擦嘴"); return object; } }

4,创建一个工厂类,用来构建代理对象,通过 Enhancer 类的 create()方法实现;

public class CglibProxyFactory { public static Object getProxy(Class<?> clazz) { // 创建动态代理增强类 Enhancer enhancer = new Enhancer(); // 设置类加载器 enhancer.setClassLoader(clazz.getClassLoader()); // 设置被代理类 enhancer.setSuperclass(clazz); // 设置方法拦截器 enhancer.setCallback(new CglibMethodInterceptor()); // 创建代理类 return enhancer.create(); } }

5、客户端调用,通过反射传入Person类信息

public static void main(String[] args) throws FileNotFoundException { //客户端调用静态代理 Person person = (Person) CglibProxyFactory.getProxy(Person.class); person.eat(); }

6、输出

饭前先洗手 我在吃饭!!! 饭后要擦嘴

OK,终于码完了动态代理,自己还去看了很久的源码,头昏脑涨!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得<font color="HotPink ">留言+点赞+收藏</font>呀。原创不易,转载请联系Build哥!

    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/24727.html

    分享给朋友:

    “由反射引出的Java动态代理与静态代理” 的相关文章

    宝塔安装全攻略:轻松管理你的服务器与网站

    宝塔面板,凭借其简单易用的特性,已经成为很多用户搭建和管理网站的首选工具。作为一款开源的服务器管理软件,宝塔面板提供了丰富的功能和灵活的操作方式,让无论是新手还是经验丰富的用户都能轻松上手。我在使用宝塔面板的过程中,深刻体会到它带来的便利和高效。 功能与特点 宝塔面板最大的一大优势在于其直观的用户界...

    Cloudflare 菲律宾节点:提升网络体验与速度的解决方案

    在当今数字化时代,每一个在线体验都至关重要。CloudFlare的出现,正是为了满足这一迫切的需求。作为全球知名的CDN(内容分发网络)服务提供商,CloudFlare不仅致力于加速网站的加载速度,也为用户提供安全防护服务。借助全球分布的节点,CloudFlare能够将用户请求快速而安全地送达目的地...

    了解尼日利亚IP地址及其获取方法

    尼日利亚IP地址概述 尼日利亚的IP地址数量颇具规模,达到3,196,160个,这在全球范围内占有0.0938%的比重,位居第61位。这意味着,尽管尼日利亚在全球互联网上不是最大的参与者,但它的IP资源依然相对丰富,给予了很多用户连接世界的机会。听起来兴奋吧?这些IP地址为本地互联网用户和企业提供了...

    Debian 修改 DNS 的详细步骤与常见问题解决方案

    在讨论 Debian 中的 DNS 修改前,我想先和大家分享一些关于 DNS 的基本信息。DNS(Domain Name System)是互联网的“电话簿”,它将我们可读的网站地址(如 www.example.com)转换为计算机能够理解的 IP 地址。这一过程对于我们浏览网页、发送邮件等操作至关重...

    跑步的全面指南:基础知识、路线选择与心理技巧

    跑步的基础知识 跑步,这项简单又有效的运动,拥有着悠久的历史和丰富的文化背景。追溯到古代,跑步不仅是人类生存的必要技能,更是一项重要的竞技活动。历史上,古希腊的奥林匹克运动会中,长跑是最受欢迎的项目之一。而在中国,长跑也早在古代就已经成为士兵训练和民间竞技的一部分。随着时代发展,跑步逐渐演变为一种大...

    如何购买域名:选择与交易的完整指南

    购买域名是启动网站或在线服务不可或缺的一步。对于很多人来说,域名不仅是网站的门面,更是品牌的形象。想象一下,拥有一个简洁、易记的域名能够让用户更轻松地找到你的服务或产品,并在他们心中留下深刻的印象。 域名的功能多种多样,它不仅能帮助你构建互联网身份,还能影响网站在搜索引擎中的排名。因此,选择一个合...