Spring.Net学习系列一: 统一异常处理

在实际项目中,日志处理是一项常见的功能,如何统一处理异常?

一般的做法是会写个LogProvider类或ILogProvider接口,然后把错误信息通过这个provider写入文件或者数据库等等。

如:

try
 {
     helloWorld.Show();
 }
catch (Exception ex)
 { 
     LogProvider.Write(ex.StackTrace);
     MessageBox.show(
" 发生错误 " );
 }

 

 

这样会到处充斥着LogProvider.Write(ex.StackTrace);
这样类似的代码,是不是闻到了坏味道?

是的,我们需要想办法放在一个统一的地方,一是代码美观,二是也便于维护。

这个时候我们的Spring.net出马了。利用Spring的AOP,,通过代理,我们可以很方便的统一处理这些异常信息。

当然,异常的记录一般是通过日志模块来处理。所以我们先实现一个日志模块,这里用大名鼎鼎的Log4Net,

稍作封装后,代码如下:

 

 

代码
 1  using  System;
 2  using  System.Collections.Generic;
 3  using  System.Text;
 4 
 5  namespace  Log
 6  {
 7      public   class  Log
 8      {
 9          private   static  Log instance;
10          private   static   readonly  log4net.ILog log  =  log4net.LogManager.GetLogger( " AppLog " );
11          private   static   object  obj = new   object  ();
12          private  Log()
13         {
14 
15         }
16          ///   <summary>
17          ///  获取Log单例
18          ///   </summary>
19          ///   <returns></returns>
20          public   static  Log GetInstance()
21         {
22              if  (instance  ==   null )
23             {
24                  lock  (obj)
25                 {
26                     instance  =   new  Log();
27                     log4net.Config.XmlConfigurator.Configure();
28                 }
29             }
30              return  instance;
31         }
32 
33          public   void  Debug( object  o)
34         {
35              if  (log.IsDebugEnabled)
36             {
37                  if  (o  is  Exception) log.Debug( " Debug " , o  as  Exception);
38                  else  log.Debug(o.ToString());
39              
40             }
41         }
42 
43          public   void  Error( object  o)
44         {
45              if  (log.IsErrorEnabled)
46             {
47                  if  (o  is  Exception) log.Error( " Error " , o  as  Exception);
48                  else  log.Error(o.ToString());
49             }
50         }
51 
52          public   void  Warn( object  o)
53         {
54              if  (log.IsWarnEnabled)
55             {
56                  if  (o  is  Exception) log.Warn( " Warn " , o  as  Exception);
57                  else  log.Warn(o.ToString());
58             }
59         }
60 
61          public   void  Info( object  o)
62         {
63              if  (log.IsInfoEnabled)
64             {
65                  if  (o  is  Exception) log.Info( " Info " , o  as  Exception);
66                  else  log.Info(o.ToString());
67             }
68         }
69 
70          public   void  Fatal( object  o)
71         {
72              if  (log.IsFatalEnabled)
73             {
74                  if  (o  is  Exception) log.Fatal( " Fatal " , o  as  Exception);
75                  else  log.Fatal(o.ToString());
76             }
77         }
78      }
79  }
80 

 

接下来我们通过 Spring.Net 的 AOP 特性实现异常的统一处理,如果我们需要在异常发生时做一些操作的话我们就必须实现 Spring.Aop.IThrowsAdvice,该接口没有任何实现方法,是一个空接口,它仅仅做为一个标记接口而存在,但实现了 IThrowsAdvice 接口的类必须定义至少一个 AfterThrowing 方法,方法的签名如下:

         AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass);

         其中中括号括起来的前三个参数是可选的,返回值可以是任意数据类型。Spring.Aop.Framework.Adapter.ThrowsAdviceInterceptor 类实现对实现了 Spring.Aop.IThrowsAdvice 派生类中的方法依赖注入,其中的 ThrowsAdviceInterceptor() 方法检查 Spring.Aop.IThrowsAdvice 的派生类是否定义了至少一个异常处理方法,如果没有则抛出 ArgumentException 异常,MapAllExceptionHandlingMethods() 方法则在定义好的重载方法中查找出异常类型与最后一个参数所定义的类型中最接近的方法,而且我们不应该在其中实现了两个相同异常类型的方法,即使他们的参数数目不同,否则也将抛出 ArgumentException 异常。

 

代码
 1  using  System;
 2  using  System.Collections.Generic;
 3  using  System.Text;
 4  using  Spring.Aop;
 5  using  System.Reflection;
 6  namespace  Log
 7  {
 8       public   class  ExceptionThrowAdvice : IThrowsAdvice
 9      {
10          private  Log logInstance;
11        
12          public  ExceptionThrowAdvice()
13         {
14             logInstance  =  Log.GetInstance();
15         }
16 
17          public   void  AfterThrowing(MethodInfo method, Object[] args, Object target, Exception exception)
18         {
19             logInstance.Error(exception);
20         }
21      }
22  }
23 

 

接下来我们开始测试效果

首先定义一个接口以及实现类

如果使用spring.net+Nhibernate,这里就是Service和IService了(呵呵比较熟悉了吧)

using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  LogTest
{
   
public   interface  IHelloWorld
    {
       
void  Show();
    }
}

 

 

代码
using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  LogTest
{
   
public   class  HelloWorld:IHelloWorld
    {
        
#region  IHelloWorld 成员

        
public   void  Show()
        {
            
throw   new  Exception( " 发生异常 " );
        }

        
#endregion
    }
}

 

