欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 基础【19】代理

程序员文章站 2022-12-14 22:26:26
Java 代理(Proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。 在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。 JavaWeb 中最常见的过滤器、Struts 中的拦截器、Spring 中的 AOP...都有代理的应用。 此篇博客将编写例子描述 Java 底 ......

   java 代理(proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。

   在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。

   javaweb 中最常见的过滤器、struts 中的拦截器、spring 中的 aop...都有代理的应用。

   此篇博客将编写例子描述 java 底层技术和开源类库cglib实现代理的方法,并对比各方法的优缺性。

Java 基础【19】代理

1.jdk 静态代理

   抽象接口:

/**
 * 用户服务抽象
 */
public interface userservice {

    /**
     * 用户登录
     *
     * @param username 用户名
     * @param pwd      密码
     * @return 登陆结果
     */
    string login(string username, string pwd);
}

   实现该接口:

/**
 * 用户服务实现
 *
 * @author rambo 2019-03-01
 **/
public class userserviceimpl implements userservice {


    @override
    public string login(string username, string pwd) {
        console.log("进行登陆逻辑.........");

        return "登陆结果";
    }
}

   编码代理类,实现该接口,代理目标作为私有对象:

/**
 * 用户服务代理类
 *
 * @author rambo 2019-03-01
 **/
public class userserviceproxy implements userservice {
    private userservice userservice;

    userserviceproxy(userservice userservice) {
        this.userservice = userservice;
    }

    @override
    public string login(string username, string pwd) {
        console.log("登陆前扩展.....");
        userservice.login(username, pwd);
        console.log("登陆后扩展.....");
        return "登陆结果";
    }
}

   编写测试类:

    @test
    public void testlogin() throws exception {
        userserviceproxy userserviceproxy = new userserviceproxy(new userserviceimpl());
        userserviceproxy.login("rambo","111111");
    }

   这应该是最原始实现代理的样子,缺点也很明显,需要编码代理类,势必导致代理类冗余,且当目标类增加或删除方法时,需要维护代理类。   

2.jdk 动态代理(接口代理)

   代理核心方法 proxy.newproxyinstance :

    /**
     * jdk 生成代理类
     * @param loader 当前目标对象使用类加载器
     * @param interfaces 目标对象实现的接口的类型
     * @param h 事件处理对象,通过反射执行目标对象的方法
     * @return 生成的代理类实例
     */
    @callersensitive
    public static object newproxyinstance(classloader loader,class<?>[] interfaces, invocationhandler h)

   代理工厂类:

/**
 * 代理工厂类
 *
 * @author rambo 2019-03-01
 **/
public class jdkproxyfactory {

    private object target;

    public jdkproxyfactory(object target) {
        this.target = target;
    }

    public object getproxyinstance() {
        return proxy.newproxyinstance(target.getclass().getclassloader(), target.getclass().getinterfaces(), new invocationhandler() {
            @override
            public object invoke(object proxy, method method, object[] args) throws throwable {
                console.log("执行目标前的扩展......");
                object returnvalue = method.invoke(target, args);
                console.log("执行目标后的扩展......");
                return returnvalue;
            }
        });
    }
}

   编写测试类:

    @test
    public void testgetproxyinstance() throws exception {
        userservice proxyinstance = (userservice) new proxyfactory(new userserviceimpl()).getproxyinstance();
        proxyinstance.login("rambo", "111111");
    }

   目标对象需要实现接口,代理对象是可以不用实现接口的。

   使用目标对象接口、自定义调用处理类 invocationhandler 实例化代理类,内部通过反射调用目标对象的方法。

3.cglib 代理 (子类代理)

   当目标对象是个单独的类,没有实现任何接口,是无法使用上述两种代理方法,这时候怎么办?

   可以使用 cglib 代理(需要单独引入 cglib 类库),自定义目标类的子类进行目标对象的扩展,且这种扩展进行在 jvm 运行期。

   cglib 底层是通过一个小而快的字节码处理框架 asm 来转换字节码并生成新的类。

   不局限目标类存在方式、运行期增强目标类、底层精致的 asm 字节码框架,使 cglib 成为许多 aop 框架生成动态代理的首选。

   代理工厂类:

/**
 * cglib 动态代理工厂
 *
 * @author rambo 2019-03-01
 **/
public class cgbproxyfactory implements methodinterceptor {

    private object target;

    public cgbproxyfactory(object target) {
        this.target = target;
    }

    public object getproxyinstance() {
        enhancer en = new enhancer();
        en.setsuperclass(target.getclass());
        en.setcallback(this);
        return en.create();

    }

    @override
    public object intercept(object obj, method method, object[] args, methodproxy proxy) throws throwable {
        console.log("执行目标前的扩展......");
        object returnvalue = method.invoke(target, args);
        console.log("执行目标后的扩展......");
        return returnvalue;
    }
}

   编写测试用例:

    @test
    public void testgetproxyinstance() throws exception {
        userservice proxyinstance = (userservice) new cgbproxyfactory(new userserviceimpl()).getproxyinstance();
        proxyinstance.login("rambo","111111");
    }

   jdk invocationhandler 、cglib methodinterceptor 具体实现的细节,如你有兴趣可翻翻源码,这里就不赘述了。

   至此代理的几种方式已经使用都已描述完毕,如有不符,还望斧正。