用.Net的动态代码生成功能实现AOP

随着AOP的概念被越来越多的讨论,动态代码生成技术也正受到更多人的重视。动态代码生成可以分为动态生成源代码、动态生成中间代码、动态生成机器码等几个层次。动态生成源代码最为简单,各种WEB开发环境都可以理解为这一层次,例如,用ASP很容易写出如下代码:

< % Response.Write  " <script>alert('hello world');</script> "  % >

而动态生成中间代码或机器代码则要复杂得多,一般来说很难人工完成,大多数情况都有借助专门的库或工具来完成。ATL中有个小thunk的东东,就是这种想法的一个应用:它通过动态生成机器码完成了窗口消息处理之前从HWND到窗口类指针的转换,从而免去了从长长的列表中查找指定的类的过程(参见《深入解析ATL》中文版第410页)。而在JAVA和.Net这样的托管环境下,类似于C++的直接向内存中写机器码的方法变得不怎么现实,取而代之的是使用运行环境本身为我们提供的动态代码生成的方法。
.Net提供了Emit和CodeDOM两种方式用于动态生成代码,前一种方式用于生成可以即时执行、即时保存成二进制文件的机器码,后一种方式更多的适合于生成人能读懂的源代码。我在这里主要使用了Emit方式。
我的目标是这样的:仿照Spring中AOP的思想,通过动生成类代码,实现指定类的动态代理,从而可以对指定的方法实行运行时的拦截。例如:

     public   interface  MyInterface
    
{
        
void Say(String name);
    }


    
public   class  MyClass: MyInterface
    
{
        
public void Say(String name)
        
{
            Console.WriteLine(
"大家好,我是" + name);
        }

    }

类MyClass实现了接口MyInterface,在其方法Say中向屏幕输出一字符串。下面的测试程序很容易验证程序在正常工作:

    MyInterface inst  =   new  MyClass();
    inst.Say(
" Billy " );

我现在要做的是,在调用方法Say之前和之后做一些事,也就是说要拦截方法Say的调用,并且尽可能少的修改用户代码,从而,在用户看来,他的类和接口的定义是不变的,他仍然是调用MyInterface的一个实例inst上的方法,但在调用的时候我已经可以有所察觉。简单的思想就是用户调用Say的时候实际上调用的是另一个方法,而在这个方法中再去调用真正的MyClass::Say()。
若要让用户仍在MyInterface这个接口下声明变量,并且调用的是另一个方法,能完成此工作的方式就是使用多态,也就是方法的晚绑定:

     public   class  MyClass2: MyClass  { ... }

但这样做会遇到一个在JAVA世界中不存在的问题,就是除非声明了virtual,否则C#的函数是早绑定的,在子类中不能重写没有被声明为virtual的方法。而之前我说过,我要尽量少的让用户修改代码,特别是类和接口声明是不要修改的,所以这里我决定让我的类不继承于MyClass,而是让它直接实现MyInterface接口,从而它和MyClass类成为了兄弟:

     public   class  MyClass2: MyInterface
    
{
        
private MyClass c = new MyClass();
        
public void Say(String name)
        
{
            
//Do something
            c.Say(name);
            
//Do something
        }

    }

我要实现的最终结果是这样的,但问题很明显,这段代码谁来写?总不能让用户再为每个想使用AOP的类都写这样的代码吧。使用工具来自动生成源代码?也不是个好主意。那么最好的方式就是在运行时根据接口MyInterface来动态产生这样一个代理类,并且提供一个工厂,当用户想要生成新的MyClass对象时,我的程序来接管这部分工作,我先生成MyClass和MyClass2两个类的实例c1和c2,并且把c1作为c2的一个成员变量;动态为MyClass2类生成所有的MyInterface接口方法的实现,在实现中都是调用c1的相应的方法,最后把c2以接口MyInterface的身份返回给用户,用户代码变成了这样:

    MyInterface inst  =  DynamicProxyBuilder.GetProxyObject < MyInterface, MyClass > ();
    inst.Say(
" Billy " );

这里我使用了C# 2.0提供的泛型功能,从而免去了返回对象的类型转换,当然如果使用C# 1.x,只要稍做改动就可以了。
DynamicProxyBuilder是我专门用来生成动态代理的类,GetProxyObject是它唯一的公共方法:

         public   static  InterfaceType GetProxyObject < InterfaceType, BaseType > ()
        
