Java 动态代理之jdk 动态代理

Java 动态代理

什么是动态代理:

​ 动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情。

java 中主要有两种方式实现动态代理 : *一是JDK动态代理,二是Cglib动态代理*

1.jdk动态代理

​ jdk动态代理主要用的JDK自带的java.lang.reflect包下面的两个类,一个是接口:InvocationHandler,另一个类Proxy。

要求:使用jdk动态代理的方式去要求被代理类必须实现一或者多个接口

实现步骤 :

  • 创建两个接口Log,UserService接口

Log 接口用于记录方法调用日志

package com.example.demo.JDKproxy;

/**
 * @author gino
 * 2021-04-30
 */
public interface Log {
  public   boolean savelog();

}

UserService 提供CRUD方法

package com.example.demo.JDKproxy;

/**
 * @author gino
 * 2021-04-29
 */
public interface UserService {
    public void delete( long id );
    public void update();
}

  • 创建实现类UserServiceImpl
package com.example.demo.JDKproxy;

/**
 * @author gino
 * 2021-04-29
 */
public class UserServiceImpl implements UserService,Log{
    @Override
    public void delete(long id) {
        System.out.println("delete user"+id);
    }

    @Override
    public void update() {
        System.out.println("update user");
    }

    @Override
    public boolean savelog() {
        System.out.println("save Log");
        return false;
    }
}
  • 创建方法拦截器 实现InvocationHandler 接口,实现invoke方法
package com.example.demo.JDKproxy;

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

/**
 * @author gino
 * 2021-04-30
 */
public class ProxyHandler implements InvocationHandler {

    /**
     * 被代理的对象
     */
    private  Object taget;

    /**
     * 通过构造方法将被代理代理对象设置进来
     * @param taget
     */
    public ProxyHandler(Object taget) {
        this.taget = taget;
    }
		
   /***
     *
     * @param proxy  代理对象
     * @param method 方法对象
     * @param args  方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用前增强
        System.out.println("method invoke before  doing "+method.getName());
        //调用被代理类的方法
        Object returnValue=method.invoke(taget,args);
        //调用后增强
        System.out.println("method invoke after  doing "+method.getName());
        return returnValue;
    }
}
  • 测试代码以及结果
package com.example.demo.JDKproxy;

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

/**
 * @author gino
 * 2021-04-29
 */
public class main {
    public static void main(String[] args) {


       //jdk动态代理类的必须实现一个或者多个接口

        // 1. 创建被代理的对象,UserService接口的实现类
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        //创建代理对象,并告知代理对象代理的是谁
        InvocationHandler handler=new ProxyHandler(userServiceImpl);
        //获取被代理对象的类加载器和类实现的接口
        ClassLoader loader = userServiceImpl.getClass().getClassLoader();
        Class[] interfaces = userServiceImpl.getClass().getInterfaces();
        //通过Proxy 对象获取代理对象
        UserService porxy = (UserService) Proxy.newProxyInstance(loader, interfaces, handler);
        porxy.delete(99);
        Log porxy2 = (Log) Proxy.newProxyInstance(loader, interfaces, handler);
        porxy2.savelog();
    }
}

运行结果如下

在这里插入图片描述

源码分析:

​ 为什么jdk动态代理的被代理类必须实现一个或者多个接口?

​ 从测试用例中可以看出, jdk动态代理的是通过 proxy.newProxyInstance()方法返回被代理的对象, 然后再去操作放回的对象就可以实现方法的前置和后置增强

​ 而proxy.newProxyInstance()方法中传入了三个参数 第一个是被代理类的类加载器, 第二个是被代理类的实现的所有接口,第三个是方法的拦截器接口实现类 进入源码可以看到如下代码。

@CallerSensitive
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);
    }

    /*
     * 查找或者生成代理类 如果代理以前生成过 则直接会从缓存中拿 
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * 使用指定的调用处理程序调用其构造函数。
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
      
        //设置代理对象构造方法的参数 ,参数来自InvocationHandler接口的构造方法    与上面ProxyHandler 类中要通过构造方法将被代理的类设置进去相呼应
        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);
    }
}

其中就有对实现接口的判断Class<?> cl = getProxyClass0(loader, intfs); 进入getProxyClass0,会发现原来实现接口还有最大值65535,而且每一次调用方法都会先从缓存里面拿代理对象

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

再点进去就会发现 Objects.requireNonNull(parameter);里面有非空判断

在这里插入图片描述

在这里插入图片描述
至此代码层次的设计便要求被代理类要实现一个或者多个接口, 为什么要加这段代码呢

我们通过反编译被生成的**$Proxy0.class**文件发现 生成的代理类本身就继承了Proxy 类实现了被代理类实现的接口,因为java是单继承的 ,如果被代理是继承父类而不是实现接口 那便违反了java的单继承规则

public final class $Proxy0 extends Proxy implements Interface {

   public $Proxy0(InvocationHandler paramInvocationHandler) {\

 super(paramInvocationHandler);

    }

的代理类本身就继承了Proxy 类实现了被代理类实现的接口,因为java是单继承的 ,如果被代理是继承父类而不是实现接口 那便违反了java的单继承规则`

public final class $Proxy0 extends Proxy implements Interface {

   public $Proxy0(InvocationHandler paramInvocationHandler) {\

 super(paramInvocationHandler);

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值