JDK动态代理

动态代理有两种方式,一种是JDK动态代理;另一种是CGLib动态代理。本篇说一下JDK动态代理。

一、什么是动态代理

动态代理就是在代码运行过程中生成的代理对象。和它对应的还有静态代理动态代理生成的代理对象好比是经销商,被代理对象是生产商。生产商只负责生产产品,把销售的权限放给经销商。这样经销商会在生产商生产的基础上再加入销售的环节。对比到动态代理上来,动态代理就是对原有对象方法的一个补充和扩展,它不会破坏原有的方法。(有点AOP的味道)。而且代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理类和被代理类都会实现同一个接口。在外界看来这两个类没有啥区别,其实差别还是很大的。

二、代码demo

需要代理的接口:


public interface aa
{    
    public String eat(String food);
    public String drink();
}

实现类

public class RealSubject implements aa
{ 
    public String eat(String food)
    {
        return "eat" + food;
    }
 
    
    public String drink()
    {
        return " drink ";
    }

调用处理器实现类(要实现invocationHandler接口)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class InvocationHandlerImpl implements InvocationHandler
{
 
    /**
     * 这个就是我们要代理的真实对象
     */
    private Object subject;
 
    /**
     * 构造方法,给我们要代理的真实对象赋初值
     *
     * @param subject
     */
    public InvocationHandlerImpl(Object subject)
    {
        this.subject = subject;
    }
 
    /**
     * 该方法负责集中处理动态代理类上的所有方法调用。
     * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     *
     * @param proxy  代理类实例
     * @param method 被调用的方法对象
     * @param args   调用参数
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("在调用之前,我要干点啥呢?");
        System.out.println("Method:" + method);
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object returnValue = method.invoke(subject, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("在调用之后,我要干点啥呢?");
        return returnValue;
    }
}

最终实现:


public class DynamicProxyDemonstration
{
    public static void main(String[] args)
    {
        //代理的真实对象
        Subject realSubject = new RealSubject();
        
        /**
         * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
         * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
         * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
 
        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
        String food= subject.eat("apple");
        System.out.println(hello);
    }
 
}

三、部分源码分析

3.1

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

这一行代码是生成动态代理对象的。那它是如何产生的呢?有如下几个步骤:
1、将被代理对象的接口先复制一份

        final Class<?>[] intfs = interfaces.clone()

2、获得与指定类装载器和一组接口相关的代理类类型对象

 Class<?> cl = getProxyClass0(loader, intfs)

3、通过反射获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h)

final Constructor<?> cons = cl.getConstructor(constructorParams)

4、生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法

return cons.newInstance(new Object[]{h})

3.2
执行
subject.eat("apple")这行代码时,会自动调用InvocationHandlerImpl的invoke方法.
其实最后编译完以后,是下面这个样子:

public final class ProxySubject
  extends Proxy
  implements aa
{
  private static Method m1;
  private static Method m3;
  private static Method m4;
  private static Method m2;
  private static Method m0;
  
  public ProxySubject(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String eat(String paramString)
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

可以看到,这就是最终真正的代理类,它继承自Proxy并实现了我们定义的aa接口.通过Proxy.newInstance得到的是这个实现类。当它再调用被代理类对象里面的方法时,会跳转调用实现invocationHandler的invoke()方法。这个invoke()方法是对被代理对象当前方法的增强。

四、动态代理的步骤

1、使用被代理类的接口信息,去编写代理对象的代码
2、使用JDK自带的API完成javac编译
3、使用被代理对象的类加载器完成代理类的class文件的加载
4、JVM根据代理类的class信息去创建代理对象(用到了反射)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值