运用Unity实现AOP拦截器[结合异常记录实例]

 

 

本篇文章将通过Unity实现Aop异常记录功能;有关Unity依赖注入可以看前两篇文章:

1:运用Unity实现依赖注入[结合简单三层实例] 

2:运用Unity实现依赖注入[有参构造注入] 

另早期写过一个利用Spring.net实现相同的功能:spring.net结合普通三层(实现IOC 及AOP中的异常记录功能)

 

一:理论知识

AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。   OOP是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系;AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。   AOP是使用切面(aspect)将横切关注点模块化,OOP是使用类将状态和行为模块化。在OOP的世界中,程序都是通过类和接口组织的,使用它们实现程序的核心业务逻辑是十分合适。但是对于实现横切关注点(跨越应用程序多个模块的功能需求)则十分吃力,比如日志记录,验证等

Unity默认提供了三种拦截器:TransparentProxyInterceptor、InterfaceInterceptor、VirtualMethodInterceptor。

TransparentProxyInterceptor:代理实现基于.NET Remoting技术,它可拦截对象的所有函数。缺点是被拦截类型必须派生于MarshalByRefObject。

InterfaceInterceptor:只能对一个接口做拦截,好处时只要目标类型实现了指定接口就可以拦截。

VirtualMethodInterceptor:对virtual函数进行拦截。缺点是如果被拦截类型没有virtual函数则无法拦截,这个时候如果类型实现了某个特定接口可以改用

InterfaceInterceptor。

 

二:实例介绍

本实例是通过Unity实现异常的统一记录,解决以前在解决方法不同地方出现重复异常记录情况;

图中同样是基于我们前两篇文章讲解依赖注入时的分层结构;增加对log4net.dll引用来记录异常的内容;

若要了解分层情况看前一篇运用Unity实现依赖注入[结合简单三层实例],本文侧重讲解AOP的实现;

 

三:实例编码

1:首先了解公共助手层Command;其中Log4netFile加载log4net配置信息,特别要注意assembly

using log4net;
using log4net.Core;
using System.Reflection;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace Command
{
    public  class Log4NetFile
    {
        private ILog logger;
        public Log4NetFile()
        {
            logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
            log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo("Log4Net.config"));
        }

        public void Log(string message)
        {
            logger.Warn(message);
        }
       
        public void Log(Exception ex)
        {
            logger.Warn("异常的内容:", ex);
        }
    }
}

而ExceptionLogBehavior类是我们异常拦截器,也是本篇的重点,其中我们继承IInterceptionBehavior接口,不仅可以查看传入的参数,还可以对它进行统一的异常处理;getNext()(input, getNext)就是重调运行;

using Microsoft.Practices.Unity.InterceptionExtension;
namespace Command
{
    public class ExceptionLogBehavior:IInterceptionBehavior
    {
        /// <summary>
        /// 获取当前行为需要拦截的对象类型接口。
        /// </summary>
        /// <returns>所有需要拦截的对象类型接口。</returns>
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }
        /// <summary>
        /// 通过实现此方法来拦截调用并执行所需的拦截行为。
        /// </summary>
        /// <param name="input">调用拦截目标时的输入信息。</param>
        /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param>
        /// <returns>从拦截目标获得的返回信息。</returns>
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            Console.WriteLine("执行前");
            IMethodReturn retvalue = getNext()(input, getNext);
           
            #region 参数部分
            Console.WriteLine("-------------参数内容---------------");
            for (int i = 0; i < input.Arguments.Count; i++)
            {
                var parameter = input.Arguments[i];
                Console.WriteLine(string.Format("第{0}个参数值为:{1}", i+1, parameter.ToString()));
            }
            Console.WriteLine("------------------------------------");
            #endregion

            #region 异常处理部分
            if (retvalue.Exception == null)
            {
                Console.WriteLine("执行成功,无异常");
            }
            else
            {
                //记录异常的内容 比如Log4Net等
                new Log4NetFile().Log("异常的内容为:" + retvalue.Exception.Message);
                //retvalue.Exception设为null表示异常已经被处理过了
                retvalue.Exception = null;
            }
            #endregion
            Console.WriteLine("完成");
            return retvalue; 
        }

        /// <summary>
        /// 获取一个<see cref="Boolean"/>值,该值表示当前拦截行为被调用时,是否真的需要执行
        /// 某些操作。
        /// </summary>
        public bool WillExecute
        {
            get { return true; }
        }
    }

