不是架构的架构之五:业务层的实现与自动代理(补充)
在上一篇中,我们提到了通过spring.net的自动代理生成自动适应配置的业务层,也简单提到了自动代理实现的原理,但是,由于自动代理是一个比较复杂的机制,尤其对于没有架构设计经验的朋友来说,所以在今天的文章中,主要是分析spring.net的自动代理的原理,然后对我们实现的业务层做一些分析和总结。
我们再来回顾一下调用自动代理的代码:
public static T Get<T>(T instance) where T : class { var p = new ProxyFactory(instance); p.AddAdvice(new AroundAdvise()); return p.GetProxy() as T; }
在方法中接收了一个泛型参数:业务对象的实例,这个业务对象当然必须是class,返回一个经过spring.net框架包装后的自动代理对象,这个自动代理对象类型仍然是T,显然这个对象和传入的对象看起来是一致的。
首先创建了一个代理工厂,我们预先将需要注入到业务对象的代码写到AroundAdvise类中,这里将AroundAdvise类添加到代理工厂,通过代理工厂获取到的业务对象,实际上是返回了一个继承了业务对象的代理对象。见下图。
在图中,我们传入的业务对象是UserBiz,而返回的是经过自动代理后生成的随机名称的对象。
在spring.net内部实际上是通过Emit生成的IL代码。事实上,通过CodeDom也可以实现相类似的功能,几年前我曾经实现过一个基于CodeDom的数据持久框架,性能比目前大多数ORM性能都要好得多,但是由于工作原因,没有将其做完善就放下了。在.net 3.5框架中,我们还可以使用lambda表达式来实现类似的功能,通过lambda表达式性能和Emit相近,但是复杂性远低于Emit。而在.net4.0框架中,还可以使用动态语言ironpython来实现。
我们来看看一段业务层的代码。
public class UserBiz { public static UserBiz Instance { get; private set; } static UserBiz() { Instance=BizAutoWcfProxy.Get(new UserBiz()); } private UserBiz() { } public virtual User GetFirst(string user,string pwd) { using (var db = Db.Get()) { return User.Schema.QueryFirst(db, User.Schema.Name == user & User.Schema.Pwd == pwd); } } }
这是一个用户业务类的代码,业务对象不需要继承任何对象,也不需要实现任何接口,但是我们在编写业务对象的时候,仍然要遵循一些约定:
1.隐藏构造函数。使用private定义的无参构造函数,避免在不熟悉的程序员直接去new这个业务对象。表现层调用业务层一律通过业务对象.Instance来得到。
2.业务层方法需要都是virtual定义。由于自动代理的缘故,在自动代理对象中需要自动重新实现所有的业务方法,所以业务方法需要是可覆盖的。
3.方法传入传出参数必须是可序列化的。
4.在业务层中不允许方法重载。这个是由于我们的设计所限,我们昨天提到,通过wcf调用的时候,传给服务端的仅仅是方法名称,如果重载了方法,可能会导致调用错误。
使用这个业务方法:
var user = UserBiz.Instance.GetFirst(username, encodePwd);
使用完全透明,在开发表现层时,完全不需要考虑应用环境的问题。