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

日常求赞,感激老板。

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

一、什么是代理模式

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

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

二、为什么要用代理模式

代理模式的长处:

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

代理模式的毛病:

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

三、怎么应用代理模式

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」,多谢

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


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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理