然后就是配置文件了

objects.xml

代码
<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< objects xmlns = " http://www.springframework.net "  xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance "
         xsi:schemaLocation
= " http://www.springframework.net 
         http: // www.springframework.net/xsd/spring-objects.xsd">

  
< object  id = " ExceptionThrowAdvice "  type = " Log.ExceptionThrowAdvice, Log "   />
  
< object  id = " HelloWorld "  type = " LogTest.HelloWorld, LogTest "   />

  
< object  id = " HelloWorldProxy "  type = " Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop "   >
    
< property name = " ProxyInterfaces " >
      
< list >
        
< value > LogTest.IHelloWorld,LogTest </ value >
      
</ list >
    
</ property >
    
< property name = " Target " >
      
< ref   object = " HelloWorld "   />
    
</ property >
    
< property name = " InterceptorNames " >
      
< list >
        
< value > ExceptionThrowAdvice </ value >
      
</ list >
    
</ property >
  
</ object >

</ objects >

 

app.config

 

代码
<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< 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 >
    
< section name = " log4net "  type = " log4net.Config.Log4NetConfigurationSectionHandler,log4net "   />
  
</ configSections >

  
<!-- spring配置 -->
  
< spring >
    
< context >
      
< resource uri = " ~/objects.xml " />
    
</ context >
  
</ spring >

  
<!--  Log输出定义  -->
  
< log4net >
    
< appender name = " LogFileAppender "  type = " log4net.Appender.FileAppender "   >
      
< param name = " File "  value = " log.text "   />
      
< param name = " datePattern "  value = " MM-dd HH:mm "   />
      
< param name = " AppendToFile "  value = " true "   />
      
< layout type = " log4net.Layout.PatternLayout " >
        
< param name = " ConversionPattern "  value = " %d [%t] %-5p %c [%x] - %m%n "   />
      
</ layout >
    
</ appender >
    
< appender name = " AppLogAppender "  type = " log4net.Appender.FileAppender "   >
      
< param name = " File "  value = " AppLog.text "   />
      
< param name = " datePattern "  value = " MM-dd HH:mm "   />
      
< param name = " AppendToFile "  value = " true "   />
      
< layout type = " log4net.Layout.PatternLayout " >
        
< param name = " ConversionPattern "  value = " %d [%t] %-5p %c [%x] - %m%n "   />
      
</ layout >
    
</ appender >
    
< logger name = " Spring " >
      
< level value = " INFO "   />
      
< appender - ref   ref = " LogFileAppender "   />
    
</ logger >
    
< logger name = " NHibernate " >
      
< level value = " INFO "   />
      
< appender - ref   ref = " LogFileAppender "   />
    
</ logger >
    
< logger name = " AppLog " >
      
< level value = " ALL "   />
      
< appender - ref   ref = " AppLogAppender "   />
    
</ logger >
  
</ log4net >
</ configuration >

 

 

然后在我们的启动程序中

 

代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;
using  Spring.Context;
using  Spring.Context.Support;
using  Log;

namespace  LogTest
{
    
public   partial   class  Form1 : Form
    {
        
private  IApplicationContext _ctx;
        
private  IHelloWorld _helloWorld;
        
public  Form1()
        {
            InitializeComponent();
            
try
            {
                helloWorld.Show();
            }
            
catch
            {
                MessageBox.Show(
" 发生异常 " );
            }
        }
        
private  IApplicationContext ctx
        {
            
get
            {
                
if  (_ctx  ==   null )
                {
                    _ctx 
=  ContextRegistry.GetContext();
                }
                
return  _ctx;
            }
        }
        
private  IHelloWorld helloWorld
        {
            
get
            {
                
if  (_helloWorld  ==   null )
                {
                    _helloWorld 
=  ctx[ " HelloWorldProxy " as  IHelloWorld;
                }
                
return  _helloWorld;
            }
        }
    }
}

 

 

。ok大功告成了,看到了吧,代码不记录异常信息时的代码一样,但实现了异常的统计记录,你可以看到在日志文件中已经写入了错误信息。

示例源码点击下载

 

 

 [下面引用自《Spring 技术手册》]
 注意到当异常发生时, Throw Advice 的任务只是执行对应的方法,您并不能在 Throw Advice 中将异常处理掉,在 Throw Advice 执行完毕后,原告的异常仍将传播至应用程序之中, Throw Advice 并不介入应用程序的异常处理,异常处理仍旧是应用程序本身所要负责的,如果想要在 Throw Advice 处理时中止应用程序的处理流程,作法是抛出其它的异常。

 

 

 

 

运行效果文字描述如下:

程序弹出对话框 “发生异常”

在AppLog.text文件中记录错误信息:

2009-12-13 16:34:17,812 [11] ERROR AppLog [(null)] - Error
System.Exception: 发生异常
   在 LogTest.HelloWorld.Show() 位置 F:\Study\LogAop\LogTest\HelloWorld.cs:行号 13
   在 _dynamic_LogTest.HelloWorld.Show(Object , Object[] )
   在 Spring.Reflection.Dynamic.SafeMethod.Invoke(Object target, Object[] arguments)
   在 Spring.Aop.Framework.DynamicMethodInvocation.InvokeJoinpoint()
   在 Spring.Aop.Framework.AbstractMethodInvocation.Proceed()
   在 Spring.Aop.Framework.Adapter.ThrowsAdviceInterceptor.Invoke(IMethodInvocation invocation)

 

你可能感兴趣的:(spring)