Java动态代理

动态代理

Java的动态代理有两种:

  • JDK的动态代理
  • CGLIB动态代理

之所以学动态代理,是为了更好理解Spring AOP的原理。动态代理是代理模式的一种,代理模式的定义和优点我就不说了。

先学一学JDK的动态代理

基于JDK的动态代理

我们用代码来说话

//基础接口
public interface BaseBehavior {

    void eat(String... foods) ;

    void run(String run) ;
}
//被代理的类
//person类可以走可以吃
public class Person implements BaseBehavior {

    @Override
    public void eat(String... foods) {
        System.out.print("开始吃饭:");
        for (String s : foods){
            System.out.print(s+" ");
        }
        System.out.println();
    }

    @Override
    public void run(String where) {
        System.out.println("去" + where+",唱跳rap");
    }
}


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//代理类
public class DynamicProxy implements InvocationHandler {

    private Object object ;

    public DynamicProxy(BaseBehavior baseBehavior) {
        this.object = baseBehavior ;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object value = null ;

        String methodName = method.getName() ;

        if ("eat".equals(methodName)){
            //如果是吃饭
            //1.做饭
            //2.吃饭
            //3.洗碗
            cook(args) ;
            value = method.invoke(this.object , args) ;
            wash();
        }else if ("run".equals(methodName)){
            //如果是出行
            //1.穿鞋
            //2.出行
            //3.回家
            putOnShoes() ;
            value = method.invoke(this.object , args) ;
            goHome() ;
        }
        return value ;
    }

    private void goHome() {
        System.out.println("回家喽");
    }

    private void putOnShoes() {
        System.out.println("出门了,开始穿鞋");
    }

    private void wash() {
        System.out.println("吃完饭了,开始刷碗");
    }

    private void cook(Object[] args) {
        System.out.print("开始做饭:");
        String[] ss = (String[]) args[0] ;
        for (String s : ss){
            System.out.print(s+" ");
        }
        System.out.println();
    }

}


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//代理工厂
public class ProxyFactory {

    //获得代理类的实例
    public static BaseBehavior builder(Class classFile) throws IllegalAccessException, InstantiationException {
        BaseBehavior obj = (BaseBehavior) classFile.newInstance() ;

        InvocationHandler adviser = new DynamicProxy(obj) ;

        BaseBehavior proxy = (BaseBehavior) Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces() ,
                adviser
        ) ;

        return proxy ;
    }
}
public class Test {

    //测试类
    public static void main(String[] args) {
        BaseBehavior behavior = null;
        try {
            behavior = ProxyFactory.builder(Person.class) ;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        behavior.eat("宫保鸡丁","水煮鱼","水煮肉片");
        behavior.run("北京");

    }
}

输出结果如下:

开始做饭:宫保鸡丁 水煮鱼 水煮肉片
开始吃饭:宫保鸡丁 水煮鱼 水煮肉片
吃完饭了,开始刷碗
出门了,开始穿鞋
去北京,唱跳rap
回家喽

JDK的动态代理是基于反射机制的,使用到的是

  • InvocationHandler接口:

    动态代理类继承此接口,重写 public Object invoke(Object proxy, Method method, Object[] args)

    1. proxy是代理的真实对象
    2. method是代理的方法
    3. args是代理方法接受的参数
  • Proxy.newProxyInstance方法:

    创建动态代理类的静态方法,public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    1. loader为被代理类的类加载器
    2. interfaces为需要代理的一组接口,代理后就可以调用接口方法
    3. h为一个InvocationHandler,也就是与被代理类关联的Handler

newProxyInstance方法

在这里插入图片描述

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

JDK API 1.6 中Proxy介绍

动态代理类*(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

代理类具用以下属性:

  • 代理类是公共的、最终的,而不是抽象的。
  • 未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
  • 代理类扩展 java.lang.reflect.Proxy
  • 代理类会按同一顺序准确地实现其创建时指定的接口。
  • 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
  • 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。
  • 如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。
  • 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission
  • 每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

在多代理接口中重复的方法

当代理类的两个或多个接口包含一个具有相同名称和参数签名的方法时,代理类的接口顺序变得非常重要。在代理实例上调用重复方法 时,传递到调用处理程序的 Method 对象没有必要成为其声明类可以从接口(通过该接口调用代理方法)的引用类型指派的对象。此限制存在的原因是,生成的代理类中的相应方法实现无法确定它通过哪一个接口调用。因此,在代理实例上调用重复方法时,第一个接口中的方法的 Method 对象包含接口的代理类列表中的方法(直接或通过超级接口继承),该对象会传递到调用处理程序的 invoke 方法,无论该方法调用通过哪一种引用类型发生。

如果代理接口包含某一方法,它的名称和参数签名与 java.lang.ObjecthashCodeequalstoString 方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method 对象将使 java.lang.Object 成为其声明类。换句话说,java.lang.Object 公共的非最终方法理论上在所有代理接口之前,以便确定哪一个 Method 对象传递到调用处理程序。

还要注意,当重复方法被指派到调用处理程序时,invoke 方法只可以抛出经过检查的异常类型,该异常类型可以使用所有 代理接口(可以通过它调用)中方法的 throws 子句指派一种异常类型。如果 invoke 方法抛出一个经过检查的异常,该异常没有指派给任何由一个代理接口(可以通过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的 UndeclaredThrowableException。此限制表示并非所有的由传递到 invoke 方法的 Method 对象上调用 getExceptionTypes 返回的异常类型都可以由 invoke 方法成功抛出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值