JDK动态代理介绍与使用

2022-07-25 08:46:39

一、介绍

  JDK动态代理是代理模式的一种实现方式。因为它是基于接口来做代理的,所以也常被称为接口代理。在JDK动态代理中有两个重要的角色:

  • InvocationHandler(Interface)
    用户实现这个接口,来编写代理类处理的核心逻辑。
  • Proxy(Class)
    用来创建一个代理实例,此时需要用到上面自定义的InvocationHandler。

二、功能

  动态代理拥有代理模式的基本功能,如:调用真实方法的预处理模块化通用功能。除此之外,还可以在运行时动态创建代理对象,无需针对每个接口编写代理逻辑(针对每个接口都编写对应的处理逻辑,叫做静态代理)。

三、使用步骤

  1. 编写目标接口、目标实现类
  2. 自定义InvocationHandler接口实现类,编写代理处理逻辑
    我们来看看InvocationHandler这个接口的唯一一个方法invoke 方法
/**
 * proxy:代理对象,一般用不到
 * method:指代的是我们所要调用真实对象的某个方法的Method对象
 * args:指代的是调用真实对象某个方法时接受的参数
 **/
Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable;
  1. 创建代理对象
    创建代理对象时,需要关联一个InvocationHandler对象。这样当我们通过代理对象调用目标方法的时候,这个方法的调用就会被转发到InvocationHandler这个接口的 invoke 方法中。

我们一般把创建代理对象的方法,直接写在自定义的InvocationHandler实现类中。

  1. 使用代理调用目标接口中的方法

四、示例

需求:调用对象的每个方法时,在调用前、调用后、调用异常等都打印出一行日志。
对于这种需求,我们就需要把打印日志的功能模块化起来,不能在每个方法中都编写这种打印日志的代码,那样会把通用功能业务功能混合在一起,后续不好维护。

1. 编写目标接口、目标类

目标接口(因为JDK动态代理是基于接口实现的,所以被代理的目标类,一定要实现一个接口。)

publicinterfaceUserService{
    StringgetUserName(Long userId);voidsay(String msg);}

目标类

publicclassUserServiceImplimplementsUserService{@Overridepublic StringgetUserName(Long userId){return"user"+ userId;}@Overridepublicvoidsay(String msg){
        System.out.println("say "+ msg);}}
2. 自定义InvocationHandler
publicclassLogInvocationHandlerimplementsInvocationHandler{/**
     * 1. 目标类
     */privatefinal Object target;publicLogInvocationHandler(Object target){this.target= target;}/**
     * 2. 代理逻辑
     */@Overridepublic Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable{//调用目标方法
        Object result= null;try{//前置通知
            System.out.println(method.getName()+"方法开始调用...");
            result= method.invoke(target, args);//返回通知, 可以访问到方法的返回值
            System.out.println(method.getName()+"方法返回值:"+ result);}catch(Exception e){
            e.printStackTrace();//异常通知, 可以访问到方法出现的异常
            System.out.println(method.getName()+"方法调用出现了异常");}//后置通知.
        System.out.println(method.getName()+"方法调用完成!");return result;}/**
     * 3. 获取目标类代理
     */public ObjectgetProxy(){return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),this);}}

自定义的InvocationHandler实现类的三部曲(非常重要):

  1. 重写invoke方法,编写代理核心逻辑
  2. 保存目标对象(就是上面的target对象,在创建代理时会用到)
  3. 提供创建代理的方法
    使用Proxy类newProxyInstance方法实现,需要的三个参数:类加载器、目标类接口、代理逻辑处理类(自定义的InvocationHandler)

这样使用代理对象调用接口方法时,就可以转发到代理处理类的invoke方法中了。

3. 使用代理调用目标方法
@TestpublicvoiddynamicProxyTest(){
    UserService userService=newUserServiceImpl();

    LogInvocationHandler logInvocationHandler=newLogInvocationHandler(userService);
    UserService userServiceProxy=(UserService) logInvocationHandler.getProxy();

    userServiceProxy.getUserName(1L);
    System.out.println("=====================");
    userServiceProxy.say("hello");}

打印结果在这里插入图片描述
可以看到,使用代理对象userServiceProxy来调用接口方法时,请求都转发到了代理处理类的invoke方法中,在invoke方法中使用反射调用目标方法,最终转发到了target目标对象中。

相关文章:CGLIB动态代理介绍

  • 作者:Bronze5
  • 原文链接:https://blog.csdn.net/Bronze5/article/details/106968019
    更新时间:2022-07-25 08:46:39