之前介紹了Spring.Net的IOC基礎用法。在IOC的基礎上接着介紹AOP,AOP即面向切面編程,這次用Spring.Net搭建程序的日誌服務。包括方法日誌,性能日誌,異常日誌等。
首先Spring.Net公開了攔截器接口(也可叫環繞)“IMethodInterceptor”,基於該接口實現攔截器
業務異常攔截器
using System;
using AopAlliance.Intercept;
namespace MyService
{
///<summary NoteObject="Class">
/// [功能描述: 業務異常攔截器,一方面寫日誌,另一方面整個業務層的facade層不讓拋出非底層異常,即使拋出了其他異常,也轉換成業務異常]<br></br>
/// [創建者: 張聯珠]<br></br>
/// [創建時間: 2014-7-17]<br></br>
/// <說明>
///
/// </說明>
/// <修改記錄>
/// <修改時間></修改時間>
/// <修改內容>
///
/// </修改內容>
/// </修改記錄>
/// </summary>
public class BusinessExceptionAdvice : IMethodInterceptor
{
/// <summary>
/// 業務異常攔截調用方法
/// </summary>
/// <param name="invocation">方法信息</param>
/// <returns>返回值</returns>
public object Invoke(IMethodInvocation invocation)
{
try
{
return invocation.Proceed();
}
//可以按照異常類型來分別捕獲處理
catch (Exception ex)
{
//寫日誌
LogUtils.WriteExceptionLog(ex.Message, ex);
//拋出異常
throw ex;
}
}
}
}
性能日誌攔截器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using AopAlliance.Intercept;
namespace MyService
{
///<summary NoteObject="Class">
/// [功能描述: 性能攔截器,進行性能方面的日子書寫工作]<br></br>
/// [創建者: 張聯珠]<br></br>
/// [創建時間: 2014-7-17]<br></br>
/// <說明>
///
/// </說明>
/// <修改記錄>
/// <修改時間></修改時間>
/// <修改內容>
///
/// </修改內容>
/// </修改記錄>
/// </summary>
public class CapabilityAdvice : IMethodInterceptor
{
/// <summary>
/// 業務異常攔截調用方法
/// </summary>
/// <param name="invocation"></param>
/// <returns></returns>
public object Invoke(IMethodInvocation invocation)
{
Stopwatch watch = new Stopwatch();
watch.Start();
var result = invocation.Proceed();
//寫性能日誌
LogUtils.WriteCapabilityLog("執行類" + invocation.TargetType.Name + "的方法" + invocation.Method.Name +"耗時:" + watch.Elapsed.TotalSeconds.ToString());
return result;
}
}
}
方法日誌攔截器
using AopAlliance.Intercept;
using System.Text;
namespace MyService
{
///<summary NoteObject="Class">
/// [功能描述: 操作日誌攔截器,對容器對象的符合條件的方法都書寫方法輸入輸出日誌]<br></br>
/// [創建者: 張聯珠]<br></br>
/// [創建時間: 2014-7-17]<br></br>
/// <說明>
///
/// </說明>
/// <修改記錄>
/// <修改時間></修改時間>
/// <修改內容>
///
/// </修改內容>
/// </修改記錄>
/// </summary>
public class OperationLogAdvice : IMethodInterceptor
{
/// <summary>
/// 攔截方法
/// </summary>
/// <param name="invocation">方法實例</param>
/// <returns>方法返回值</returns>
public object Invoke(IMethodInvocation invocation)
{
//輸出類名,方法名
string methodString = string.Format("類名:{0}方法名:{1}", invocation.TargetType.Name, invocation.Method.Name);
StringBuilder sb = new StringBuilder();
sb.Append(methodString);
LogUtils.WriteOperationLog(methodString);
//有參數
if (invocation.Arguments != null && invocation.Arguments.Length > 0)
{
foreach (var arg in invocation.Arguments)
{
if (arg != null)
{
sb.Append("參數:" + arg.ToString());
}
else
{
sb.Append("參數:null");
}
}
LogUtils.WriteOperationLog(sb.ToString());
}
//執行方法
var result = invocation.Proceed();
//有結果的話,寫出結果到日誌
if (result != null)
{
sb.Append("結果:" + result.ToString());
}
LogUtils.WriteOperationLog(sb.ToString());
return result;
}
}
}
寫日誌工具類(基於Log4Net)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Logging;
using System.Text.RegularExpressions;
namespace MyService
{
///<summary NoteObject="Class">
/// [功能描述: 日誌輔助工具]<br></br>
/// [創建者: 張聯珠]<br></br>
/// [創建時間: 2013-9-17]<br></br>
/// <說明>
///
/// </說明>
/// <修改記錄>
/// <修改時間></修改時間>
/// <修改內容>
///
/// </修改內容>
/// </修改記錄>
/// </summary>
public static class LogUtils
{
#region 靜態成員
/// <summary>
/// 書寫異常日誌
/// </summary>
/// /// <param name="message">日誌內容</param>
/// <param name="exception">異常對象</param>
public static void WriteExceptionLog(string message, Exception exception)
{
LogManager.GetLogger("Exception").Error(message, exception);
}
/// <summary>
/// 書寫性能日誌
/// </summary>
/// <param name="message">日誌內容</param>
public static void WriteCapabilityLog(string message)
{
LogManager.GetLogger("Capability").Info(message);
}
/// <summary>
/// 書寫安全日誌
/// </summary>
/// <param name="message">日誌內容</param>
public static void WriteSecurityLog(string message)
{
LogManager.GetLogger("Security").Info(message);
}
/// <summary>
/// 書寫操作日誌
/// </summary>
/// <param name="message">日誌內容</param>
public static void WriteOperationLog(string message)
{
LogManager.GetLogger("Operation").Info(message);
}
/// <summary>
/// 書寫調試日誌
/// </summary>
/// <param name="message">日誌內容</param>
public static void WriteDebugLog(string message)
{
LogManager.GetLogger("Debug").Debug(message);
}
/// <summary>
/// 書寫調試日誌
/// </summary>
/// <param name="message">日誌內容</param>
public static void WriteSqlLog(string message)
{
LogManager.GetLogger("SqlLog").Debug(ReplaceSpaceToOne(message));
}
/// <summary>
/// 把多個空格替換成一個
/// </summary>
/// <param name="str">要處理的串</param>
/// <returns>結果</returns>
private static string ReplaceSpaceToOne(string str)
{
string resultString = string.Empty;
try
{
resultString = Regex.Replace(str, "\\s{2,}", " ");
return resultString;
}
catch
{
return str;
}
}
#endregion
}
}
然後修改app.config的配置(配置攔截器對象和日誌)
配置代碼
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="spring">
<!---此三項必須配置-->
<!--參數段-->
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
<!--運用程序上下文段-->
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<!--配置總節點段-->
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
<sectionGroup name="common">
<!--日誌段-->
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
</sectionGroup>
<!--日誌段-->
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<common>
<logging>
<!--日誌適配器-->
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net">
<arg key="configType" value="FILE-WATCH"/>
<arg key="configFile" value="~/Conf/Log4Net.xml"/>
</factoryAdapter>
</logging>
</common>
<spring>
<!--當前容器對象節點,最主要的容器對象配置在這裏-->
<context>
<resource uri="config://spring/objects"/>
<resource uri="~/Conf/AopConfig.xml"/>
</context>
<objects xmlns="http://www.springframework.net" default-autowire="byType">
<!--系統主數據庫基層配置-->
<!--狗實現類配置-->
<object id="objdog" type="MyService.DogService,MyService" singleton="false">
</object>
<!--鴨子實現類配置-->
<object id="objduck" type="MyService.DuckService,MyService" singleton="false">
</object>
<!--接口實現類配置-->
<object type="MyService.InterfaceService,MyService" singleton="false">
</object>
</objects>
</spring>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
攔截器和日誌詳細配置
後期可以通過修改這個配置來控制給哪些類增加攔截器(開發模式和上線模式)
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop">
<!--必須讓Spring.NET容器管理DefaultAdvisorAutoProxyCreator類-->
<object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/>
<!-- 操作方法日誌 -->
<object id="OperationLogAdvice" type="MyService.OperationLogAdvice, MyService" />
<!-- 操作方法日誌攔截 -->
<object type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
<property name="advice" ref="OperationLogAdvice"/>
<property name="patterns">
<list>
<value>.*Service</value>
<value>.*Facade</value>
<value>.*Dal</value>
<value>.*Table</value>
</list>
</property>
</object>
<!-- 異常處理日誌攔截 -->
<object id="BllExceptionAdvice" type="MyService.BusinessExceptionAdvice, MyService" />
<!-- 異常處理日誌攔截 -->
<object type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
<property name="advice" ref="BllExceptionAdvice"/>
<property name="patterns">
<list>
<value>.*Service</value>
<value>.*Facade</value>
<value>.*Dal</value>
<value>.*Table</value>
</list>
</property>
</object>
<!-- 性能日誌攔截 -->
<object id="CapabilityAdvice" type="MyService.CapabilityAdvice, MyService" />
<!-- 性能日誌攔截 -->
<object type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
<property name="advice" ref="CapabilityAdvice"/>
<property name="patterns">
<list>
<value>.*Service</value>
<value>.*Facade</value>
<value>.*Dal</value>
<value>.*Table</value>
</list>
</property>
</object>
</objects>
後面可以通過該配置來控制哪些日誌的輸出(開發模式和上線模式)
<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="false">
<appender name="ExceptionRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/異常日誌.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %15logger %appdomain %location -- %message %exception %newline" />
</layout>
</appender>
<appender name="DebugRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/調試日誌.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date -- %message %exception %newline" />
</layout>
</appender>
<appender name="OperationRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/方法日誌.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="SqlRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/Sql語句日誌.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="CapabilityRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/性能.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="SecurityRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="LockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<file value="Logs/安全.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG"/>
</root>
<logger name="Exception">
<appender-ref ref="ExceptionRollingLogFileAppender" />
</logger>
<logger name="Operation">
<appender-ref ref="OperationRollingLogFileAppender"/>
</logger>
<logger name="Debug">
<appender-ref ref="DebugRollingLogFileAppender" />
</logger>
<logger name="SqlLog">
<appender-ref ref="SqlRollingLogFileAppender" />
</logger>
<logger name="Capability">
<appender-ref ref="CapabilityRollingLogFileAppender"/>
</logger>
<logger name="Security">
<appender-ref ref="SecurityRollingLogFileAppender"/>
</logger>
</log4net>
以上實現之後程序不用修改就會給實現類對象加上攔截器實現的日誌服務,並且是可配置調整的,即所說的切面服務;可以實現統一的參數校驗、安全認證、日誌服務等
是不是就毫無侵入性的把切面服務加入程序裏了_
代碼可以到下載裏下載名稱“SpringAop練習”