Java 基础5- 代理

代理

代理可以理解为要做的事不变,只是从直接做变成了间接做。这个意思不仅适用于 Java ,也适用于计算机相关的其他方面,比如网络。

静态代理

先来看个代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SimpleStaticProxyDemo {
    static interface IService {
        void hello();
    }

    static class RealService implements IService {

        @Override
        public void hello() {
            System.out.println("你好");
        }
    }

    static class TraceProxy implements IService {
        private IService realService;
        public TraceProxy(IService realService) {
            this.realService = realService;
        }

        @Override
        public void hello() {
            System.out.println("开始");
            realService.hello();
            System.out.println("结束");
        }
    }

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = new TraceProxy(realService);
        proxyService.hello();


    }
}

输出为:

开始hello
你好
结束hello

这段代码就是把 RealService 这个对象用 TraceProxy 给包裹了一层,通过 TraceProxy 执行了 hello() 方法,这种情况就可以说 TraceProxy 代理了 realService

这里说的静态是指,这段代码是写死的, TraceProxy 目前只能代理 RealService 这一种类型,如果要代理其他的类就需要重新写类似的代码。

动态代理

动态代理也是代理,它和静态代理不同之处是:动态代理在没运行的时候不知道自己要代理哪个类。

SDK 代理

以下是 SDK 动态代理的代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SimpleJDKProxyDemo {
    static interface IService {
        void hello();
    }

    static class RealService implements IService {

        @Override
        public void hello() {
            System.out.println("你好");
        }
    }

    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;
        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("开始" + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("结束" + method.getName());
            return  result;
        }
    }

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<?>[]{ IService.class }, new SimpleInvocationHandler(realService));

        proxyService.hello();
    }
}

输出:

开始hello
你好
结束hello

IServiceRealService 的定义不变,程序的输出也没变,但代理对象 proxyService 的创建方式变了。接下来说一下 newProxyInstance 这个方法:

方法声明为:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  1. loader 表示类加载器
  2. interfaces 表示代理类(注意 RealService 是被代理类)要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中就是 IService
  3. h的类型为 InvocationHandler,它是一个接口,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法(反射的章节有见过它)。

注意:newProxyInstance 的返回值类型为 Object ,可以强制转换为 interfaces 数组中的某个接口类型。这里强制转换为了 IService 类型,需要注意的是,它不能强制转换为某个类类型,这也是局限之处,比如 RealService ,即使它实际代理的对象类型为 RealService

InvocationHandler 的构造方法接受一个参数 realObj 表示被代理的对象,invoke() 方法处理所有的接口调用,它有三个参数:

  1. proxy 表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大。
  2. method表示正在被调用的方法。
  3. args 表示方法的参数。

如果把 proxy 当作 InvocationHandler.invoke() 的第一个参数传进去,就会出现无限递归,因为又会调到 SimpleInvocationHandler 中的 invoke() 方法。

到此可以看到,写了 SimpleInvocationHandler 之后,就可以用它代理不同的类,而不是只能限定一种类型。

cglib 代理

SDK 动态代理的局限在于,它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法,那就没有办法了,所以需要使用第三方类库:cglib 。以下是 cglib 的写法:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class SimpleCglibProxyDemo {
    static class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始" + method.getName());
            Object result = methodProxy.invokeSuper(o, args);
            System.out.println("结束" + method.getName());
            return  result;
        }
    }

    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new SimpleInterceptor());
        return (T) enhancer.create();
    }

    public static void main(String[] args) {
        Real proxyService = getProxy(Real.class);
        proxyService.hello("LLL");
    }
}

注意这里依赖的 jar 包的版本号可能会有冲突导致报错,自行更换最新版本即可解决。

输出:

开始hello
LLL你好
结束hello

这里的 Real 就是没有实现任何接口的。以下是分析:

getProxy() 为一个类生成代理对象,这个类具体是什么还不知道,所以使用了泛型,生成的这个代理对象可以安全地转换为被代理类的类型,也是因为使用了泛型,它使用了 cglibEnhancer 类。

Enhancer 类的 setSuperclass 设置被代理的类setCallback 设置被代理类的 public非final 方法被调用时的处理类,这样代理类和被代理类就可以关联起来了。Enhancer 支持多种类型,这里使用的类实现了 MethodInterceptor 接口,它与 SDK 中的 InvocationHandler 有点类似。

与前面的 InvocationHandler 不同,SimpleInterceptor 中没有被代理的对象,它通过 MethodProxyinvokeSuper() 方法调用被代理类的方法:

// 正确写法
Object result = methodProxy.invokeSuper(o, args);
// 错误写法
// Object result = method.invoke(o, args);

错误写法之所以错误是因为: object 是代理对象,调用这个方法还会调用到 SimpleInterceptorintercept() 方法,造成无限递归。

对比

SDK 代理面向的是一组接口,它为这些接口动态创建了一个实现类。接口的具体实现逻辑是通过自定义的 InvocationHandler 实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择。

cglib 代理面向的是一个具体的类,它动态创建了一个新类,继承了该类,重写了其方法。

从代理的角度看,SDK 代理的是对象,需要先有一个实际对象,自定义的 InvocationHandler 引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法;cglib 代理的是类,创建的对象只有一个。如果目的都是为一个类的方法增强功能,SDK 要求该类必须有接口,且只能处理接口中的方法,cglib 没有这个限制。

本文作者:为何匆匆

本文链接:https://www.cnblogs.com/nyfblog/p/16519148.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   为何匆匆  阅读(47)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起