• CGlib动态代理


    原文地址:https://my.oschina.net/huangyong/blog/159788
    作者:黄勇
    黄勇,从事近十年的 JavaEE 应用开发工作,曾任阿里巴巴公司系统架构师,现任特赞CTO。对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式。国内开源软件推动者之一,Smart Framework 开源框架创始人。热爱技术交流,乐于分享自己的工作经验。目前著有《架构探险——从零开始写Java Web框架》和《架构探险-轻量级微服务架构.上册》两本书,下半年即将出版《架构探险-轻量级微服务架构.下册》。

    一. 点睛

    在前面的文章:jdk的动态代理,介绍了JDK动态代理,用了这个DynamicProxy以后,觉得它还是非常好的,好的地方是,接口变了,这个动态代理类不用做改动。而静态代理就不一样了,接口变了,实现类还需要动,代理类也需要动。但是JDK动态代理也并不是”万灵丹”,也有局限性,它也有搞不定的时候,比如要代理一个没有任何接口的类,它就没有用武之地了。

    那么,能否代理没有接口的类呢?答案是肯定的,那就是CGlib这个类库。虽然它看起来不太起眼,但是SpringHibernate这样高端的开源框架都用到了它。它是一个在运行期间动态生成字节码的工具,也就是动态生成代理类了。说起来很高深,实际用起来一点也不难,下面再写一个CGlibProxy

    二. 示例

    我使用的maven工程进行构建,先引入依赖:

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

    编写CGlibProxy 

    package org.light4j.proxy.dynamicProxy;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CGLibProxy implements MethodInterceptor {
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Class<T> cls){
            return (T) Enhancer.create(cls, this);
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] arg,
                MethodProxy proxy) throws Throwable {
            before();
            Object result = proxy.invokeSuper(obj, arg);
            after();
            return result;
        }
    
        private void before() {
            System.out.println("Before...");
        }
    
        private void after() {
            System.out.println("After...");
        }
    }

    需要实现CGLib给我们提供的MethodInterceptor接口,并填充intercept方法。方法中最后一个MethodProxy类型的参数proxy值得注意,CGLib给我们提供的是方法级别的代理,也可以理解为对方法的拦截(这不就是传说中的”方法拦截器”吗?)。这个功能对于我们来说,如同雪中送炭。我们直接调用proxyinvokeSuper方法,将被代理的对象obj以及方法参数args传入其中即可。

    DynamicProxy类似,在CGLibProxy中也添加一个泛型的getProxy方法,便于我们可以快速地获取自动生成的代理对象。下面用一个main方法来描述:

    package org.light4j.proxy;
    
    import org.light4j.proxy.dynamicProxy.CGLibProxy;
    import org.light4j.proxy.impl.HelloImpl;
    
    public class Main {
        public static void main(String[] args) {
            CGLibProxy cgLibProxy = new CGLibProxy();
            Hello helloProxy = cgLibProxy.getProxy(HelloImpl.class);
    
            helloProxy.say("longjiazuo");
        }
    }

    仍然通过两行代码就可以返回代理对象,与JDK动态代理不同的是,这里不需要任何的接口信息,对谁都可以生成动态代理对象(不管它是”矮穷挫”,还是”高富帅”)。

    由于我一向都追求完美,用两行代码返回代理对象还是有些多余的,不想总是去new这个CGLibProxy对象,最好new一次,以后随时拿随时用,于是想到了”单例模式”:

    package org.light4j.proxy.dynamicProxy;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CGLibProxy implements MethodInterceptor {
    
        private static CGLibProxy instance = new CGLibProxy();
    
        private CGLibProxy(){
        }
    
        public static CGLibProxy getInstance(){
            return instance;
        }
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Class<T> cls){
            return (T) Enhancer.create(cls, this);
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] arg,
                MethodProxy proxy) throws Throwable {
            before();
            Object result = proxy.invokeSuper(obj, arg);
            after();
            return result;
        }
    
        private void before() {
            System.out.println("Before...");
        }
    
        private void after() {
            System.out.println("After...");
        }
    }

    加上了以上几行代码就解决问题了,需要说明的是,这里有一个private的构造方法,就是为了限制外界不能再去new它了,换句话说,这个类被”阉割”了。

    下面用一个main方法来证明我的简单主义思想:

    package org.light4j.proxy;
    
    import org.light4j.proxy.dynamicProxy.CGLibProxy;
    import org.light4j.proxy.impl.HelloImpl;
    
    public class Main {
        public static void main(String[] args) {
            Hello helloProxy = CGLibProxy.getInstance().getProxy(HelloImpl.class);
    
            helloProxy.say("longjiazuo");
        }
    }

    没错,只需一行代码就可以获取代理对象了!
    到此,通过三篇文章的分析,先后介绍了无代理,静态代理,JDK动态代理,CGLib动态代理,其实代理的世界远不止这么小,还有很多实际的应用场景。

    摘自 http://blog.longjiazuo.com/archives/1851

  • 相关阅读:
    关于程序员的段子,你能读懂几个?网友:你若全对算我输!
    【C++学习笔记】超详细C++注释的使用方法,不赶紧收藏就错过啦!
    做编程容易短命?衷心建议:三十六计,“保命”要紧
    【C++学习笔记】C++异常处理!你绝对不能错过的干货!
    Linux Socket套接字出现问题怎么办?教你5个方法“有备无患”
    【致敬伟大的程序员】写代码写进国博,这么牛的还有谁?
    重要的事情说三遍:局部变量一定要初始化!你做到了吗?
    自我介绍
    .NET应用程序中嵌入VB6表单
    在VB6中使用。net DLL
  • 原文地址:https://www.cnblogs.com/love-cj/p/8350296.html
Copyright © 2020-2023  润新知