Java JDK动态代理

简介: Java JDK动态代理

Java JDK动态代理

前言

本篇文章主要讲解的是什么是JAVA JDK版本的动态代理,如何使用JAVA JDK版本的动态代理。对于JAVA JDK版本的动态代理的原理不会过多的描述。本篇文章旨在如果使用JDK动态代理。

必须理解和掌握的前提概念:

为了更好的掌握和使用JDK 动态代理需要了解一下概念。

1.什么是代理(proxy)

例如:由于客户端不想或者不能直接访问一个对象,此时可以通过一个称为代理的第三者来实现间接访问对象,这个过程为一个代理。

更加生活化的实例是:

例如:代购,如:在当地买不到某件商品,又或者因为当地的这件商品的价格比其他地区的贵,所以托人或者机构在其他地区购买该商品,并携带回来。

2.静态代理和动态代理

可以提前学习一下设计模式中的代理模式,会对一下内容会有一个更深的理解。


静态代理
传统的代理模式中客户端通过Proxy类调用RealSubject类和方法,同时还在代理类还可以封装其他方法。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在,代理类的接口和所代理方法都已明确制定。每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法,都被固定,这种代理称为静态代理。

静态代理的局限性:如果需要为不同的真实主题类提供代理类或者代理一个真实主题类的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。


动态代理
(dynamic proxy)可以让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的RealSubject而且可以代理不同的方法。

JDK中提供的动态代理智能代理一个或者多个接口,如果需要动态代理具体类或者抽象类,可以是用CGLib(Code Generation Library)等工具

如何实现JAVA JDK动态代理

掌握了基本前提概念之后,还有几点我们需要进行明确的。

先看代理模式简易版类图:

1.为了让客户端能够一致性的对待RealSubject对象和Proxy对象,引入抽象类Subject

2.为了保持行为的一致性,Proxy和realSubject通常会实现相同的接口

3.对于接口的理解:接口这个术语有多个意思,1.接口(API)某个类的接口或该类持有的方法的集合。2.使用关键字interface声明的代码。

—————————————————————————————————————————

Java JDK动态代理

从JDK1.3开始,java语言提供了对动态代理的支持,java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类。

1.Proxy类

proxy类提供了用于创建动态代理类和实例对象的方法,他是说创建的动态代理类的父类。

常用方法:

public static Class<?> getProxyClass(ClassLoader loader,
                                     Class<?>... interfaces)

该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

该方法用于返回一个动态创建的代理类的实例,方法中的第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。

2.InvocationHandler接口

InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法:

public Object invoke(Object proxy, Method method, Object[] args)

该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含3个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。
动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时调用请求会请求自动转发给InvocationHandler对象的invoke()方法,有invoke()方法来实现对请求的统一处理。

实例

某软件公司要为公司SPM系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现在使用动态代理进行设计和实现。

代码结构:总共有四个类

IAbstractUserDAO

package test;
/**
 * @author : [WangWei]
 * @version : [v1.0]
 * @className : ISubject
 * @description : [抽象用户DAO类,抽象主题类]
 * @createTime : [2022/9/18 8:27]
 * @updateUser : [WangWei]
 * @updateTime : [2022/9/18 8:27]
 * @updateRemark : [描述说明本次修改内容]
 */
public interface IAbstractUserDAO {
   public Boolean findUserById(String userId);
}

UserDAO

package test;
/**
 * @author : [WangWei]
 * @version : [v1.0]
 * @className : RealSubject
 * @description : [用户DAO类,具体主题类]
 * @createTime : [2022/9/18 8:27]
 * @updateUser : [WangWei]
 * @updateTime : [2022/9/18 8:27]
 * @updateRemark : [描述说明本次修改内容]
 */
public class UserDAO implements IAbstractUserDAO {
    @Override
    public Boolean findUserById(String userId) {
        return null;
    }
}

DAOLogHandler

package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;
/**
 * @author : [WangWei]
 * @version : [v1.0]
 * @className : InvocationHandlerImpl
 * @description : [请求处理程序类]
 * @createTime : [2022/9/18 8:31]
 * @updateUser : [WangWei]
 * @updateTime : [2022/9/18 8:31]
 * @updateRemark : [描述说明本次修改内容]
 */
