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设计模式》–刘伟

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

目录
相关文章
|
4月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
94 2
|
2月前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
118 53
|
2月前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
110 1
|
2月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
84 5
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
87 1
|
3月前
|
缓存 Java Maven
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
如何解决Java项目中因JDK版本不匹配导致的编译错误,包括修改`pom.xml`文件、调整项目结构、设置Maven和JDK版本,以及清理缓存和重启IDEA。
69 1
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
|
3月前
|
Java Spring 数据库连接
[Java]代理模式
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
47 0
[Java]代理模式
|
2月前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
3月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
28 1
|
3月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
33 1