关于java:代理模式之动静态代理

8次阅读

共计 10769 个字符,预计需要花费 27 分钟才能阅读完成。

日常求赞,感激老板。

欢送关注公众号:其实是白羊。干货继续更新中 ……

一、什么是代理模式

为某一对象提供一个代理对象,代理对象可管制被代理对象实现一系列操作,并向外界暴露出代理对象,从而管制被代理对象

简略了解就是中介,你想去买房子,间接找中介,中介除了能实现帮你买房子的操作还能帮你选房剖析、买房流程等其余服务。

二、为什么要用代理模式

代理模式的长处:

  • 两头隔离:代理模式能将被代理对象和客户对象分隔开,在客户对象不能或不香间接调用被代理对象时通过调用代理对象来实现
  • 耦合度低、扩展性好:不间接依赖被代理对象,使得零碎耦合性升高,进步扩展性
  • 合乎开闭准则、加强被代理对象性能:通过代理类可轻松实现对被代理类性能的扩大,而不用批改原被代理类代码,合乎开闭准则,对外加强被代理对象性能

代理模式的毛病:

  • 代理模式会使零碎中设计类的数量减少
  • 代理类会升高申请解决的速度
  • 减少零碎的复杂度

三、怎么应用代理模式

1. 动态代理

接口:

public interface BuyHouse {void buyHouse();
}

被代理类:

public class BuyHouseImpl implements BuyHouse {
    @Override
    public void buyHouse() {System.out.println("我买房子了");
    }
}

代理类:

public class BuyHouseProxy implements BuyHouse {

    private BuyHouseImpl buyHouseImpl;

    public BuyHouseProxy(BuyHouseImpl buyHouseImpl){this.buyHouseImpl = buyHouseImpl;}

    @Override
    public void buyHouse() {System.out.println("选房剖析 ing");
        buyHouseImpl.buyHouse();
        System.out.println("买房流程 ing");
    }
}

客户端调用:

BuyHouseProxy proxy = new BuyHouseProxy(new BuyHouseImpl());
proxy.buyHouse();
/* 执行后果:选房剖析 ing
我买房子了
买房流程 ing
*/

动态代理须要咱们本人编写代理类:

  • 代理类持有被代理类对象
  • 代理类和被代理类实现雷同接口
  • 代理类在重写办法中实现持有的被代理类对象重写办法的调用和扩大性能编写

毛病:咱们须要手动为每个被代理类编写代理类,工作量大,扩展性也不好

解决方案 – 动静代理

2. 动静代理

这里介绍两种动静代理技术:JDK 动静代理和 CGLIB 动静代理

1)JDK 动静代理

应用举例:

要求:被代理类要实现一个接口

咱们应用下面动态代理中买房子例子中的被代理类:BuyHouseImpl和接口:BuyHouse

上面编写代理解决类:

// 实现 InvocationHandler 接口并重写 invoke 办法
public class ProxyHandler implements InvocationHandler {

    /**
     * 被代理对象
     */
    private Object target;
    // 有参结构,传入被代理对象
    public ProxyHandler(Object target) {this.target = target;}

