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
IService
和 RealService
的定义不变,程序的输出也没变,但代理对象 proxyService
的创建方式变了。接下来说一下 newProxyInstance
这个方法:
方法声明为:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader
表示类加载器interfaces
表示代理类(注意RealService
是被代理类)要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中就是IService
。- h的类型为
InvocationHandler
,它是一个接口,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法(反射的章节有见过它)。
注意:newProxyInstance
的返回值类型为 Object
,可以强制转换为 interfaces
数组中的某个接口类型。这里强制转换为了 IService
类型,需要注意的是,它不能强制转换为某个类类型,这也是局限之处,比如 RealService
,即使它实际代理的对象类型为 RealService
。
InvocationHandler
的构造方法接受一个参数 realObj
表示被代理的对象,invoke()
方法处理所有的接口调用,它有三个参数:
proxy
表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大。method
表示正在被调用的方法。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()
为一个类生成代理对象,这个类具体是什么还不知道,所以使用了泛型,生成的这个代理对象可以安全地转换为被代理类的类型,也是因为使用了泛型,它使用了 cglib
的 Enhancer
类。
Enhancer
类的 setSuperclass
设置被代理的类,setCallback
设置被代理类的 public非final
方法被调用时的处理类,这样代理类和被代理类就可以关联起来了。Enhancer
支持多种类型,这里使用的类实现了 MethodInterceptor
接口,它与 SDK
中的 InvocationHandler
有点类似。
与前面的 InvocationHandler
不同,SimpleInterceptor
中没有被代理的对象,它通过 MethodProxy
的 invokeSuper()
方法调用被代理类的方法:
// 正确写法
Object result = methodProxy.invokeSuper(o, args);
// 错误写法
// Object result = method.invoke(o, args);
错误写法之所以错误是因为: object
是代理对象,调用这个方法还会调用到 SimpleInterceptor
的 intercept()
方法,造成无限递归。
对比
SDK
代理面向的是一组接口,它为这些接口动态创建了一个实现类。接口的具体实现逻辑是通过自定义的 InvocationHandler
实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择。
cglib
代理面向的是一个具体的类,它动态创建了一个新类,继承了该类,重写了其方法。
从代理的角度看,SDK
代理的是对象,需要先有一个实际对象,自定义的 InvocationHandler
引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法;cglib
代理的是类,创建的对象只有一个。如果目的都是为一个类的方法增强功能,SDK
要求该类必须有接口,且只能处理接口中的方法,cglib
没有这个限制。
本文作者:为何匆匆
本文链接:https://www.cnblogs.com/nyfblog/p/16519148.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步