public class DAOLogHandler implements InvocationHandler {
    private Calendar calendar;
    private Object subject;
    //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象
    public DAOLogHandler(Object subject) {
        this.subject = subject;
    }
   /*
    * @version V1.0
    * Title: invoke
    * @author Wangwei
    * @description 实现invoke()方法,调用在真实主题类中定义的方法
    * @createTime  2022/9/25 10:21
    * @param [proxy, method, args]
    * @return java.lang.Object
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用记录方法调用时间
        this.beforeInvoke();
       Object result =method.invoke(subject,args);//转发调用
        //标记方法调用结束
        this.afterInvoke();
        return  result;
    }
    //记录方法调用时间
    private void beforeInvoke(){
       calendar=new GregorianCalendar();
       int hour=calendar.get(Calendar.HOUR_OF_DAY);
       int minute=calendar.get(Calendar.MINUTE);
       int second=calendar.get(Calendar.SECOND);
       String time=hour+":"+minute+":"+second;
        System.out.println("调用时间:"+time);
    }
    private void afterInvoke(){
        System.out.println("方法调用结束");
    }
}

Client

package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
 * @author : [WangWei]
 * @version : [v1.0]
 * @className : DynamicProxyDemonstration
 * @description : [客户端测试类]
 * @createTime : [2022/9/18 8:36]
 * @updateUser : [WangWei]
 * @updateTime : [2022/9/18 8:36]
 * @updateRemark : [描述说明本次修改内容]
 */
public class Client {
    public static void main(String[] args) {
        InvocationHandler handler=null;
        IAbstractUserDAO userDAO=new UserDAO();
        handler=new DAOLogHandler(userDAO);
        IAbstractUserDAO proxy=null;
        //动态创建代理对象,用于代理一个IAbstractUserDAO类型的真实对象
        proxy=(IAbstractUserDAO) Proxy.newProxyInstance(IAbstractUserDAO.class.getClassLoader(),new Class[]{IAbstractUserDAO.class},handler);
        proxy.findUserById("David");
    }
}

参考资料:

Java JDK 动态代理(AOP)使用及实现原理分析

书籍:《java设计模式》–刘伟

如果博主的文章对您有所帮助,可以评论、点赞、收藏,支持一下博主!!!

目录
相关文章
|
8天前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
24 1
|
6天前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
27天前
|
安全 Java API
【Java炸裂更新】JDK 22:区域锚定引领G1垃圾回收革命,性能飙升新高度!
【9月更文挑战第6天】JDK 22的发布,标志着Java在性能优化和垃圾回收技术上的又一次重大突破。区域锚定技术的引入,不仅提升了G1垃圾收集器的效率,也为Java应用的性能提升注入了新的动力。随着Java生态的不断发展和完善,我们有理由相信,Java将继续在编程界保持其铁打英雄的地位,为开发者们带来更多惊喜和可能。 让我们共同期待,Java在JDK 22的引领下,开启一个全新的性能飙升时代!
63 17
|
24天前
|
Java API 开发者
【Java模块化新飞跃】JDK 22模块化增强:构建更灵活、更可维护的应用架构!
【9月更文挑战第9天】JDK 22的模块化增强为开发者构建更灵活、更可维护的应用架构提供了强有力的支持。通过模块化设计、精细的依赖管理和丰富的工具支持,开发者可以更加高效地开发和管理应用,提高应用的性能和可维护性。
54 10
|
26天前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
48 11
|
26天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
49 11
|
24天前
|
监控 IDE Java
【Java性能调优新工具】JDK 22性能分析器:深度剖析,优化无死角!
【9月更文挑战第9天】JDK 22中的性能分析器为Java应用的性能调优提供了强大的支持。通过深度集成、全面监控、精细化分析和灵活报告生成等核心优势,性能分析器帮助开发者实现了对应用性能的全面掌控和深度优化。在未来的Java开发过程中,我们期待性能分析器能够继续发挥重要作用,为Java应用的性能提升贡献更多力量。
|
27天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
45 11
|
24天前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。
|
26天前
|
Java API 数据处理
【Java的SIMD革命】JDK 22向量API:释放硬件潜能,让Java应用性能飙升!
【9月更文挑战第7天】 JDK 22向量API的发布标志着Java编程语言在SIMD技术领域的重大突破。这一新特性不仅释放了现代硬件的潜能,更让Java应用性能实现了飙升。我们有理由相信,在未来的发展中,Java将继续引领编程语言的潮流,为开发者们带来更加高效、更加强大的编程体验。让我们共同期待Java在SIMD技术的推动下开启一个全新的性能提升时代!
下一篇
无影云桌面