    /**
     * 代理类解决逻辑
     * 获取到的代理类执行办法最终会执行此办法中的逻辑
     * @param proxy 代理对象
     * @param method 被代理办法
     * @param args 被代理办法的参数数组
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是前置减少性能");
        Object result = method.invoke(target, args);
        System.out.println("我是后置减少性能");
        return result;
    }

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

测试执行:

public static void main(String[] args) {ProxyHandler proxyHandler = new ProxyHandler(new BuyHouseImpl());
    BuyHouse buyHouse = (BuyHouse) proxyHandler.getProxy();
    buyHouse.buyHouse();}
/*
执行后果:我是前置减少性能
我买房子了
我是后置减少性能
/*
源码剖析:

咱们以 ProxynewProxyInstance办法为入口

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

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

追踪一下 getProxyClass0 办法:

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);
}
// 持续查看 proxyClassCache 的 get 办法
public V get(K key, P parameter) {Objects.requireNonNull(parameter);

    expungeStaleEntries();

    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {valuesMap = oldValuesMap;}
    }

    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    while (true) {if (supplier != null) {
            // 联合上下文能够看出 supplier 是个 Factory,调用 get 办法过来代理类类对象
            V value = supplier.get();
            if (value != null) {return value;}
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

再往下追溯能够看到是通过 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 来生成字节码文件

源码总结:
  1. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创立动静代理类;
  2. 通过反射机制最终取得动静代理类的结构器,其惟一参数类型是调用处理器接口类型;
  3. 通过构造函数创立动静代理类实例,结构时调用处理器对象作为参数被传入。
  4. 通过使代理类继承 Proxy 获取应用 InvocationHandler,实现和被代理类雷同接口来调用雷同办法
问题:

1:那么为什么调用代理类的指标办法最终会调用的 ProxyHandler implements InvocationHandlerinvoke办法呢?

咱们能够在生成代理对象之前加上 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 保留代理对象字节码文件:

// 保留生成的代理类的字节码文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
ProxyHandler proxyHandler = new ProxyHandler(new BuyHouseImpl());
BuyHouse buyHouse = (BuyHouse) proxyHandler.getProxy();
buyHouse.buyHouse();

执行实现能够看到:

public final class $Proxy0 extends Proxy implements BuyHouse {public final void buyHouse() throws  {
        try {
            // 调用 ProxyHandler 的 invoke 办法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);
        }
    }
}

因为 JDK 最终生成的代理类继承了 Proxy 类并实现了咱们定义的接口 BuyHouse,在重写BuyHouse 接口中的 buyHouse 办法中通过反射调用了 ProxyHandler implements InvocationHandlerinvoke办法

2:为什么生成的代理类都要继承 Proxy?

咱们看生成的代理类可知,代理类中只在应用了父类 Proxy 中的 InvocationHandler(但其实咱们能够以在初始化时间接放入代理对象),然而 基于继承的形式能够缩小生成代理类时的性能耗费 ,其次还能够用于 标识此对象是个代理对象

2)CGLIB 动静代理

应用举例:

应用 CGLIB 动静代理须要额定引入 cglib 依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

还是应用下面的例子(留神这次被代理类能够不必实现接口)

编写办法拦截器:

public class MyMethodInterceptor implements MethodInterceptor {

    /**
     * 拦挡办法
     * @param o cglib 生成的代理对象
     * @param method 被代理对象办法
     * @param objects 办法参数
     * @param methodProxy 代理办法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("我是前置减少性能");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("我是后置减少性能");
        return result;
    }
}

客户端调用代码:

public static void main(String[] args) {Enhancer enhancer = new Enhancer();
    // 设置代理类父类即被代理对象的 class
    enhancer.setSuperclass(BuyHouseImpl.class);
    // 设置办法拦截器实例
    enhancer.setCallback(new MyMethodInterceptor());
    // 获取代理对象
    BuyHouseImpl buyHouse = (BuyHouseImpl) enhancer.create();
    // 调用代理对象代理办法会被办法拦截器拦挡
    buyHouse.buyHouse();}
/*
执行后果:我是前置减少性能
我买房子了
我是后置减少性能
*/
源码剖析:

咱们从 enhancer.create() 作为入口

public Object create() {
    classOnly = false;
    argumentTypes = null;
    return createHelper();}
// 接着看 createHelper()
private Object createHelper() {
    // 执行前校验
    preValidate();
    // 创立 EnhancerKey 对象
    Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                                         ReflectUtils.getNames(interfaces),
                                         filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                                         callbackTypes,
                                         useFactory,
                                         interceptDuringConstruction,
                                         serialVersionUID);
    this.currentKey = key;
    // 调用 Enhancer 父类的 create 办法
    Object result = super.create(key);
    return result;
}

AbstractClassGeneratorcreate 办法

