此篇狗尾续狗,有炒现饭之嫌。但为了学习Spring.NET的AOP实现,同时也是响应前篇的读者,还是再选这个例子。这不,CCTV6又在放《失恋33天》,咱还写这个。
Advice(通知)
Spring.NET 使用标记接口 AopAlliance.Aop.IAdvice 来定义通知,这个接口又有四个直接的派生接口,还有两个间接地派生接口。
Spring.Aop.IAfterReturningAdvice,定义方法执行之后的通知,通知的方法名为 AfterReturning
Spring.Aop.IBeforeAdvice,定义所有的前置通知,还是一个标记接口
Spring.Aop.IMethodBeforeAdvice,方法的前置通知接口,通知的方法名称为 Before
AopAlliance.Intercept.IInterceptor, 环绕通知的接口,也是一个标记接口
AopAlliance.Intercept.IMethodInterceptor,方法的环绕通知接口,Invoke 是调用方法。
我们要用到的是IMethodInterceptor
class WorkThreadAdvice : IWorkThreadHandler, IMethodInterceptor
{
private IBlockDialog blockForm;
private bool reportProgress;
private BackgroundWorker worker;
private object returnValue;
public WorkThreadAdvice(bool reportProgress)
{
this.reportProgress = reportProgress;
}
#region Implementation of IWorkThreadHandler
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var args = e.Argument as IMethodInvocation;
returnValue = args.Proceed();
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
blockForm.ShowProcess(e.ProgressPercentage);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (blockForm != null)
{
blockForm.UnBlock();
}
}
public void ReportProgress(int percentage)
{
worker.ReportProgress(percentage);
}
#endregion
#region Implementation of IMethodInterceptor
public object Invoke(IMethodInvocation invocation)
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
if (reportProgress)
{
worker.WorkerReportsProgress = true;
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
}
worker.RunWorkerAsync(invocation);
blockForm = invocation.Target as IBlockDialog;
if (blockForm != null)
{
blockForm.Handler = this;
blockForm.Block();
}
while (worker.IsBusy)
{
Thread.Sleep(500);
}
return returnValue;
}
#endregion
}
Attribute
public class WorkThreadAttribute : AopAttributeBase
{
private readonly bool reportProgress;
public WorkThreadAttribute(bool reportProgress)
{
this.reportProgress = reportProgress;
}
public WorkThreadAttribute() : this(false)
{
}
public override IAdvice CreateAdvice()
{
return new WorkThreadAdvice(reportProgress);
}
}
UI:MVP模式
View接口:
public interface IArticleView
{
List<ArticleModel> Articles { set; }
}
Persenter接口:
Spring.NET被拦截的类不能像PIAB那样继承MarshalByRefObject,必须实现一个接口
public interface IArticlePresenter : IAsyncPresenter
{
void Download();
IArticleView View { get; set; }
}
View实现:WinForm
public partial class ArticleForm : BaseView, IArticleView
{
#region Constants and Fields
private readonly IArticlePresenter presenter;
#endregion
#region Constructors and Destructors
public ArticleForm()
{
this.InitializeComponent();
this.presenter = PresenterLoader.LoadPresenter<IArticlePresenter>();
this.presenter.View = this;
}
#endregion
#region Methods
protected override void OnShown(EventArgs e)
{
this.presenter.Download();
}
#endregion
#region Implementation of IArticleView
private List<ArticleModel> articles;
public List<ArticleModel> Articles
{
set;
}
#endregion
}
Presenter实现:
public class ArticlePresenter : AsyncPresenterBase,IArticlePresenter
{
private List<ArticleModel> articles;
#region Implementation of IAsyncPresenter
[WorkThread]
public void Download()
{
articles = ArticleModel.GetAll();
Thread.Sleep(3000);
}
public IArticleView View { get; set; }
protected override void Binding()
{
View.Articles = articles;
}
#endregion
}
Proxy(代理)
PIAB生成代理对象很方便:PolicyInjection.Create
Spring.NET没有如此简便的方法,但可以通过ProxyFactory来生成Proxy,同时加入自定义AOP相关的Attribute的逻辑即可
class PresenterLoader
{
public static T LoadPresenter<T>() where T : IAsyncPresenter
{
var context = ContextRegistry.GetContext();
var sourceObject = (T)context.GetObject(typeof(T).Name.Substring(1),typeof(T));
var factory = new ProxyFactory(sourceObject);
var sourceObjectType = sourceObject.GetType();
var methodInfos = sourceObjectType.GetMethods();
foreach (MethodInfo methodInfo in methodInfos)
{
var attributes = methodInfo.GetCustomAttributes(false);
foreach (object attribute in attributes)
{
if (attribute is AopAttributeBase)
{
var pointcutAttribute = attribute as AopAttributeBase;
var advice = pointcutAttribute.CreateAdvice();
var advisor = new AttributeMatchMethodPointcutAdvisor { Advice = advice, Attribute = attribute.GetType() };
factory.AddAdvisor(advisor);
}
}
}
var obj = (T)factory.GetProxy();
return obj;
}
}
Configuration(配置)
使用过Spring.NET IoC的都知道
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net" >
<object id="ArticlePresenter" type="WinFormPractice.Presenters.ArticlePresenter" />
</objects>
</spring>
</configuration>
好了,Spring.NET之AOP研究告一段落。项目中咱还是用的EntLib,马上准备升级EntLib5。不过,Spring.NET的IoC和配置还是很强大的。