{
            Type interfaceType 
= typeof(InterfaceType);
            Type baseType 
= typeof(BaseType);

            
//取得代理类的动态类型,如果不存在,就创建它
            Type targetType = GetProxyType(interfaceType, baseType);

            
//创建代码对象和被代理对象的实例,并且返回代理对象
            return (InterfaceType)Activator.CreateInstance(targetType, Activator.CreateInstance(baseType));
        }


其功能是返回一个实现了接口InterfaceType的能代理BaseType类型对象的对象,而这个对象类型,就是前面所说的MyClass2类型。这个MyClass2类是在编译阶段不存在的,它在第一次被请求时动态生成,生成的方式是调用DynamicProxyBuilder类的GetProxyType方法:

         private   static  Type GetProxyType(Type interfaceType, Type baseType)
        
{
            
// 这里省略了各种有效性检查

            
//动态类型的名称,也就相当于前面说的MyClass2
            String proxyClassName = "_dynamicproxy." + baseType.FullName + "Proxy";

            
if (!typeList.ContainsKey(proxyClassName)) //检查所请求类型是否已经生成了
            
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
【基本说明】 1、能够生成三层工厂模式操作的所有基本代码,简单的数据库操作。 2、生成的代码包括了 MODEL、BLL、Factory、IDAL、DAL、DBHelper、Config 生成的代码内有详细注释可提供参考。 3、提供数据库增、删、改、查、分页及其事务,并提供多种重载方式。 4、所有数据表必须有主键且主键是第一列,这个主要是为了保证获取记录和分页获取的统一性,其实可以取消这个规则。 6、建议新建App_Code文件夹将生成的C#代码放里面。见此文件夹直接拷贝到项目下既可以使用。 7、不保证所提供软件或程序的完整性和安全性。 8、请在使用前查毒 (这也是您使用其它网络资源所必须注意的) 。 9、《Coder(ASP.NET代码生成器)》需要.Net FrameWork2.0运行环境,基于SQL Server 2005使用。 10、如无法运行本软件,请下载并安装由微软公司提供的.Net FrameWork2.0系统. 11、如果您在使用过程中遇到程序问题或建议请于我联系我的Email是 mailto:liliang119007@163.com。 【生成单层代码】 输入数据库名(Server)登录名(Name)密码(Pwd),连接后选择库名(Database)表名(Tables) 之后单击'生成单层代码'新建App_Code文件夹将生成的C#代码(ASP.NET后台代码)放里面, 【生成三层工厂模式项目】 (1)B/S架构: 输入数据库名(Server)登录名(Name)密码(Pwd)连接数据库成功后直接点生成整个项目选择路径确定就好了。 (2)C/S架构: 输入数据库名(Server)登录名(Name)密码(Pwd)连接数据库成功后直接点生成整个项目选择路径确定, 生成项目后打开该项目解决方案将表示层删掉, 再单击vs的(文件→添加→新建项目→选择Windows应用程序),这样就生成C/S架构的程序了!
Spring AOP的Java实现有两种方式: 编译时增强和运行时增强。编译时增强是在编译阶段生成代理类,其中AspectJ是一个基于Java实现AOP框架,它有自己的编译器或织入器,需要使用AspectJ的编译器编译切面类才能执行。而运行时增强是在运行时动态生成代理类,其中JDK动态代理和CGLIB库是两种常用的实现方式。JDK动态代理只能代理接口,而CGLIB能代理接口和类,但不能代理被final修饰的类。JDK动态代理通过反射生成代理类,而CGLIB通过继承被代理类的方式生成代理类。Spring AOP使用了AspectJ的注解来定义切面、切入点、通知增强等,但没有使用AspectJ的代理类生成实现,所以不需要额外的AspectJ编译器或织入器。Spring AOP同时实现了通过JDK动态代理和CGLIB来生成代理类,当目标类实现了接口时,默认使用JDK动态代理,当目标类没有实现接口时,使用CGLIB。Spring AOP的切面类(用@Aspect注解标注的类)也是由IOC容器管理的Bean。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [理解Spring AOP实现与思想 案例代码](https://download.csdn.net/download/qq_37171817/12502814)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java自定义注解、Spring AOP、使用AOP实现和自定义注解实现日志记录](https://blog.csdn.net/suiyishiguang/article/details/126612592)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [java AOP实现方式及Spring AOP总结](https://blog.csdn.net/wenqiangluyao/article/details/126309757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值