protected Object create(Object key) {
    try {ClassLoader loader = getClassLoader();
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        if (data == null) {synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                if (data == null) {Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                    data = new ClassLoaderData(loader);
                    newCache.put(loader, data);
                    CACHE = newCache;
                }
            }
        }
        this.key = key;
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {return firstInstance((Class) obj);
        }
        // 最终执行这个
        return nextInstance(obj);
    } catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);
    }
}

接着看 EnhancernextInstance办法:

protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;

    if (classOnly) {return data.generatedClass;}

    Class[] argumentTypes = this.argumentTypes;
    Object[] arguments = this.arguments;
    if (argumentTypes == null) {
        argumentTypes = Constants.EMPTY_CLASS_ARRAY;
        arguments = null;
    }
    return data.newInstance(argumentTypes, arguments, callbacks);
}
// 最终调用了 newInstance 办法
// 第一个参数为代理对象的结构器 class,第二个为代理对象构造方法参数,第三个为对应回调对象(办法拦截器)// 依据这些参数反射生成代理对象(通过代理对象结构器 calss 获取结构器并实例化代理对象)public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);
    try {
        // Explicit reference equality is added here just in case Arrays.equals does not have one
        if (primaryConstructorArgTypes == argumentTypes ||
            Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
            // If we have relevant Constructor instance at hand, just call it
            // This skips "get constructors" machinery
            return ReflectUtils.newInstance(primaryConstructor, arguments);
        }
        // Take a slow path if observing unexpected argument types
        return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
    } finally {
        // clear thread callbacks to allow them to be gc'd
        setThreadCallbacks(null);
    }

}
源码总结:
  1. 通过 Enhancer 设置被代理类 class 和自定义办法拦截器,通过反射机制生成代理类 class
  2. 代理类通过继承被代理类来重写被代理办法
问题:

1:那么为什么调用代理类的指标办法最终会调用的 MyMethodInterceptor implements MethodInterceptorintercept办法呢?

咱们能够在客户端调用时应用 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\"); 来保留生成的代理对象:

BuyHouseImpl$$EnhancerByCGLIB$$bdedfc7f.class:生成的代理对象 class

通过生成的代理对象源码能够看到:

public final void buyHouse() {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {var10000.intercept(this, CGLIB$buyHouse$0$Method, CGLIB$emptyArgs, CGLIB$buyHouse$0$Proxy);
    } else {super.buyHouse();
    }
}

代理对象继承被代理对象并重写 buyHouse 办法,在重写办法中调用 MethodInterceptor 的 intercept 办法实现了办法拦挡

3)比照总结

JDK 动静代理是通过反射机制生成一个和被代理类实现雷同接口且继承 Proxy 类的代理类,并实现接口的办法,放弃和被代理类雷同的接口,并在调用办法时调用父类持有的 Invocationhandler 来解决,只能对实现了接口的类生成代理

CGLIB 动静代理则是通过应用 ASM 开源包,加载被代理对象的 class 文件,并批改其字节码文件生成一个继承被代理对象的子类来作为代理对象,并重写代理办法使其调用自定义的办法拦截器去执行,因为基于继承所以被代理类和办法不能被 final 关键字润饰,保障能够被继承和重写

性能

  • 在 JDK1.6 之前 CGLIB 基于 ASM 字节码生成框架效率高于 JDK 动静代理的 java 反射
  • JDK1.6 之后一直对 JDK 动静代理优化调用次数比拟少时效率高于 CGLIB 动静代理
  • JDK1.8 之后 JDK 动静代理效率高于 CGLIB 动静代理

Spring 的抉择

  • 当被代理类实现了接口时,应用 JDK 动静代理
  • 当被代理类没实现接口时,应用 CGLIB 动静代理
  • 也设置强制应用 CGLIB,在 spring 配置文件中配置 <aop:aspectj-autoproxy proxy-target-class="true" /> 即可

四、最初

点个赞啊亲

如果你认为本文对你有帮忙,能够「在看 / 转发 / 赞 /star」,多谢

如果你还发现了更好或不同的想法,还能够在留言区一起探讨下


欢送关注公众号:「其实是白羊」干货继续更新中 ……

正文完
 0