为了账号安全,请及时绑定邮箱和手机立即绑定

代理模式之动静态代理

日常求赞,感谢老板。

一、什么是代理模式

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

简单理解就是中介,你想去买房子,直接找中介,中介除了能完成帮你买房子的操作还能帮你选房分析、买房流程等其他服务。

二、为什么要用代理模式

代理模式的优点:

  • 中间隔离:代理模式能将被代理对象和客户对象分隔开,在客户对象不能或不香直接调用被代理对象时通过调用代理对象来实现
  • 耦合度低、扩展性好:不直接依赖被代理对象,使得系统耦合性降低,提高扩展性
  • 符合开闭原则、增强被代理对象功能:通过代理类可轻松完成对被代理类功能的扩展,而不必修改原被代理类代码,符合开闭原则,对外增强被代理对象功能

代理模式的缺点:

  • 代理模式会使系统中设计类的数量增加
  • 代理类会降低请求处理的速度
  • 增加系统的复杂度

三、怎么使用代理模式

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

执行完成可以看到:image-20201222154859463

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:\\");来保存生成的代理对象:

image-20201224165920837

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

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


点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消