AOP代理详解:从静态代理到动态代理(Emit实现)

0 下载量 68 浏览量 更新于2024-08-29 收藏 698KB PDF 举报
"本文主要介绍了AOP(面向方面编程)的概念、切面的定义以及为何需要使用AOP。文中通过实例展示了AOP如何简化代码维护,特别是针对日志记录等横切关注点的处理。文章进一步探讨了AOP的两种实现方式——静态代理和动态代理,并以静态代理为例进行了详细解释。" AOP(面向方面编程)是一种编程范式,旨在提高软件设计的模块化程度,通过分离关注点来解决系统中的横切问题。这些横切关注点,如事务管理、日志记录和缓存,通常会分散在多个类和方法中,导致代码冗余和耦合度增加。AOP的核心是AOP代理,它能够在运行时拦截方法调用并执行特定的切面逻辑。 切面是AOP中的核心概念,它代表与业务逻辑无关但又需贯穿整个系统的行为,如日志记录、权限检查等。将这类行为抽取为切面,可以使其独立于业务代码,实现更好的解耦。 使用AOP的主要原因是减少代码重复,提高可维护性。假设我们需要记录用户操作日志,如果没有AOP,我们需要在每个涉及用户操作的接口中添加日志记录代码。这不仅使代码变得臃肿,而且在后期调整或移除日志功能时非常不便。而采用AOP,可以通过定义切面,在系统层面统一处理日志记录,无需修改业务代码。 AOP的实现有两种主要方式:静态代理和动态代理。静态代理,如AspectJ,是在编译时就已经生成了代理类。这种方式需要手动编写代理类,虽然灵活性较低,但性能通常较好。动态代理,如Spring AOP,是在运行时动态生成代理对象,提供了更高的灵活性,无需预先知道被代理类的具体信息。 以静态代理为例,假设有一个业务类`Business`,包含一个`Test()`方法,我们需要在调用前后添加日志记录。为了实现切面逻辑,我们创建一个`BusinessProxy`类,该类持有`Business`的实例,并在其`Test()`方法中添加日志记录代码。这样,当我们通过`BusinessProxy`调用`Test()`时,实际上执行的是包含日志记录的代理方法,既保持了原业务类的纯洁性,又实现了所需的功能。 动态代理则更进一步,它在运行时根据需要创建代理对象。例如,Spring AOP使用Java的动态代理机制(Java.lang.reflect.Proxy)或字节码技术(如CGLIB)来生成代理对象。在Spring中,我们可以通过定义切面(Aspect)和通知(Advice)来指定何时以及如何插入切面逻辑,而Spring AOP会在适当的时候自动处理这些逻辑。 AOP提供了一种强大的工具,允许开发者以更加整洁和模块化的方式来处理系统中的横切关注点,降低了代码的复杂性和维护难度。无论是静态代理还是动态代理,它们都是为了实现相同的目标——解耦关注点,提高代码质量。理解并掌握AOP,对于编写高效、易于维护的软件系统至关重要。
2011-07-14 上传
纯手工打造Emit实现AOP private static void OverrideMethods(TypeBuilder tb, MethodInfo method) { if (!method.IsPublic|| !method.IsVirtual || IsObjectMethod(method)) return; Type[] paramTypes = GetParameterTypes(method); MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual; MethodBuilder mb = tb.DefineMethod(method.Name, attr, method.ReturnType, paramTypes); LocalBuilder result = null; ILGenerator il = mb.GetILGenerator(); bool is_void = method.ReturnType != typeof(void); if (is_void == false) result = il.DeclareLocal(method.ReturnType); object[] attrs = method.GetCustomAttributes(typeof(AspectAttribute), false); if (attrs != null) { //初始化所有当前方法用到的参数object[] CreateLocalParameterArr(il, paramTypes); //初始化AspectContext Type ctxType = typeof(AspectContext); ConstructorInfo info = ctxType.GetConstructor(Type.EmptyTypes); var ctx = il.DeclareLocal(ctxType); il.Emit(OpCodes.Newobj, info); il.Emit(OpCodes.Stloc, ctx); //给AspectContext的参数值属性ParameterArgs赋值 var propMethod = ctxType.GetMethod("set_ParameterArgs"); il.Emit(OpCodes.Ldloc, ctx); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, propMethod); int m = attrs.Length; LocalBuilder[] lbs = new LocalBuilder[m]; MethodInfo[] endInvokeMethods = new MethodInfo[m]; //初始化标记的横切对象,并调用横切对象的BeforeInvoke方法 for (int i = 0; i < m; i++) { var tmpType = attrs[i].GetType(); var aspect = il.DeclareLocal(tmpType); ConstructorInfo tmpInfo = tmpType.GetCon