Proxy Pattern
现实生活中,租房时的中介就相当于代理对象,房东相当于实际对象,我们相当于客户端,通过中介把房东的房子租下来;
代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象;
代理存在的价值:
- 节省成本比较高的实际对象的创建开销,按需延迟加载;
- 执行权限检查,代理检查权限后,再调用实际对象;
- 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上;
一、静态代理
/**
* 静态代理示例
*/
public class SimpleStaticProxy {
/**
* 代理和实际对象共同的接口
*/
static interface IService {
void sayHello();
}
/**
* 实际对象类
*/
static class RealService implements IService {
@Override
public void sayHello() {
System.out.println("Hello");
}
}
/**
* 代理对象类
*/
static class ProxyService implements IService {
/**
* IService的成员变量,指向实际对象
*/
private IService realService;
public ProxyService(IService realService) {
this.realService = realService;
}
@Override
public void sayHello() {
System.out.println("enter sayHello");
// 转发给了实际对象的sayHello方法
this.realService.sayHello();
System.out.println("leave sayHello");
}
}
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = new ProxyService(realService);
proxyService.sayHello();
}
}
enter sayHello
Hello
leave sayHello
二、动态代理
2.1 JDK动态代理
2.1.1 JDK动态代理代码示例
JDK动态代理代码如下:
/**
* JDK动态代理示例
*/
public class SimpleJdkDynamicProxy {
/**
* 代理和实际对象共同的接口
*/
static interface IService {
void sayHello();
}
/**
* 实际对象类
*/
static class RealService implements IService {
@Override
public void sayHello() {
System.out.println("Hello");
}
}
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("enter " + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leave " + 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.sayHello();
}
}
代理对象proxyService的创建方式,不再是静态代理中我们自己实现的代理类new出来的了,而是由Proxy类的静态方法newProxyInstance来创建的;
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
三个参数:
- loader为类加载器;
- interfaces为代理类要实现的接口列表,元素的类型只能是接口;
- h类型是InvocationHandler,是一个接口,只定义了一个invoke方法,对代理接口所有方法的调用都会转给该方法;
返回类型为Object,可以强制转换为interfaces数组中的某个接口类型,但不能强制转换为某个类类型;
SimpleInvocationHandler实现了InvocationHandler,构造方法接收一个参数realObj表示被代理的对象(即实际对象),invoke方法处理所有的接口调用,三个参数:
- proxy表示代理对象本身,不是被代理的对象,一般用处不大;
- method表示正在被调用的方法;
- args表示方法的参数;
不能将proxy作为参数传递给method.invoke(),否则会出现死循环;
2.1.2 动态代理基本原理
运行以下命令,将动态生成的代理类$Proxy0保存到$Proxy.class文件中:
java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true com.transformers.SimpleJdkDynamicProxy
JDK动态生成的代理类代码:
package com.transformers;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0
extends Proxy
implements SimpleJdkDynamicProxy.IService
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sayHello()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.transformers.SimpleJdkDynamicProxy$IService").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
$Proxy0的父类是Proxy,有一个接收InvocationHandler类型的构造方法,保存为实例变量h;$Proxy0实现了IService接口,对于IService每个方法,都会调用h的invoke方法(这里只有sayHello);对于Object中的方法,同样转发给了InvocationHandler(这里有equals、toString、hashCode);
这个类本身与被代理对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关!给定这个接口数组,动态创建每个接口的实现代码,其实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理。
查看Proxy.newProxyInstance源码,JDK动态代理创建proxyService的代码可以用如下代码来代替:
Class<?> cl = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<?>[]{IService.class});
Constructor<?> cons = cl.getConstructor(new Class<?>[] { InvocationHandler.class });
InvocationHandler handler = new SimpleInvocationHandler(realService);
IService proxyService = (IService) cons.newInstance(handler);
proxyService.sayHello();
创建过程分三步:
- 通过Proxy.getProxyClass创建代理类定义,类定义被缓存;
- 获取代理类的构造方法,构造方法中有一个InvocationHandler类型的参数;
- 创建InvocationHandler对象,创建代理类对象;
Proxy.getProxyClass需要两个参数,一个是类加载器ClassLoader,另一个是接口数组。该方法会动态生成一个类,类名以$Proxy开头,后跟一个数字,即上面的$Proxy0;
2.1.3 动态代理的优点
相比于静态代理,动态代理代码复杂且麻烦了许多,但是,使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类;
通用动态代理类示例:
public class GeneralProxy {
static interface IServiceA {
void a();
}
static class ServiceAImpl implements IServiceA {
@Override
public void a() {
System.out.println("ServiceAImpl.a");
}
}
static interface IServiceB {
void b();
}
static class ServiceBImpl implements IServiceB {
@Override
public void b() {
System.out.println("ServiceBImpl.b");
}
}
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 {
Object result = null;
System.out.println("enter " + realObj.getClass().getSimpleName() + "::" + method.getName());
result = method.invoke(realObj, args);
System.out.println("leave " + realObj.getClass().getSimpleName() + "::" + method.getName());
return result;
}
}
public static <T> T getProxy(Class<?> cls, T realObj) {
return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls},
new SimpleInvocationHandler(realObj));
}
public static void main(String[] args) {
IServiceA realServiceA = new ServiceAImpl();
IServiceB realServiceB = new ServiceBImpl();
IServiceA proxyServiceA = getProxy(IServiceA.class, realServiceA);
proxyServiceA.a();
IServiceB proxyServiceB = getProxy(IServiceB.class, realServiceB);
proxyServiceB.b();
}
}
enter ServiceAImpl::a
ServiceAImpl.a
leave ServiceAImpl::a
enter ServiceBImpl::b
ServiceBImpl.b
leave ServiceBImpl::b
两个接口IServiceA和IServiceB,实现类分别是ServiceAImpl和ServiceBImpl,虽然接口和实现类不同,但利用动态代理,可以调用同样的getProxy获取代理对象,共享同样的代理逻辑SimpleInvocationHandler;
2.2 cglib动态代理
/**
* cglib动态代理示例
*/
public class SimpleCGLibDemo {
/**
* 被代理的类
*/
static class RealService {
public void sayHello() {
System.out.println("hello");
}
}
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("enter " + method.getName());
Object result = methodProxy.invokeSuper(object, objects);
System.out.println("leave " + method.getName());
return result;
}
}
/**
* 为一个类生成代理对象
* @param cls
* @param <T>
* @return
*/
private static <T> T getProxy(Class<T> cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls); // 设置被代理的类
enhancer.setCallback(new SimpleInterceptor()); // 设置被代理类的public非final方法被调用时的处理类
return (T) enhancer.create();
}
public static void main(String[] args) {
RealService proxyService = getProxy(RealService.class);
proxyService.sayHello();
}
}
与JDK的InvocationHandler不同的是,SimpleInterceptor中没有被代理的对象,通过MethodProxy的invokeSuper方法调用被代理的方法:
Object result = methodProxy.invokeSuper(object, objects);
但是,不能使用如下的代码调用代理类的方法:
Object result = method.invoke(object, objects);
因为object是代理对象,调用这个方法还会调用到SimpleInterceptor的intercept方法,造成死循环;
在main方法中,没有创建被代理的对象;
cglib实现机制是通过继承实现的,动态创建了一个类,这个类的父类就是被代理的类,代理类重写了父类所有public非final方法,改为调用Callback中的相关方法,在示例代码中,调用了SimpleInterceptor的intercept方法;
3、JDK代理与cglib代理对比
JDK代理面向的是一组接口,为接口动态创建了一个实现类;接口的具体实现逻辑是通过自定义的InvocationHandler实现的,其背后不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择;
cglib代理面向的是一个具体的类,动态创建一个新类,继承了该类,重写了其方法;
JDK代理的是对象,需要先有一个实际对象,自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法;
cglib代理的是类,创建的对象只有一个;
本文详细介绍了代理模式的概念及其在Java中的两种实现方式:静态代理和动态代理。通过代码示例展示了如何使用JDK动态代理和cglib动态代理创建代理对象,并对比了两者之间的区别。
2053

被折叠的 条评论
为什么被折叠?



