动态代理源码解析之JDK

来源:互联网 发布:怎么能开淘宝店铺 编辑:程序博客网 时间:2024/05/17 09:16

程序员基本都知道有个模式叫代理模式,其实质就是调用由代理类发起,再由代理类来进行调用真实的业务类来调用,实现业务控制,在代理类的处理中,可以在真实的业务类调用前面和后面进行逻辑处理;

而动态代理跟普通的代理模式在代码编写上是有区别的,动态代理的代理类class是动态生成的;

动态代理的应用场景很广,最被人熟知的当属spring的AOP,AOP的应用场景非常广泛,比如权限控制,缓存等等

动态代理的实现有:jdk动态代理和cglib动态代理,本篇博文尝试从源码角度来看看jdk动态代理是怎么实现的

先来看看jdk动态代理怎么用,上代码

  • UserService

    public interface UserService {    void add();}
  • UserServiceImpl

    public class UserServiceImpl implements UserService {    @Override    public void add() {        System.out.println("add");    }}
  • MyInvocationHandler

    public class MyInvocationHandler implements InvocationHandler {    private Object target;    public MyInvocationHandler(Object target) {        super();        this.target = target;    }    public Object getProxy() {        return Proxy.newProxyInstance(Thread.currentThread()                        .getContextClassLoader(), target.getClass().getInterfaces(),                this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("----- before -----");        Object result = method.invoke(target, args);        System.out.println("----- after -----");        return result;    }}
  • Test

    public class Test {    public static void main(String[] args) {        UserService userService = new UserServiceImpl();        MyInvocationHandler invocationHandler = new MyInvocationHandler(                userService);        UserService proxy = (UserService) invocationHandler.getProxy();        proxy.add();    }}
  • main方法执行结果

    ----- before -----add----- after -----
  • jdk动态代理代理类需要实现java.lang.reflect.InvocationHandler接口,从包名可以看出,jdk动态代理应该是用反射来实现的

看看代码

  • 在main方法中new了一个实现类来赋值给接口,而不是直接定义实现类,最重要的原因是jdk动态代理只支持接口的动态代理,被代理的类都必须要有接口
  • 动态代理的入口是getProxy()方法,getProxy()方法实现为:newProxyInstance,接受参数:(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);这里的ClassLoader为当前线程上下文的ClassLoader,interfaces为被代理类的所有接口数组
  • 看到这里,想必jdk动态代理的核心逻辑就在这个newProxyInstance方法里;这里贴下源码

    @CallerSensitivepublic 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的核心逻辑        Class<?> cl = getProxyClass0(loader, intfs);        // 生成完以后,那就是根据生成的class来产生代理实例        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源码也用很多简写,个人不大推崇这种写法,比如:intfs
    • 再看getProxyClass0方法,用到的WeakCache后面专门写文章讲解,这里知道是用来做缓存的就行
      //采用WeakCache缓存代理classprivate static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static Class<?> getProxyClass0(ClassLoader loader,                                       Class<?>... interfaces) {    if (interfaces.length > 65535) {        throw new IllegalArgumentException("interface limit exceeded");    }    //get方法先去缓存查找,如果没找到那么用ProxyClassFactory来生成    //再进入get方法看,第一行代码就是Objects.requireNonNull(interfaces);    //从这里看出jdk动态代理中被代理的类必须实现接口    return proxyClassCache.get(loader, interfaces);}

未完(时间关系后续补)