基于Dynamic Proxy技术的方法拦截器开发
代理技术——使代码变得更简洁
在面向对象编程中,会用到大量的类,并且会多次调用类中的方法。有时可能需要对这些方法的调用进行一些控制。如在权限管理中,一些用户没有执行某些方法的权限。又如在日志系统中,在某个方法执行完后,将其执行的结果记录在日志中。处理这些需求的一般做法是在调用这些方法的地方加上适当的代码。如以下C#代码如示:
public class Class1 { public void MyMethod() { …… } } |
static class Program { …… static void { Class if(permit()) { c1.MyMethod(); logger(); } } …… } |
在以上代码中,permit()是一个得到MyMethod方法执行权限的函数,如果MyMethod方法可以被执行,那么permit()返回true,否则,返回false。logger()是记录日志的函数。
我们可以看出,在这段程序中存在一个问题,就是它的主要功能是执行MyMethod方法,至于权限控制以及日志功能只是它的次要功能(这里说它们是次要功能,并不是说它们不重要,而是说没有这些功能并不影响程序的核心功能的执行)。而将这些次要功能和程序的主要业务逻辑混在一起,如果程序所涉及的类比较多的话,这些次要功能的代码将和业务逻辑代码紧密地结合在一起,这样在修改某一部分时(比如换一个写日志的函数),必须要修改大量的代码。而且程序员在考虑业务逻辑的同时,还要关注这些次要功能,从而无法将精力集中在业务逻辑上。
根据以上在软件开发中存在的不足,人们提出了代理(Proxy)技术用以解决上述的问题。代理技术的基本原理是在待调用类和调用者之间加了一个代理类(Proxy Class),这个代理类有两个作用。第一个作用是执行被代理类的方法。第二个作用是在代理类中可以加入控制这个方法的代码,从而使调用者不必关心和自己的业务无关的事情。现将上例用代理技术重新实现,代码如下:
public class Class1 { public virtual void MyMethod() { …… } } |
public class ProxyClass1 : Class1 { public override void MyMethod() { if(permit()) { base.MyMethod(); logger(); } } } |
static class Program { …… static void { Class1 pc1 = new ProxyClass1(); pc1.MyMethod(); } …… } |
从以上代码可以看出,所有的次要功能代码(在这里指权限和日志)都被放到了代理类中,而在主程序中只有和业务逻辑相关的代码(在这里指MyMethod方法)。从而可以达到将次要功能和主要功能分开的目的。
但使用代理技术也有其不足之处。从以上代码可以看出,如果在Class1中加入一个方法,那么就得在ProxyClass1中加入一个方法来override。如果有非常多的类,那么工作量是相当巨大的,并且有很多重复代码。于是一种经过改良的代理技术出现了,这就是下面要介绍的动态代理(Dynamic Proxy)技术。它的基本原理和代理技术类似,只是生成代理类是完全自动的,而且可以对方法进行匹配控制 (如对所有以My开头的方法进行控制)。
动态代理(Dynamic Proxy)——更快捷的代理技术[一级标题]
动态代理(Dynamic Proxy)从它的名字就可以看出这项技术的核心就是“动态”。它可以根据被代理类自动生成代理类。如果要向Class1中增加或删除方法时,可以完全不用考虑代理类的事情,因为这些代理类都是自动生成的,调用者根本就感觉不到。
Dynamic Proxy技术的基本原理是通过扫描被代理类的所有public方法,并且自动生成一个从被代理类继承的类,然后在这个生成的类中override这些public方法。说到这,需要解释一下自动生成代理类。这里说的自动生成,并不是象我们平常所说的生成源代码,而是直接使用中间语言(Intermedial Language)直接在内存中生成。这样在速度上和源码编译而成的中间语言相近。我们可以利用Reflection Emit API来直接生成中间语言。下面就详细介绍一下自动生成动态代理类的过程。
生成中间语言的先期工作——初始化
为了生成中间语言,需要一个ModuleBuilder对象。Module在Assembly中相当于资源(如Class,Interface,Enum等)的集合,一个Assembly中可以有多个Module。下面是实现初始化Module的代码:
private ModuleBuilder m_Module;
private AssemblyBuilder m_Assembly;
private void InitModule()
{
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName asmName = new AssemblyName("DynamicModule");
m_Assembly = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
m_Module = m_Assembly.DefineDynamicModule("Module", "DynamicProxy.dll");
}
在这个初始化过程中,首先在当前域中建立一个动态的装配集(Dynamic Assembly),然后用它建立一个动态模块(Dynamic Module)。“Module”是这个动态模块的名字,而后面的“DynamicProxy.dll”是为了在生成代理类后将其保存到硬盘上,以便观察生成的结果。得到当前域有很多方法,也可以将这个函数的第一条语句换成 AppDomain domain = System.Threading.Thread.GetDomain();。 在编写以上代码之前,需要引入两个namespace。
using System.Reflection;
using System.Reflection.Emit;
在Dynamic Module中定义一个代理类
现在我们开始在Dynamic Module中定义一个代理类。在Reflection Emit API中将Class、Interface等称为Type,通过TypeAttributes指定具体的行为。下面是定义代理类的具体实现代码:
private TypeBuilder m_TypeBuilder;
private Type m_Type;
private void GenerateType()
{
m_TypeBuilder = m_Module.DefineType(m_Type.Name + "_" + m_Type.GetHashCode().ToString(),
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, m_Type);
}
从以上代码可以看出,代理类是通过Dynamic Module中的DefineType方法建立的。这个代理类的名字是被代理类的名称后面接“-”,再加上被代理类的Hash Code。m_Type变量保存的是被代理类的Type,它是通过Create(这个方法在以后介绍)方法传入。这个方法的第二个参数是说明使用DefineType建立的类型种类。在这里表明建立一个公共(Public)的、不可被继承的(Sealed)类(Class)。DefineType的默认动作是建立一个类,因此TypeAttributes.Class可以省略。最后一个参数代理所定义的代理类是从m_Type所指向的类继承的。
为每一个public方法生成一个Nested Class
到这一步,我们已经进入Dynamic Proxy的最核心的部分。在看实现代码之前,先介绍一下Dynamic Proxy所涉及的技术细节。
在前面已经说过,Dynamic Proxy是通过继承技术在子类中干预父类方法的执行。但是这个子类,也就是代理类,是自动生成的,调用者根本感觉不到它的存在,如果按以上所说的技术,用户是无法在一个自己无法干预的类中写代码的。这样就需要我们在生成代理类时提供一个外部可访问的机制。在代理类中某个override方法执行时,不是直接调用被代理类的方法,而是使用这个机制来引发一个用户可干预的动作。更具体地说,就是将被代理类的方法通过这个机制交给调用者来执行,而不是由代理类自己执行。如果要实现这种机制,就需要使用一个类——MulticastDelegate。
MulticastDelegate的作用相当于C/C++中函数指针。但是需要注意的是MulticastDelegate在C#源代码中是不可被继承的,它只能在IL中被继承。使用IL生成的代码被译成C#源码如下如示:
public sealed class Method_delegate : MulticastDelegate
{
public Method_delegate (object local, IntPtr i);
public override void Invoke(string str);
}
这段代码是声明一个从MulticastDelegate继承的类,这个子类中保存了相应的方法指针。其中这个类的构造函数有两个参数,一个是这个子类的对象引用(object local),另外一个是方法的指针(IntPtr i)。另外Invoke方法的参数是根据被代理类中相应方法的参数而确定的,在这个例子中,只有一个string类型的参数。需要注意的是这个类无法用C#编译,它只能用IL写。
我们会为被代理类中的每一个public方法都生成这么一个类,然后将相对应的方法的指针保存在这个类中,最后将这个类作为参数传出去,供用户调用。下面是自动生成这些类的具体实现:
private TypeBuilder[] m_NestedTypeBuilders;
private ConstructorBuilder[] m_NestedTypeConstructors;
private void GenerateDelegateClass()
{
MethodInfo[] methodInfos = m_Type.GetMethods();//得到被代理类的所有public方法信息
//根据方法的个数初始化Nested Type数组,以后将保存并使用到这些值
m_NestedTypeBuilders = new TypeBuilder[methodInfos.Length];
m_NestedTypeConstructors = new ConstructorBuilder[methodInfos.Length];
//开始为每一个方法建立Nested Class
for (Int32 i = 0; i < m_NestedTypeBuilders.Length; i++)
{
//建立Nested Class
m_NestedTypeBuilders[i] = m_TypeBuilder.DefineNestedType("__" + methodInfos[i].Name + "__delegate", TypeAttributes.NestedPrivate | TypeAttributes.Sealed, typeof(MulticastDelegate));
//为每一个Nested Class加一个构造函数(Constructor)
m_NestedTypeConstructors[i] =
m_NestedTypeBuilders[i].DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard, new Type[] { typeof(Object), typeof(IntPtr) });
m_NestedTypeConstructors[i].SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
Type[] argsType = GetParameterTypes(methodInfos[i]);
//在这个Nested Class中加入一个在运行时(runtime)管理的Invoke方法
MethodBuilder mb = m_NestedTypeBuilders[i].DefineMethod("Invoke", MethodAttributes.Public, CallingConventions.Standard, methodInfos[i].ReturnType, argsType);
mb.SetImplementationFlags(MethodImplAttributes.Runtime |
MethodImplAttributes.Managed);
}
}
//得到一个方法所有参数的Type数组,这个方法在以后会被多次调用
private Type[] GetParameterTypes(MethodInfo methodInfo)
{
ParameterInfo[] args = methodInfo.GetParameters();
Type[] argsType = new Type[args.Length];
for (Int32 j = 0; j < args.Length; j++) { argsType[j] = args[j].ParameterType; }
return argsType;
}
以上代码有几点需要注意。
1、生成嵌套类(Nested Class),要使用DefineNestedType,而不能用DefineType。在使用Type Attributes时要用NestedPrivate或NestedPublic,不能用NotPublic或Public。
2、在这里定义的构造函数(Constructor)和传统意义上的构造函数不一样。因为这个构造函数只有函数头,而没有函数体。这是为什么呢?答案可以从它的下一条语句得到。在这条语句中用了SetImplementationFlags方法将这个构造函数设成了Runtime和Managed。也就是说这个构造函数是在运行时管理的,而不是由cil管理的。即它的函数体是在运行过程中生成的,而不是事先写好的。
3、Invoke方法的定义和构造函数的定义类似,也没有函数体。它也是在运行时管理的。
为每一个Nested Class声明一个变量
为了保存相应的函数指针,我们来为每一个Nested Class定义一个变量。这些变量在稍后将在代理类的构造函数中进行初始化。下面是定义变量的实现代码:
private FieldBuilder[] m_MultiCastDelegates;
private void GenerateFields()
{
// 声明__Interceptor变量
m_Interceptor = m_TypeBuilder.DefineField( "__Interceptor", typeof(Interceptor), FieldAttributes.Private );
// 开始为每一个Nested Class声明一个变量
MethodInfo[] methodInfos = m_Type.GetMethods();
m_MultiCastDelegates = new FieldBuilder[methodInfos.Length];
for (Int32 i = 0; i < methodInfos.Length; i++)
{
m_MultiCastDelegates[i] = m_TypeBuilder.DefineField(methodInfos[i].Name + "_field", m_NestedTypeBuilders[i], FieldAttributes.Private);
}
}
这个方法的第一条语句是声明一个Interceptor类型的变量,Interceptor是用户定义的一个类,负责截获方法的执行。_Interceptor是通过代理类的参数来传入的,这个将在以后介绍。声明一个变量用TypeBuilder中的DefineField方法,这里声明的是一个private类型的变量。变量类型是m_NestedTypeBuilders[i]中所指的类型。
回调(Callback)方法的生成
以前曾讲过,代理类中override方法是在方法体内使用“base”调用父类的同名方法。但是这样做有一些问题,当我们将override方法的指针存入Nested Class变量,并且在Interceptor中的Call(这个方法将在以后介绍)方法中执行这个override方法,会产生一个嵌套调用。因为Call方法是在代理类中的override方法中调用的。因此我们需要加入另外一个方法(称为回调方法)来调用父类的方法,并且将这个回调方法的指针加入到Nested Class变量中。下面是回调函数的实现代码:
private MethodBuilder[] m_CallBackMethods;
private void GenerateCallBackMethods()
{
MethodInfo[] methodInfos = m_Type.GetMethods();
m_CallBackMethods = new MethodBuilder[methodInfos.Length];
for (Int32 i = 0; i < methodInfos.Length; i++)
{
Type[] argTypes = GetParameterTypes(methodInfos[i]); // 得到当前方法所有参数的Type
m_CallBackMethods[i] = m_TypeBuilder.DefineMethod("callback_" + methodInfos[i].Name, MethodAttributes.Private, CallingConventions.Standard, methodInfos[i].ReturnType, argTypes); //定义回调方法
//得到IL的产生器
ILGenerator ilGenerator = m_CallBackMethods[i].GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0); // 将代理类的引用压入evaluation栈
for (Int32 j = 0; j < argTypes.Length; j++)
{
ilGenerator.Emit(OpCodes.Ldarg, j + 1); //将回调函数的每一个参数压入evaluation栈
}
ilGenerator.Emit(OpCodes.Call, methodInfos[i]); //调用父类的方法
ilGenerator.Emit(OpCodes.Ret); //返回
}
}
在上面的方法中涉及到C#的一项重要技术,那就是直接在C#源代码中写IL,并且这些IL是在内存中直接通过.net framework二次编译成本地代码。在这个方法中首先用DefineMethod定义一个回调方法。然后通过GetILGenerator方法得到一个IL产生器。在产生器中,写IL主要是通过Emit方法实现的。IL指令可以通过OpCodes类得到。有一个概念在这里不得不提,那就是evaluation栈。这个栈在向方法中写入IL时自动建立,它的主要作用是在调用方法、建立对象等需要传递参数时,需先将这些要传的参数值按顺序压入栈中,然后再执行相关的操作。在这个方法中,调用父类方法时,先将方法的参数压栈,再调用方法。在调用时,方法依次将evaluation栈中的值依次出栈。在每一个方法的最后的一条IL语句必须是OpCodes.Ret,而不管方法是否需要返回值,如果evaluation栈到最后还剩一个值,那么这条指令将这个值出栈返回。当然,如果方法返回的是void,将抛出例外。或者方法有返回值,而evaluation为空,那么同样会抛出例外。
覆盖(Override)方法的生成
Override函数是面向用户的接口。它负责向用户提供调用回调方法的机制(通过Interceptor的Call方法)。实现代码如下:
private void GenerateOverrideMethods()
{
MethodInfo[] methodInfos = m_Type.GetMethods();
for (Int32 i = 0; i < methodInfos.Length; i++)
{
Type[] argTypes = GetParameterTypes(methodInfos[i]);
MethodBuilder mb = m_TypeBuilder.DefineMethod(methodInfos[i].Name,
MethodAttributes.Public | MethodAttributes.Virtual,
CallingConventions.Standard,
methodInfos[i].ReturnType,
argTypes); //定义override方法
ILGenerator ilGenerator = mb.GetILGenerator();
//将m_Interceptor变量压入栈
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, m_Interceptor);
//将Call的第一个参数压栈
ilGenerator.Emit(OpCodes.Ldstr, methodInfos[i].Name);
//将Call的第二个参数压栈
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, m_MultiCastDelegates[i]);
//将Call的第三个参数压栈
LocalBuilder local = ilGenerator.DeclareLocal(typeof(Object[]));
ilGenerator.Emit(OpCodes.Ldc_I4, argTypes.Length);
ilGenerator.Emit(OpCodes.Newarr, typeof(Object));
ilGenerator.Emit(OpCodes.Stloc, local);
ilGenerator.Emit(OpCodes.Ldloc, local);
for (Int32 j = 0; j < argTypes.Length; j++)
{
ilGenerator.Emit(OpCodes.Ldc_I4, j);
ilGenerator.Emit(OpCodes.Ldarg, j + 1);
ilGenerator.Emit(OpCodes.Box, argTypes[j]);
ilGenerator.Emit(OpCodes.Stelem_Ref);
ilGenerator.Emit(OpCodes.Ldloc, local);
}
//调用Call方法
ilGenerator.Emit(OpCodes.Call, typeof(Interceptor).GetMethod("Call", new
Type[]{ typeof(String), typeof(MulticastDelegate), typeof(Object[]) }));
if (methodInfos[i].ReturnType.Equals(typeof(void)))
{
ilGenerator.Emit(OpCodes.Pop); //如果override方法返回void, 将Call返回的null出栈
}
else
{
//将返回值是值类型(value type)时拆箱(Unbox)
ilGenerator.Emit(OpCodes.Unbox_Any, methodInfos[i].ReturnType);
}
ilGenerator.Emit(OpCodes.Ret);
}
}
在上面的代码中出现了一个OpCodes.Newarr指令,功能是建立一个数组。这个数组中保存的是override方法传进来的参数值。另外还需要提到一点,当这个override方法返回void时,Call返回的是null,因此必须将这个null从evaluation栈弹出来才能执行正确。如果override的返回值是一个值类型(如int、float等),必须调用OpCodes.Unbox_Any指令,否则不能正确地返回值。
生成代理类的构造函数
到这里,这个代理类已经基本完成了。但还缺一个最重要的部分——构造函数。这个构造函数主要有两个功能。
1、 调用父类相应的构造函数。
2、 初始化相关的变量。
实现代码如下:
private void GenerateConstructor()
{
m_ConstructorBuilder = m_TypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(Interceptor)});
ILGenerator ilGenerator = m_ConstructorBuilder.GetILGenerator();
//调用父类构造函数(假设父类只有无参数的构造函数)
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, m_Type.GetConstructor(new Type[] { }));
// 初始化__Interceptor变量
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, m_Interceptor);
// 初始化Nested Class变量
for (Int32 i = 0; i < m_MultiCastDelegates.Length; i++)
{
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldftn, m_CallBackMethods[i]);
ilGenerator.Emit(OpCodes.Newobj, m_NestedTypeConstructors[i]);
ilGenerator.Emit(OpCodes.Stfld, m_MultiCastDelegates[i]);
}
ilGenerator.Emit(OpCodes.Ret);
}
这个构造函数有一个Interceptor类型的参数。将初始化Nested Class变量的指令集转换为C#源码为:m_MultiCastDelegates[i] = new Method_Delegate(this, m_CallBackMethods[i]),Method_Delegate为相应的Nested Class类名。
在前面许多地方都提到了Interceptor类,这个类是代理类的使用者自己定义的,然后通过参数传入代理类。类中只有一个Call方法。代码如下:
public Object Call(String methodName, MulticastDelegate methodDelegate, params Object[] args)
{
return methodDelegate.Method.Invoke(methodDelegate.Target, args);
}
这个方法有三个参数,第一个参数是override方法的名字,第二个参数就是代理类中的相应的Nested Class变量,第三个参数是相应override方法的参数值。通过这个方法,调用者可以根据methodName,args等信息对override进行控制。假设有一个名字MyMethod的override方法,如果我们想让这个方法执行完后写一条日志,那么这个Call函数可以这么写:
public Object Call(String methodName, MulticastDelegate methodDelegate, params Object[] args)
{
Object obj = methodDelegate.Method.Invoke(methodDelegate.Target, args);
if (methodName.Equals("MyMethod"))
logger();
return obj;
}
这样所有调用MyMethod的语句都会在这个方法执行完后执行logger()函数写日志。
Wrap函数的建立
在前面已经建立了实现Dynamic Proxy所需的所有方法,最后要做的工作就是将这些方法组合起来。这些工作将在Wrap方法中完成:
public Object Wrap(Type type)
{
Type newType = null;
try
{
m_Type = type;
GenerateType();
GenerateDelegateClass();
GenerateFields();
GenerateCallBackMethods();
GenerateOverrideMethods();
GenerateConstructor();
newType = m_TypeBuilder.CreateType(); //建立代理类的Type
//建立Nested Class的Type
foreach (TypeBuilder tb in m_NestedTypeBuilders)
tb.CreateType();
m_Assembly.Save("DynamicProxy.dll"); //保存这个代理类为DynamicProxy.dll
}
catch (Exception err)
{
throw err;
}
//建立代理类实例
return Activator.CreateInstance(newType, new Interceptor());
}
在这个方法中,有一个地方需要注意,在建立一个Nested Class之前,应先建立包含这个Nested Class的类,然后再建立Nested Class。在建完代理类后,将其保存在DynamicProxy.dll中(如果你感兴趣,可以用Microsoft的ildasm.exe查看这个dll的IL。)。最后建立了代理类的实例,将Interceptor的一个实例通过参数传入到代理类的构造函数中。
如何应用动态代理技术
至此所有的工作都完成了。我们可以将以上代码放入一个叫DynamicProxyBuilder的类中。以下代码是如何使用DynamicProxyBuilder的一个例子:
DynamicProxyBuilder dynamicProxyBuilder = new DynamicProxyBuilder();
Class1 class1 = (Class1)dynamicProxyBuilder.Wrap(typeof(Class1));
class1.MyMethod();
以上代码可以为Class1生成一个代理类,如果用户想控制这个方法,可以在Call方法中加入适当的代码即可。在使用DynamicProxyBuilder时需要注意两点:
1、 被代理类的public方法必须都是virtual的,否则无法继承。
2、 在建立一个类时必须用Wrap,而不能直接用new关键字。
总结
Dynamic Proxy技术也是AOP(Aspect Oriented Programming)技术的核心。通过动态代理实现的AOP,一般可分为两个部分。第一部分是动态代理的实现,在这部分不同的语言在实现上有很大的不同。由于C#没有提供象java那样的动态代理机制,因此这些工作必须由我们自己来做,以上所述便是用C#实现动态代理的全部过程。第二部分就是利用这个动态代理机制来控制方法的执行。由上述可知,在这个方法拦截器中有一个用于改变方法调用轨迹的类Interceptor,当调用被代理类方法时,先调用这个类中的Call方法,然后在Call方法中加入适当代码来决定是否执行这个被代理类的方法。我们可以更进一步地去想,如果在Call方法中加入一些和AOP相关的代码,例如在满足一定条件下,在方法执行之前或执行之后插入一些代码,或者干脆不让这个方法执行。这样就成为一个真正的AOP framework了。
AOP是动态代理的最具代表性的应用。它在程序设计中的应用十分广泛。在一般的系统中,可分为两个部分,核心功能和系统功能。所谓核心功能就是和这个系统相关的业务功能,如在mail服务器中的核心功能是接收和发送电子邮件。系统功能则可以看成是系统的可选功能,如日志、安全等。如果没有了这些功能,mail服务器仍可以照常工作,只是不太安全了,并且无法查找以往的记录。实现这些系统功能,一般的作法是将系统功能的代码和核心功能的代码混在一起。这样做不仅加大了系统设计和实现的难度,而且使设计系统核心功能的程序必须要考虑这些系统的功能,分散了他们的注意力。然而,利用AOP却可解决以上问题,从而达到分离核心功能和系统功能的目的。
除了AOP,动态代理还有许多其它的用途。比如我们要开发一个远程服务的程序。在客户端要执行服务端的一些方法。一般的作法是利用.net remoting或直接用socket进行通讯,告诉服务器要调用的方法,然后服务器调用方法后通过.net remoting或socket将执行结果返回。这样做从表面上看没什么问题。但是对于某些方法的调用,并不要求结果的实时变化,也就是说当服务器上的数据变化了,并不要求通过这个方法得到的数据马上变化,可以有一段时间的延迟。如果是这样的话,那么每次都让服务器重新执行这个方法好象有点得不偿失。由此我们自然想到了Cache(缓存)。当要被缓存的方法在第一次调用后,将其结果存入Cache。当以后再调用这个方法时,可以直接从Cache中取结果,而无需每次都执行这个方法。但这样还有一个问题,如果我们无法修改这个服务器的代码,那么如何加入有关Cache的代码呢?即使能修改代码,那么我们是否能当不需要这个Cache时很容易地将其关闭呢?如果这个服务器使用了动态代理或者直接使用了利用动态代理技术实现的AOP,这么这些问题简直就是小菜一碟。即使你不了解服务器的源代码甚至是没有源代码,你仍可以利用方法拦截来实现这个Cache功能。
当然,动态代理除了上述的应用外,还可用在其它许多地方。如果你充分发挥自己的想象力,利用这种技术还可以做出许多应用非常广泛的程序。