动态代理类是实现在运行时指定的接口列表的类,这样通过类实例上的一个接口的方法调用将被编码并通过统一接口分派到另一个对象。它可用于为接口列表创建类型安全的代理对象,而无需预先生成代理类。动态代理类对于需要提供对象调用的类型安全反射调度的应用程序或库很有用提供接口 API
上图是很好的示例,但为什么我们使用动态代理?
有没有一个简单的例子可以在现实世界中使用,以获得更多的感知?
一个常见的用例是面向方面的编程,在其中您寻求跨多个组件应用通用功能,而不需要组件本身来实现功能。在这些情况下,您可以使用动态代理来包装所有具有附加行为的目标组件。这样做
几个例子:
诸如 Hibernate 和实体框架之类的 ORM 这样做是为了提供围绕代码优先设计的持久性实现。核心领域类是在不知道它们的持久性的情况下构建的,框架在启动时包装或扩展这些类以处理实际实现。
使用诸如日志记录或缓存等方面来包装接口的所有成员。例如,如果您想记录 ISomeInterface 上的每个方法调用,您可以编写一个动态代理来查找所有接口方法,调用带有方法详细信息的 Log 方法,然后将调用传递给实际实现。
想象一下,您有两个对象 Car 和 Motorboat,它们分别实现了接口 CanDrive 和 CanFloat。现在,您想要第三个对象来实现这两个接口并重用 Car 和 Motorboat 的逻辑。在 Groovy、Ruby 和 Scala 等语言中,您可以使用 mixin 来解决这个问题。然而,在 Java 中,没有这样的东西。您当然可以使用例如适配器设计模式,但在许多情况下(尤其是在构建框架时)动态代理很有用。考虑使用 cglib 库的示例:
CanDrive car = new Car();
CanFloat motorboat = new Motorboat();
net.sf.cglib.proxy.Mixin amphibian = net.sf.cglib.proxy.Mixin.create(new Object[] { car, motorboat });
TestCase.assertEquals("bzzz bzzz bzzz ...", ((CanFloat) amphibian)._float());
TestCase.assertEquals("pyr pyr pyr pyr ...", ((CanDrive) amphibian).drive());
此链接在代码中描述了动态代理:
public static class DynamicProxyGenerator
{
public static T GetInstanceFor<T>()
{
Type typeOfT = typeof(T);
var methodInfos = typeOfT.GetMethods();
AssemblyName assName = new AssemblyName("testAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assBuilder.DefineDynamicModule("testModule", "test.dll");
var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + "Proxy", TypeAttributes.Public);
typeBuilder.AddInterfaceImplementation(typeOfT);
var ctorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { });
var ilGenerator = ctorBuilder.GetILGenerator();
ilGenerator.EmitWriteLine("Creating Proxy instance");
ilGenerator.Emit(OpCodes.Ret);
foreach (var methodInfo in methodInfos)
{
var methodBuilder = typeBuilder.DefineMethod(
methodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType,
methodInfo.GetParameters().Select(p => p.GetType()).ToArray()
);
var methodILGen = methodBuilder.GetILGenerator();
if (methodInfo.ReturnType == typeof(void))
{
methodILGen.Emit(OpCodes.Ret);
}
else
{
if (methodInfo.ReturnType.IsValueType || methodInfo.ReturnType.IsEnum)
{
MethodInfo getMethod = typeof(Activator).GetMethod(/span>"CreateInstance",new Type[]{typeof((Type)});
LocalBuilder lb = methodILGen.DeclareLocal(methodInfo.ReturnType);
methodILGen.Emit(OpCodes.Ldtoken, lb.LocalType);
methodILGen.Emit(OpCodes.Call, typeofype).GetMethod("GetTypeFromHandle")); ));
methodILGen.Emit(OpCodes.Callvirt, getMethod);
methodILGen.Emit(OpCodes.Unbox_Any, lb.LocalType);
}
else
{
methodILGen.Emit(OpCodes.Ldnull);
}
methodILGen.Emit(OpCodes.Ret);
}
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
}
Type constructedType = typeBuilder.CreateType();
var instance = Activator.CreateInstance(constructedType);
return (T)instance;
}
}