zoukankan      html  css  js  c++  java
  • JDK动态代理和cglib代理

    写一个简单的测试用例,Pig实现了Shout接口

    public class MyInvocation implements InvocationHandler {
    
        Object k;
    
        public MyInvocation(Object k) {
            this.k = k;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //System.out.println(proxy);
    
            System.out.println("----- before -----");
            Object result = method.invoke(k, args);
            System.out.println("----- after -----");
            return result;
        }
    
        public Object getProxy() {
            return Proxy.newProxyInstance(Thread.currentThread()
                            .getContextClassLoader(), k.getClass().getInterfaces(),
                    this);
        }
    
        public static void main(String[] args) {
            Shout p = (Shout) new MyInvocation(new Pig()).getProxy(); //接口
            p.f();
        }
    }
    
    //进入Proxy
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
    
            final Class<?>[] intfs = interfaces.clone();
            ...
            
            Class<?> cl = getProxyClass0(loader, intfs);//获得代理类
    
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return newInstance(cons, ih);  //反射生成实例
        }
        
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            ...
    
            return proxyClassCache.get(loader, interfaces); //如果有实现了这些接口的代理类存在就直接返回了,否则创建一个新的
        }
        
        //proxyClassCache是一个弱引用
        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); //泛型分别代表K键,P参数,Vpublic WeakCache(BiFunction<K, P, ?> subKeyFactory,
                         BiFunction<K, P, V> valueFactory) {
            this.subKeyFactory = Objects.requireNonNull(subKeyFactory); //为空抛异常
            this.valueFactory = Objects.requireNonNull(valueFactory);
        }
    
        
        public V get(K key, P parameter) {
            ...
    
            while (true) {
                if (supplier != null) {
                    V value = supplier.get(); //获得生成的Class对象
                    if (value != null) {
                        return value;
                    }
                }
                ...
            }
        }
    

    中间是WeakCache的代码,好长,省略掉

    //进入Proxy的静态内部类ProxyClassFactory
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) { //对代理接口做检验
                    Class<?> interfaceClass = null;
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    ...
                }
    
                String proxyPkg = null;     // package to define proxy class in
    
                ....
                
                if (proxyPkg == null) {
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 
                }
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num; //形如com.sun.proxy.$Proxy0
    
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces); //生成字节码
                    
                return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length); //生成Class对象
            }
        }
    

    我们看一下生成的字节码

    byte[] c = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy0", Pig.class.getInterfaces());
    FileUtil.appendBytes(new File("/Users/ws/Desktop/ttt.class"), c);
    

    idea反编译打开看看

    package com.sun.proxy;
    
    ...
    public final class $Proxy0 extends Proxy implements Shout {
        private static Method m1;
        private static Method m3;
        private static Method m0;
        private static Method m2;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void f() throws  {
            super.h.invoke(this, m3, (Object[])null);
        }
    
        ...
        static {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("dynamic.Shout").getMethod("f", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        }
    }
    
    
    

    //cglib输出代理类字节码只需要如下设置
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/ws/Desktop/xxx");
    
    Waiter target = new NaiveWaiter();
    BeforeAdvice advice = new GreetingBeforeAdvice();
    //spring提供的代理工厂
    ProxyFactory pf = new ProxyFactory();
    //设置代理目标
    pf.setTarget(target);
    //为代理目标添加增强
    pf.addAdvice(advice);
    //生成代理实例
    Waiter proxy = (Waiter) pf.getProxy();
    proxy.greetTo("Brian");
    

    于是会在指定目录下生成代理类,打开看看

    package com.brianway.learning.spring.aop.advice;
    
    public class NaiveWaiter$$EnhancerBySpringCGLIB$$ef901900 extends NaiveWaiter implements SpringProxy, Advised, Factory {
        private boolean CGLIB$BOUND;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private MethodInterceptor CGLIB$CALLBACK_1;
        private NoOp CGLIB$CALLBACK_2;
        private Dispatcher CGLIB$CALLBACK_3;
        private Dispatcher CGLIB$CALLBACK_4;
        private MethodInterceptor CGLIB$CALLBACK_5;
        private MethodInterceptor CGLIB$CALLBACK_6;
        private static final Method CGLIB$greetTo$0$Method;
        private static final MethodProxy CGLIB$greetTo$0$Proxy;
        private static final Object[] CGLIB$emptyArgs;
        private static final Method CGLIB$serveTo$1$Method;
        private static final MethodProxy CGLIB$serveTo$1$Proxy;
        private static final Method CGLIB$equals$3$Method;
        private static final MethodProxy CGLIB$equals$3$Proxy;
        private static final Method CGLIB$toString$4$Method;
        private static final MethodProxy CGLIB$toString$4$Proxy;
        private static final Method CGLIB$hashCode$5$Method;
        private static final MethodProxy CGLIB$hashCode$5$Proxy;
        private static final Method CGLIB$clone$6$Method;
        private static final MethodProxy CGLIB$clone$6$Proxy;
    
    static {
            ...
            CGLIB$STATICHOOK1();
        }
        
        static void CGLIB$STATICHOOK1() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("com.brianway.learning.spring.aop.advice.NaiveWaiter$$EnhancerBySpringCGLIB$$ef901900");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$equals$3$Method = var10000[0];
            CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
            CGLIB$toString$4$Method = var10000[1];
            CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
            CGLIB$hashCode$5$Method = var10000[2];
            CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
            CGLIB$clone$6$Method = var10000[3];
            CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
            var10000 = ReflectUtils.findMethods(new String[]{"greetTo", "(Ljava/lang/String;)V", "serveTo", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.brianway.learning.spring.aop.advice.NaiveWaiter")).getDeclaredMethods());
            CGLIB$greetTo$0$Method = var10000[0];
            CGLIB$greetTo$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "greetTo", "CGLIB$greetTo$0");
            CGLIB$serveTo$1$Method = var10000[1];
            CGLIB$serveTo$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "serveTo", "CGLIB$serveTo$1");
        }
        
        //被代理的方法
        public final void greetTo(String var1) {
                MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
                if(this.CGLIB$CALLBACK_0 == null) {
                    CGLIB$BIND_CALLBACKS(this);
                    var10000 = this.CGLIB$CALLBACK_0;
                }
    
                if(var10000 != null) {
                    //进入拦截器
                    var10000.intercept(this, CGLIB$greetTo$0$Method, new Object[]{var1}, CGLIB$greetTo$0$Proxy);
                } else {
                    super.greetTo(var1);
                }
        }
  • 相关阅读:
    vue (v-if show 问题)
    vue 打包成 apk 文件(修改路径)
    移动端meta几个值的设置以及含义
    vue-cli 搭建
    call() 和 apply() 的作用和区别
    关于闭包的理解
    js的style和getArribute("属性名")
    vue的生命周期
    css3新特性选择器(补充)
    css3的新特性选择器-------属性选择器
  • 原文地址:https://www.cnblogs.com/wangsong/p/10274850.html
Copyright © 2011-2022 走看看