2:逻辑层我们故意让它抛出异常内容

using IAopDAL;
using IAopBLL;
using Command;
namespace AopBLL
{
    public class ReadDataBLL:IReadDataBLL
    {
        IReadData bllServer = new UnityContainerHelp().GetServer<IReadData>();
        public string ReadDataStr(string Name)
        {
            throw new Exception("BLL出现异常");
        }
    }
}

3:主程序层(Aopunity)主要关注配置信息

a:首先是Log4Net.config,每天生成一个.txt文件记录异常内容

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
  </configSections>
  <log4net>
    <appender name="RollingLogRootFileAppender" type="log4net.Appender.RollingFileAppender">
      <!--日志的路径-->
      <file value=".\Log\WanLog" />
      <!--是否覆盖,默认是追加true-->
      <appendToFile value="true"/>
      <!--文件滚动周期(每日创建新日志文件)-->
      <datePattern value="yyyyMMdd&quot;.txt&quot;"/>
      <!--设置无限备份=-1 ,最大备份数为1000-->
      <maxSizeRollBackups value="1000"/>
      <!--名称是否可以更改为false为可以更改-->
      <staticLogFileName value="false" />
      <!--文件滚动选项Composite表示根据日期和大小来滚动-->
      <rollingStyle value="Composite" />
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss}[%t][%-5p][%c]%m%n%exception%n" />
      </layout>
    </appender>
    <root>
      <level value="All" />
      <appender-ref ref="RollingLogRootFileAppender" />
    </root>
  </log4net>
</configuration>

b:App.config则是一些依赖注入跟Aop的配置,增加sectionExtension节点引用;interceptor则表示拦截器的类型,interceptionBehavior对应处理程序,lifetime生命周期

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practces/2010/unity">
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, 

Microsoft.Practices.Unity.Interception.Configuration"/>
    <typeAliases>
      <typeAlias alias="transient" type="Microsoft.Practices.Unity.TransientLifetimeManager, Microsoft.Practices.Unity"/>
    </typeAliases>
    <alias alias="ExceptionLogBehaviorName" type="Command.ExceptionLogBehavior,Command"></alias>
    <container name="FirstClass">
      <extension type="Interception"/>
      <register type="IAopBLL.IReadDataBLL,IAopBLL" mapTo="AopBLL.ReadDataBLL,AopBLL">
        <interceptor type="InterfaceInterceptor"/>
        <interceptionBehavior type="ExceptionLogBehaviorName"/>
        <lifetime type="transient"></lifetime>
      </register>
      <register type="IAopBLL.IPropertyBLL,IAopBLL" mapTo="AopBLL.PropertyBLL,AopBLL"></register>
      <register type="IAopDAL.IReadData,IAopDAL" mapTo="AopOracelDAL.ReadDataDAL,AopOracelDAL"/>
    </container>
  </unity> 
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>

c:主程序运行代码如下:

using IAopBLL;
using Command;
namespace AopUnity
{
    class Program
    {
        static void Main(string[] args)
        {
            IReadDataBLL bllServer = new UnityContainerHelp().GetServer<IReadDataBLL>();
            Console.WriteLine(bllServer.ReadDataStr("踏浪帅"));
        }
    }
}

 

四:运行效果

 

五:注意事项

1:主程序没有对Microsoft.Practices.Unity.Interception.Configuration.dll进行引用报错

创建 unity 的配置节处理程序时出错: 给定程序集名称或基本代码无效。 (异常来自 HRESULT:0x80131047)

<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, 
Microsoft.Practices.Unity.Interception.Configuration"/>

解决方法:主要是我们配置文件中有,则要对它进行引用

2:因为我们把log4net配置文件单独出来,所以要设置其为始终复制

 

 

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮,若有不足欢迎指正。  因为,我的写作热情也离不开您的肯定支持。
 
感谢您的阅读(源代码下载

posted @ 2013-09-15 22:06  踏浪帅  阅读(4135)  评论(5编辑  收藏  举报