JDK动态代理介绍与使用

一、介绍

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

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

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

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

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

  1. 使用代理调用目标接口中的方法
四、示例

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

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

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

public interface UserService {
    String getUserName(Long userId);

    void say(String msg);
}

目标类

public class UserServiceImpl implements UserService {
    @Override
    public String getUserName(Long userId) {
        return "user" + userId;
    }

    @Override
    public void say(String msg) {
        System.out.println("say " + msg);
    }
}
2. 自定义InvocationHandler
public class LogInvocationHandler implements InvocationHandler {
    /**
     * 1. 目标类
     */
    private final Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 2. 代理逻辑
     */
    @Override
    public Object invoke(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 Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

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

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

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

3. 使用代理调用目标方法
@Test
public void dynamicProxyTest() {
    UserService userService = new UserServiceImpl();

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值