代理模式详解
Posted walidake
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式详解相关的知识,希望对你有一定的参考价值。
什么是代理模式
代理模式是对象的结构模式。代理模式为其他对象提供一种代理以控制对这个对象的访问。
简单来说,在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
举个例子
我们打算结婚,但是婚礼的细节我们不想管,这时候我们找到婚庆公司,让他们帮我们包揽婚礼的细节,这就是“代理模式”。既然是代理模式,那么就应该有一个代理角色和真实角色。例子中的“我们”就是真实角色,“婚庆公司”就是代理角色。
我们打算结婚而不需要考虑婚礼细节,这就是代理模式给我们带来的好处。我们不必在目标对象内写清楚每一件事,而是可以交给代理对象代理从而对目标对象进行扩展。
静态代理
如果按照上面的例子该如何组织我们的代码?
我们在组织我们的代码,先要了解下面这几个概念。
角色 | 作用 |
---|---|
抽象角色 | 声明真实对象和代理对象的共同接口。 |
代理角色 | 代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 |
真实角色 | 代理角色所代表的真实对象,是我们最终要引用的对象。 |
实现细节
Step 1:声明抽象角色。
public interface Marry
public void marry();
Step 2:真实角色实现抽象角色接口。
@Override
public void marry()
System.out.println(this.getClass().getSimpleName() + "结婚啦");
Step 3:代理角色持有真实角色引用,实现抽象角色接口,附加其他操作。
private Walidake walidake;
public WeddingCompany(Walidake walidake)
this.walidake = walidake;
@Override
public void marry()
System.out.println("婚礼筹备");
walidake.marry();
System.out.println("婚礼结束");
Step 4:实际使用中,调用代理角色的方法对真实角色进行代理。
public static void main(String[] args)
Walidake walidake = new Walidake();
WeddingCompany weddingCompany = new WeddingCompany(walidake);
weddingCompany.marry();
从上述我们可以总结出:
1.代理角色,真实角色需要实现同一个抽象角色(接口)
2.代理角色需要持有真实角色的引用
上述代理方式我们称之为静态代理。那么有静态代理,那是不是也应该有动态代理?
答案是肯定的。不过,我们并不着急着匆匆进入动态代理的学习,我们先想想在日常编码中有没有看到过静态代理的例子?
我想你必定使用过Java的线程,对Runnable和Thread也很熟悉,而你现在你回过头去看,是否能发现Thread implements Runnable,这其实就是一个代理角色,而我们new Runnable,这就是一个真实角色,赋值给Thread,这是不是就让代理角色持有真实角色的引用。因此,Thread和Runnable这种关系也是静态代理。
动态代理
动态代理不同于静态代理的特点是它更为灵活,因为动态代理就是在运行期间动态生成代理类。我们沿用上面的例子,假设有五百个不一样的人要结婚,都交给婚庆公司来操办,那么按照静态代理的思路来做,我们需要写五百个真实角色,并且代理角色持有这五百个真实角色。这显然不合逻辑。这时候动态代理就应运而生了。
动态代理分两种,一种是基于接口实现的Java Proxy(Java自带的),一种是基于继承实现的cglib代理。下面会分别给出一个小demo,并且从源码解析角度来解析二者动态代理的实现。
Java Proxy
public class WeddingCompany implements InvocationHandler
private Object object;
public WeddingCompany(Object object)
this.object = object;
@SuppressWarnings("unchecked")
public <T> T getProxy()
return (T)Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
object.getClass().getInterfaces(),
this);
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
if ("marry".equals(method.getName()))
System.out.println("婚礼筹备");
method.invoke(object, args);
System.out.println("婚礼结束");
return null;
InvocationHandler相当于一个处理器,在invoke方法中我们能够操作真实对象,可以附加其他操作。而我们通过Proxy.newProxyInstance(..)方法生成代理。下面invoke参数的解释说明。
参数 | 说明 |
---|---|
proxy | 指代我们所代理的那个真实对象 |
method | 指代的是我们所要调用真实对象的某个方法的Method对象 |
args | 指代的是调用真实对象某个方法时接受的参数 |
实现InvocationHandler接口并附加操作后,获取代理角色。
//第一个人
Walidake walidake = new Walidake();
Marry marry = new WeddingCompany(walidake).getProxy();
marry.marry();
System.out.println();
//第二个人
Other other = new Other();
Marry marry2 = new WeddingCompany(other).getProxy();
marry2.marry();
运行结果:
那么如此一来,不论是两个人,还是五百人都可以通过同一个动态代理进行代理调用,这带给我们极大的方便。而为什么动态代理能实现这样的功能呢?
我们选择Proxy.newInstance(…)作为入口查看源码。
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
//将我们传入的接口进行复制
final Class<?>[] intfs = interfaces.clone();
//得到proxy代理类
Class<?> cl = getProxyClass0(loader, intfs);
//取得proxy代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//取得InvocationHandler对象,即代理处理类对象
final InvocationHandler ih = h;
//判别是否为public类型
if (!Modifier.isPublic(cl.getModifiers()))
//访问控制
AccessController.doPrivileged(new PrivilegedAction<Void>()
public Void run()
cons.setAccessible(true);
return null;
);
//反射构造proxy类对象
//由此可以推测,proxy的构造方法应该是 public Proxy(InvocationHandler handler)
return cons.newInstance(new Object[]h);
我们大体上走完了一个proxy代理流程(了解到其实也是通过反射来实现对类的代理)那么我们又得想,那如何能得到proxy的Class,点击进入getProxyClass0(loader, intfs):
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces)
//接口最多不得超过65535
//字节码文件中严格规定,这点不必纠结
if (interfaces.length > 65535)
throw new IllegalArgumentException("interface limit exceeded");
//从缓存中取得proxy类
return proxyClassCache.get(loader, interfaces);
我们继续追踪下去(1.7和1.8代码已发生变化,下面是针对1.8进行解析,1.7有疑惑的,可在评论区留言):
if (supplier != null)
// supplier might be a Factory or a CacheValue<V> instance:supplier可能是个Factory或者CacheValue
//然后发现从其中取到了proxy类,说明应该定位到该方法内
V value = supplier.get();
if (value != null)
return value;
定位到supplier.get()方法内:
//判断从valueFactory得到的proxy Class是否为空
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
return value;
点击进入ProxyClassFactory中的apply(key, parameter)内(注意:该类是Proxy的内部类):
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces)
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces)
Class<?> interfaceClass = null;
try
//通过类加载器加载class到内存
interfaceClass = Class.forName(intf.getName(), false, loader);
catch (ClassNotFoundException e)
if (interfaceClass != intf)
throw new IllegalArgumentException(
intf + " is not visible from class loader");
//判断class是否接口
if (!interfaceClass.isInterface())
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
//没有重复定义的接口
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null)
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
//proxy Class所在包
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//判断非public类是否在同一包下
for (Class<?> intf : interfaces)
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags))
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null)
proxyPkg = pkg;
else if (!pkg.equals(proxyPkg))
throw new IllegalArgumentException(
"non-public interfaces from different packages");
if (proxyPkg == null)
// 等价于com.sun.proxy.
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
long num = nextUniqueNumber.getAndIncrement();
//proxyPkg == null时,等价于com.sun.proxy.$proxy0,num会根据动态代理类的数量增加
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//得到生成的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try
//本地方法调用
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
catch (ClassFormatError e)
throw new IllegalArgumentException(e.toString());
一般的分析到这里就结束了,而我们应该更关注的是proxy类是如何组织生成的。因此,继续进入ProxyGenerator.generateProxyClass方法中。
public static byte[] generateProxyClass(final String name,
Class[] interfaces)
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
//生成字节码细节
final byte[] classFile = gen.generateClassFile();
//将proxy代理类写入到硬盘中
if (saveGeneratedFiles)
java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>()
public Void run()
try
FileOutputStream file = new FileOutputStream(
dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
catch (IOException e)
throw new InternalError("I/O exception saving generated file: "+ e);
);
return classFile;
查看更多细节:
private byte[] generateClassFile()
//生成proxy代理类的hashcode,equals,toString方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//添加各个接口的方法
//这就是为什么我们能够通过代理调用接口方法实现的原因
for (int i = 0; i < interfaces.length; i++)
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++)
addProxyMethod(methods[j], interfaces[i]);
//检查返回类型
for (List<ProxyMethod> sigmethods : proxyMethods.values())
checkReturnTypes(sigmethods);
//编译成class的相关内容
try
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values())
for (ProxyMethod pm : sigmethods)
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE
| ACC_STATIC));
methods.add(pm.generateMethod());
methods.add(generateStaticInitializer());
catch (IOException e)
throw new InternalError("unexpected I/O Exception");
if (methods.size() > 65535)
throw new IllegalArgumentException("method limit exceeded");
if (fields.size() > 65535)
throw new IllegalArgumentException("field limit exceeded");
//将组织好的class文件写入到文件中
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++)
cp.getClass(dotToSlash(interfaces[i].getName()));
//设置为只读模式
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try
//以下是class文件的结构,想深入了解的话可以看深入Java虚拟机
//或者可以留意我后面相应的文章
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++)
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields)
f.write(dout);
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods)
m.write(dout);
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
catch (IOException e)
throw new InternalError("unexpected I/O Exception");
return bout.toByteArray();
小结
到这里,我们就解析完了Java Proxy。从代码中我们也不难看出真实角色要实现抽象角色的目的,就是为了底层能够生成class字节码从而生成代理角色,对真实角色进行代理。
更多
为了更好地了解Proxy代理类的结构(顺便加深印象),我们不妨通过一个Generator类来生成相应的class文件:
public static void main(String[] args)
createProxyClassFile();
public static void createProxyClassFile()
String name = "CustomProxy";
byte[] data = sun.misc.ProxyGenerator.generateProxyClass(name,
new Class[] Marry.class );
try
FileOutputStream out = new FileOutputStream(name + ".class");
out.write(data);
out.close();
catch (Exception e)
e.printStackTrace();
通过反编译可以得到:
public final class CustomProxy extends Proxy implements Marry
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public CustomProxy(InvocationHandler paramInvocationHandler)
super(paramInvocationHandler);
public final boolean equals(Object paramObject)
try
return ((Boolean) this.h.invoke(this, m1,
new Object[] paramObject )).booleanValue();
catch (RuntimeException localRuntimeException)
throw localRuntimeException;
catch (Throwable localThrowable)
throw new UndeclaredThrowableException(localThrowable);
public final String toString()
try
return ((String) this.h.invoke(this, m2, null));
catch (RuntimeException localRuntimeException)
throw localRuntimeException;
catch (Throwable localThrowable)
throw new UndeclaredThrowableException(localThrowable);
public final int hashCode()
try
return ((Integer) this.h.invoke(this, m0, null)).intValue();
catch (RuntimeException localRuntimeException)
throw localRuntimeException;
catch (Throwable localThrowable)
throw new UndeclaredThrowableException(localThrowable);
public final void marry()
try
//最最最重要的就是marry方法中的invoke方法
//调用了自定义InvocationHandler的invoke方法
//这是能实现动态代理的根本原因
this.h.invoke(this, m3, null);
return;
catch (RuntimeException localRuntimeException)
throw localRuntimeException;
catch (Throwable localThrowable)
throw new UndeclaredThrowableException(localThrowable);
static
try
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] Class.forName("java.lang.Object") );
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("com.walidake.dynamic_proxy.proxy.Marry")
.getMethod("marry", new Class[0]);
catch (NoSuchMethodException localNoSuchMethodException)
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
catch (ClassNotFoundException localClassNotFoundException)
throw new NoClassDefFoundError(
localClassNotFoundException.getMessage());
既然上述是基于接口实现的代理,那我们可不可以不写接口而是直接写一个类,然后也实现代理的功能呢?
我们说过,有两种动态代理的方式。cglib就是不通过接口也能实现动态代理的代理方式。
cglib
cglib是一个强大的高性能的代码生成包,可以为那些没有接口的类创建模仿(moke)对象。上一小节我们说到Java Proxy是通过生成字节码,再把类加载进内存后实现Proxy进行动态代理的。同样地,cglib也是通过生成操作字节码的技术实现动态代理的。但与前者不同的是它并不直接操作字节码,而是通过一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。因此,cglib包要依赖于asm包,需要一起导入。
依然沿用上面的例子,这次我们不使用接口实现的方式。
实现细节:
public class WeddingCompany implements MethodInterceptor
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz)
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(clazz);
en.setCallback(this);
//生成代理实例
return (T)en.create();
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable
Object result = null;
if ("marry".equals(method.getName()))
System.out.println("婚礼筹备");
//通过继承的方法实现代理,因此这里调用的是invokeSuper
result = methodProxy.invokeSuper(object, args);
System.out.println("婚礼结束");
return result;
MethodInterceptor是方法拦截器,我们能在这里做真实对象的附加操作,object就是我们的真实对象,method、args就是真实对象调用的方法和参数,methodProxy是方法代理。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使 用,因为它更快。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。
在这个例子中,我们通过Enhancer的无参数构造器时用来创建target实例,并使用setSuperClass传入代理的父类,setCallback决定方法回调。
调用细节:
public static void main(String[] args)
Walidake proxy = new WeddingCompany().getProxy(Walidake.class);
proxy.marry();
运行结果:
显然我们看到的是Walidake$$EnhancerByCGLIB\\$\\$d8d9733e,而不是Walidake。这说明cglib代理类成功代理了真实角色。
接下来,我们选取Enhancer的create()作为入口,查看源码:
public Object create()
//生成代理类是否只读
classOnly = false;
argumentTypes = null;
return createHelper();
点击进入createHelper():
private Object createHelper()
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
//具体实现细节被隐藏在父类中
Object result = super.create(key);
return result;
点击进入super.create(key):
protected Object create(Object key)
try
//取得类加载器
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
...
//从类加载器数据中取出cglib代理类
Object obj = data.get(this, getUseCache());
if (obj instanceof Class)
//实例化代理类对象
return firstInstance((Class) obj);
//因为缓存了反射实例,能够快速实例化
return nextInstance(obj);
进入data.get(this, getUseCache()),发现这样一行:
return gen.generate(ClassLoaderData.this);
继续进行追踪:
protected Class generate(ClassLoaderData data)
synchronized (classLoader)
//取得cglib代理类类名
//实现细节:DefaultNamingPolicy.getClassName(String, String, Object, Predicate)中
//不多做扩展,可以自己看看
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
//生成cglib代理类字节码
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) // just in case
//通过类加载器加载代理Class
if (protectionDomain == null)
gen = ReflectUtils.defineClass(className, b, classLoader);
else
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
return gen;
离真相越来越近,点击进入generate(this):
transform(cg).generateClass(cw);
点击进入generateClass(cw),注意这时候我们又回到了Enhancer类:
public void generateClass(ClassVisitor v) throws Exception
//这就是设置setSuperClass的原因
//让cglib代理类通过继承的方式进行代理
Class sc = (superclass == null) ? Object.class : superclass;
if (TypeUtils.isFinal(sc.getModifiers()))
throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
//过滤构造器
filterConstructors(sc, constructors);
List actualMethods = new ArrayList();
List interfaceMethods = new ArrayList();
final Set forcePublic = new HashSet();
//确定需要添加的所有方法
getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
List methods = CollectionUtils.transform(actualMethods, new Transformer()
public Object transform(Object value)
Method method = (Method)value;
int modifiers = Constants.ACC_FINAL
| (method.getModifiers()
& ~Constants.ACC_ABSTRACT
& ~Constants.ACC_NATIVE
& ~Constants.ACC_SYNCHRONIZED);
if (forcePublic.contains(MethodWrapper.create(method)))
modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
return ReflectUtils.getMethodInfo(method, modifiers);
);
ClassEmitter e = new ClassEmitter(v);
//写入类信息
if (currentData == null)
e.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
Type.getType(sc),
(useFactory ?
TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
TypeUtils.getTypes(interfaces)),
Constants.SOURCE_FILE);
else
e.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
null,
new Type[]FACTORY,
Constants.SOURCE_FILE);
...具体的字节码操作细节
//结束写入
e.end_class();
小结
到这里,cglib代理类的解析也就结束了。我们现在知道为什么说它是基于继承的代理方式,从源代码中我们就可以看出来。
更多
同样地,我们也打印出cglib生成类的结构。使用了反射,只能打印出一个轮廓,而不能打印完整的内容。(试过javassist,classloader加载,各种谷歌都没能找出较好的方案,暂时只能使用反射做):
public class Walidake$$EnhancerByCGLIB$$d8d9733e extends com.walidake.dynamic_proxy.cglib.Walidake
public Walidake$$EnhancerByCGLIB$$d8d9733e();
public final boolean equals(Object);
public final java.lang.String toString();
public final int hashCode();
protected final java.lang.Object clone();
public java.lang.Object newInstance(Callback[]);
public java.lang.Object newInstance(Class[], Object[], Callback[]);
public java.lang.Object newInstance(Callback);
public final void marry();
public void setCallback(int, Callback);
public void setCallbacks(Callback[]);
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[]);
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[]);
public net.sf.cglib.proxy.Callback getCallback(int);
public [Lnet.sf.cglib.proxy.Callback; getCallbacks();
final java.lang.Object CGLIB$clone$4();
final void CGLIB$marry$0();
final boolean CGLIB$equals$1(Object);
final java.lang.String CGLIB$toString$2();
final int CGLIB$hashCode$3();
static void CGLIB$STATICHOOK1();
private static final void CGLIB$BIND_CALLBACKS(Object);
public static net.sf.cglib.proxy.MethodProxy CGLIB$findMethodProxy(Signature);
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$marry$0$Method;
private static final MethodProxy CGLIB$marry$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
从这里我们能更直观地看出cglib是通过继承原先的真实角色生成代理类。
再扩展
cglib也可以基于接口实现,可以关注Mixin这个类。另外值得注意的是CallbackFilter,可以实现相应的权限控制。简略提一下,后面可以看看。
因此,我们现在知道两种动态代理类都是基于字节码的操作生成相应的代理类,不同的是Java Proxy是基于接口实现的,而cglib是基于继承实现的。而值得一提的是,
总结
我们把代理分为两种:静态代理和动态代理。
应用场景
静态代理主要用来处理少部分类的托管或者扩展。静态代理对于被代理的对象很固定,我们只需要去代理一个类或者若干固定的类,数量不是太多的时候,可以使用,而且其实效果比动态代理更好。
动态代理在运行期间动态生成代理类,需要消耗的时间会更久一点。优点是可以做很多类的扩展(譬如可以通过动态代理实现aop,hibernate使用cglib来代理单端多对一和一对一关联等)。而且如果一个类的接口发生了变化,那么静态代理这时候修改起来就很麻烦了。这就是说动态代理灵活的原因。
动态代理主流的实现有两种,一种是基于接口的Java Proxy的代理机制,一种是基于继承的cglib代理机制。两种也都有其应用场景。(视乎具体业务逻辑而言)
代理模式更多的应用场景(了解即可)
1. 创建开销大的对象时候,比如显示一幅大的图片,我们将这个创建的过程交给代理去完成,我们称之为虚代理(Virtual Proxy)。
2. 为网络上的对象创建一个局部的本地代理,比如要操作一个网络上的一个对象(网络性能不好的时候,问题尤其突出),我们将这个操纵的过程交给一个代理去完成,我们GoF称之为远程代理(Remote Proxy)。
3. 对对象进行控制访问的时候,比如在Jive论坛中不同权限的用户(如管理员、普通用户等)将获得不同层次的操作权限,我们将这个工作交给一个代理去完成,我们称之为保护代理(Protection Proxy)。
4. 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。是指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它;或当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。它们都是通过代理在访问一个对象时附加一些内务处理。又称智能引用(Smart Reference)代理。
在所有种类的代理模式中,虚拟(Virtual)代理、远程(Remote)代理、智能引用代理(Smart Reference Proxy)和保护(Protect or Access)代理是最为常见的代理模式。
至此,代理模式全部解析完。
如果对此前那篇“用自定义注解做点什么”中袖珍版mybatis的动态代理模块有疑惑的,现在可以回去看看。应该是不一样的体验。
另外,本文是对后面解析Spring aop的铺垫。
项目地址:https://github.com/walidake/proxy
以上是关于代理模式详解的主要内容,如果未能解决你的问题,请参考以下文章