text:
Sample Asp.NET MVC AJAX với JQueryPosted On Tuesday, September 30, 2008 at at 5:13 PM by Van Nguyen
- Theo tin trên blog của bác ScottGu thì JQuery trong tương lai sẽ được ship cùng với .NET IDE của Microsoft. Phiên bản MVC Preview5 cũng mới được cho ra lò. Bản thân tui trước giờ vẫn thắc mắc là khi sử dụng MVC thì sẽ viết web Ajax như thế nào. Sau một lúc gu gồ, tui tìm thấy 1 link khá hay của bác Phil Haack. Vì nội blog entry đó chỉ giới thiệu 1 video podcast và không có code mẫu nên tui quyết định làm theo hướng dẫn và viết một entry cuối cùng trong tháng 9 thân yêu .
- Trong bài viết này, tui sẽ làm y chang hướng dẫn của bác Phil Haack. Như chúng ta biết, thì AJAX là chữ viết tắt của “Asynchronous JavaScript and XML”. Như vậy ta có thể làm một trang web có AJAX bằng cách kết hợp JavaScript với các WebService. Một trong những mục đích của AJAX là tránh postback về phía server khi thực hiện các thao tác đơn giản. Trường hợp điển hình nhất là những Quick Reply của các forum. Khi bạn trả lời một bài viết nào đó vào ô text box Quick Reply rồi click vào nút gửi đi, nội dung bạn đánh vào sẽ được lưu vào database, phần HTML hiển thị reply của bạn sẽ hiện ra ngay sau bài viết mới nhất và những gì bạn đánh vào text box sẽ bị xóa đi. Đó làm một chức năng AJAX điển hình mà ta có thể code bằng tay được.
- Nếu là tui, tui sẽ implement một webservice để nhận request save xuống database nội dung bài post khi người ta reply. Kết tiếp viết một số hàm JavaScript đơn giản để hiển thị thêm phần nội dung HTML trả lời vào dưới cùng của bài viết và xóa trắng text box nếu cần thiết. Và thật sự đó là cách tui làm ở cty trước, khi implement chức năng reply post cho trang web www.cofieu.com.vn/news. Khi đó tui chưa sử dụng JQuery nên đã code hơi trâu bò một cách không cần thiết, trong khi những gì tui cần để select các DOM object trong HTML đã được JQuery hỗ trợ hết. Cái thật sự cần làm là WebService để thực hiện request từ phía client.
- Để tóm tắt, tui xin trình bày cách mà bác Phil Haack đã sử dụng. Trong video podcast của bác Phil không cần implement một WebService mới mà chỉ implement một hàm Action, trả về một JSON object. Và ta sẽ phải viết một số hàm JavaScript đơn giản, nhận data đó , kết hợp với JQuery để update HTML vào chỗ thích hợp. Ý tưởng là vậy, ta sẽ bắt tay vào làm demo đó như sau:
- Trước hết để thực hiện được demo này, các bạn đã install .NET Framework 3.5 SP1, VS.NET 2008 SP1, Asp.net MVC Preview 5 Codeplex. Nếu chưa có chịu khó gu gồ nhe .
- Trong tương lai thì các file js của thư viện JQuery sẽ được include sẵn mỗi khi mình tạo project web. Còn hiện nay MVC vẫn còn đang thử nghiệm nên chúng ta chịu khó download 2 file js ở các link sau về để include vào trong project:
Link 1: jquery-1.2.6.js
Link 2: jquery.color.js
- Sau khi đã install và download những thứ cần thiết, bạn hãy mở VS.NET 2008, tạo mới 1 project web ASP.NET MVC. Kéo 2 file js vừa download vào thư mục Content như hình:
- Mở file Site.Master để include hai file đó vào website. Các bạn có thể kéo thả cho lẹ
- Để thử cách viết Controller của MVC, ta hãy tạo mới một Controller khác và không đụng đến các Controller có sẵn khi tạo ra Project web. Như vậy ta sẽ tạo ra một trang mới là TestAjax/Main.aspx, và Controller tương ứng cho View này sẽ là TestAjaxController:
- Trong file Main.aspx, chúng ta sẽ thêm vào 2 span với id lần lượt là name và url để hiển thị thông tin nhận được khi request. Ta cũng sẽ tạo một button với sự kiện onClick ứng với hàm JavaScript ShowInformation() mà ta sẽ implement sau. Còn bây giờ hãy xem đại khái HTML của file Main.aspx:
- Như vậy hàm JavaScript ShowInformation sẽ thực hiện việc Request lên server, sau đó nhận data từ server trả về và cập nhận html cho 2 thẻ span phía trên. Nội dung của hàm JavaScript như sau:
- Giải thích: Hàm ShowInformation sẽ request đến address “TestAjax/GetInformation để lấy dữ liệu. Request này sẽ được thực thi bởi hàm GetInformation trong class TestAjaxController . Theo đúng convention của ASP.NET MVC:
{
return Json( new { Message1 = "Nguyen Thoai",
}
- Điều đáng lưu ý là function này là nó tạo ra 1 Annonymouse Object và wrap trong một JSonResult Type để trả về cho client. Như ta thấy trong hình thì Anonymouse Object này sẽ có 2 properties là Message1 và Message2 và 2 field này được sử dụng trong hàm JavaScript ở trên. (Anonymous Object là 1 feature mới của .NET Framework 3.x)
- Cuối cùng tui có thêm vào một hàm JavaScript để tạo hiệu ứng đẹp mặt như hướng dẫn trong video podcast. Tóm lại nếu mới tìm hiểu ASP.NET MVC và chưa biết nó support AJAX tới đâu nhưng ta chắc chắn rằng sẽ có cách để làm những hiệu ứng AJAX rất nhanh và dễ dàng nhờ vào JQuery.
- Các bạn có thể download video trên ở đây: http://www.aspnetpodcast.com/VideoFiles/ASPNETPodcast20080727-MVCWithPhilHaack.wmv
- Link bài viết gốc của tác giả: http://haacked.com/archive/2008/07/29/super-simple-mvc-ajax-with-jquery-demo.aspx
- Demo của tui http://nthoaiblog.googlepages.com/SampleMvcPreview5AjaxJQuery.zip
http://nthoaiblog.googlepages.com/SampleMVCRCAjaxJQuery.zip (MVC Release Candidate)
Sử dụng AOP với PostSharp FrameworkPosted On Saturday, September 20, 2008 at at 9:03 PM by Van Nguyen
- Spring.NET là một framework với nhiều chức năng trong đó có AOP. Nguyên tắc hoạt động của AOP trong Spring là gắn những “custom action” vào hệ thống lúc RunTime nhờ vào khả năng tùy biến rất cao của các file xml config. Có thể nói Spring.NET là framework hỗ trợ AOP mạnh nhất hiện nay. Ngoài Spring.NET còn có nhiều framework AOP khác cho .NET, trong đó mới nổi lên là PostSharp.
I. Giới thiệu PostSharp framework
- Trước hết PostSharp là một free, open-source platform để “enhance” .NET assemblies, có nghĩa là PostSharp sẽ biến đổi assemblies của bạn trong khi bạn build source code. Điều này hoàn toàn khác với AOP của Spring.NET. Spring sẽ thực hiện những biến đổi của bạn lúc runtime, còn PostSharp sẽ trực tiếp thay đổi kết quả build source code ngay lúc chương trình được compile.
- PostSharp Laos (Lightweight Aspect – Oriented Solution) là một AOP framework cho .NET developer, và theo tôi sử dụng nó tương đối đơn giản hơn AOP trong Spring. Tuy nhiên bù lại nó sẽ mất đi tính uyển chuyển như AOP trong Spring.NET. Các “advised code” sẽ phải reference đến các class Aspect của bạn, và do PostSharp modify asemblies lúc build time nên lúc build code bạn sẽ thấy hơi lâu một chút xíu.
- Thực sự thì PostSharp sẽ “transform” các .NET assemblies với dll PostSharp Laos. Với PostSharp, developer sẽ viết những “custom attributes” rồi sử dụng các Attributes này khi khai báo class/method để thêm những “custom behavior” cho code của mình. Theo như tác giả Nuvio thì PostSharp Laos là một “leading AOP solutions” cho .NET Framework như theo tôi thì chưa chắc. Tuy vậy cũng rất đáng để tìm hiểu platform này để mở rộng nhiều lựa chọn khi ta muốn lập trình AOP.
PostSharp thực sự hoạt động như thế nào?
- PostSharp là một post-compiler. Khi bạn thêm references đến những dll của PostSharp, nó sẽ tự động thêm phần code Aspect của bạn vào lúc bạn build chương trình. Có nghĩa là code chính của chương trình vẫn không đổi nhưng ouput assembly lại bị PostSharp biến đổi. PostSharp được tích hợp vào process build của MSBuild trong Visual Studio .NET 2005 và 2008, thậm chí nó được tích hợp vào bất kì IDE nào sử dụng MSBuild như SharpDevelop.
- Theo như tác giả giới thiệu thì PostSharp platform có thể được sử dụng lúc Runtime như Spring AOP thay vì lúc compile nhưng tôi vẫn chưa có thời gian tìm hiểu sâu vào chức năng này. Trong phạm vi bài viết này, chúng ta sẽ tìm hiểu khái quát xem PostSharp có thể giúp gì khi muốn lập trình AOP, và làm một sample đơn giản để hiện thực điều đó.
II. Lập trình AOP với PostSharp
- Để sử dụng PostSharp, ta có hai lựa chọn. Hoặc là các bạn download installer của PostSharp và install vào máy mình. Khi đó trình Installer của PostSharp sẽ làm hết mọi việc cho ta, từ đăng kí dll của PostSharp vào hệ thống đến cả tích hợp file help của nó vào VS.NET. Khi bạn build project có sử dụng dll của PostSharp, các bước biến đổi assembly lúc runtime sẽ tự động được gọi và ta không cần quan tâm đến nó.
- Lựa chọn thứ hai là ta chỉ download các dll của PostSharp, tạo project AOP bình thường và không cần Install bất cứ cái gì vào máy. Tuy nhiên bù lại ta phải thực hiện một số thay đổi vào file “csproj” của solution để buộc PostSharp hoạt động lúc ta build chương trình. Sample trong bài viết này sẽ thực hiện theo cách này vì tôi không thích cài bất cứ cái gì vào máy trong khi có thể làm được vói cách khác.
- Download những thứ cần thiết của PostSharp ở đây: http://www.postsharp.org/download/
- Download PostSharp sample code ở đây: http://nthoaiblog.googlepages.com/SamplePostSharpAOP.zip
- Khi lập trình AOP với PostSharp, ngoài những việc linh tinh như add references, sửa file project “csproj” nếu cần thiết,.. thì ta cần 2 bước chính.
Bước 1: Tạo class Aspect
- Tạo ra class Aspect của chúng ta thực sự là ra một class Attribute. Class Aspect này sẽ phải “derived” một trong các Aspect base classes của PostSharp như: OnExceptionAspect, OnMethodBoundaryAspect, OnMethodInvocationAspect, …
- Nếu không “derived” các base class này, ta có thể implement một trong những interface tương ứng như IOnExceptionAspect, IOnMethodBoundaryAspect, IOnMethodInvocationAspect, …
- Class Attribute của ta khi implement 1 interface thích hợp sẽ có vài trò và khả năng tương ứng với interface mà nó implement. Ví dụ khi ta tạo một class Attribute implement interface OnMethodBoundaryAspect, ta sẽ phải overwrite các method như OnEntry, OnExit, … Code của method OnEntry sẽ được PostSharp compiler “gắn” vào phần đầu của bất cứ function nào được khai báo bằng Attribute ta đang tạo ra; và code của method OnExit sẽ được “gắn” vào cuối cùng của bất cứ function nào được khai báo bằng Attribute này. Khá đơn giản và khá giống với khi ta implement interface ImethodInterceptor trong Spring.NET AOP đúng không? Tôi nghĩ rằng không cần giải thích thêm thì bạn cũng biết khi implement OnExceptionAspect sẽ tạo ra một Attribute để dùng trong trường hợp nào. Trong file chm document của PostSharp có đề cập rất chi tiết từng lớp như trên và ta có thể tham khảo để tìm ra class mình cần. Bây giờ hãy xem một lớp Attribute trong sample được implement như thế nào:
public sealed class AroundLoggingAttribute : OnMethodBoundaryAspect
{
private ConsoleColor _color = ConsoleColor.Green;
private ConsoleColor _currentColor = Console.ForegroundColor;
public AroundLoggingAttribute(ConsoleColor outputColor)
{
_color = outputColor;
}
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
Console.ForegroundColor = _color;
Console.WriteLine("Entering {0}.", eventArgs.Method);
Console.ForegroundColor = _currentColor;
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Console.ForegroundColor = _color;
Console.WriteLine("Leaving {0}.\n", eventArgs.Method);
Console.ForegroundColor = _currentColor;
}
}
Code 1
Theo như hướng dẫn của tác giả Nuvio thì ta nên khai báo lớp là seal và sử dụng attribute Serializable. Lớp AroundLoggingAttribute đã derived từ class OnMethodBoundaryAspect. Nó implement 2 method OnEntry và OnExit, print ra console một số message với màu được dùng từ màu ở constructor. OK, bây giờ ta sẽ sang bước thứ hai
Bước 2: Viết các xử lý của chương trình, sử dụng Attribute vừa được tạo:
- Trong Sample này, tôi cũng tạo ra một lớp ServiceCommand, chỉ print ra console vài message đơn giản, mỗi method của nó được khải báo với Attribute: [AroundLogging]. Các bạn chú ý tôi có sử dụng constructor cho Attribute để sử dụng các màu khác nhau cho 2 method SayHello và SayBye.
{
[AroundLogging(ConsoleColor.Green)]
public void SayHello()
{
Console.WriteLine("Hello Dudes!");
}
[AroundLogging(ConsoleColor.Red)]
public void SayBye()
{
Console.WriteLine("Bye. Visit: http://nthoai.blogspot.com");
}
}
Code 2
- Ta có thể không cần khai báo trên mỗi function, nếu ta muốn Attribute này được apply hết cho toàn bộ class thì chỉ cần khai báo một lần Attribute này cho class ServiceCommand. Thậm chí, nếu muốn apply cho toàn bộ class trong một Assembly, thì có thể modify file AssemblyInfo.cs để làm điều đó.
Bước 3: Điều chỉnh file csproj để PostSharp compiler được thêm vào quá trình build.
(Bước này sẽ không cần thiết nếu bạn sử dụng installer của PostSharp)
- Vì tôi không install PostSharp mà chỉ sử dụng những dll của platform này, tôi buộc phải chỉnh file csproj chính của solution để buộc PostSharp build và đưa Aspect AroundLogging vào assembly của ServiceCommand như đã khai báo.
- Tôi mở file PostSharpSample.ConsoleApp.csproj (đây là file project chính của solution), tìm đến dòng ở gần cuối:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
Và thêm vào
<Import Project="..\..\buidscript\PostSharp-1.0.targets" />
<Import Project="..\..\buidscript\PostSharp-1.0.targets" />
Hình 2
- Các bạn chú ý rằng tôi sử dụng đường dẫn tương đối để chỉ đến file PostSharp-1.0.targets. Các bạn có thể copy thư mục buildscript đến một nơi nào đó trong máy, sau đó sửa lại đường dẫn này theo đường dẫn mới, reload project khi VS.NET yêu cầu, save solution, đóng solution rồi mở lại solution và build lại lần nữa để chạy được chương trình. Nếu để ý bạn sẽ thấy quá trình build sẽ hơi chậm hơn bình thường.
- Nếu vẫn chưa “clear” hoặc muốn tìm hiểu thêm những config này, hãy đọc chương đầu trong file document chm của PostSharp. Nói chung thì ta nên để thư mục chứa các file build ở một nơi nào đó trong máy, chỉ chỉnh file project lúc cần thiết là được. Và nếu muốn đơn giản hơn thì install PostSharp cho lành.
Và đây là kết quả khi chạy chương trình:
Entering void SayHello()
Hello Dudes!
Leaving void SayHello().
Entering void SayBye()
Bye. Visit: http://nthoai.blogspot.com
Leaving void SayBye().
Hình 3
Nếu bạn dùng chương trình .NET Reflector để đọc assembly thì sẽ thấy code của chúng ta đã bị sửa đổi như hình
III. Kết luận
- PostSharp có vẽ dễ sử dụng, bạn có thể viết nhiều plugin của mình để gắn vào chương trình bằng cách implement các Interface đơn giản. Tuy tác giả giới thiệu PostSharp có thể hoạt động lúc Runtime nhưng tôi vẫn chưa chắc về cái Runtime của PostSharp. Có lẽ PostSharp không flexible như AOP của Spring.NET nhưng community của PostSharp có làm các project cuả họ để kết hợp PostSharp với các framwork nổi tiếng như Spring.NET, Entity Framework, ... Nói chung có đồ free xài là vui rồi, tuy nhiên ai có lòng tốt có thể donate cho bác Nuvio ở đây: http://www.postsharp.org/donate/
Sử dụng AOP với Spring.Net p2Posted On Friday, September 19, 2008 at at 5:27 PM by Van Nguyen
- Trong bài viết trước, chúng ta đã có cơ hội làm quen với AOP và cách sử dụng AOP trong Spring.NET với những khái niệm về Advice, Pointcut. Tất nhiên còn nhiều thứ khác được hỗ trợ để lập trình AOP với Spring.NET. Bài viết này sẽ tiếp tục giới thiệu các loại Advice khác nhau, các Pointcuts khác mà ta có thể sử dụng.
I.Có nhiều loại Advice được hỗ trợ sẵn trong Spring.Net
- Ở code sample trong bài viết trước, Advice được sử dụng là “around advice”. Đôi lúc, chúng ta chỉ muốn thêm “custom action” vào trước hoặc sau khi xử lý logic chính trong function của chúng ta được thực hiện. Tất nhiên sử dụng “around advice” cũng được thôi, nhưng có thể gây “confused” cho người khác khi đọc code. Nếu xem lại code của class ConsoleLoggingAroundAdvice và muốn sửa lại để nó chỉ thực hiện việc thông báo mỗi lần method chính được gọi thì sao? Thật ra ta chỉ cần thực hiện một “custom action” trước khi xử lý chính được gọi (trong trường hợp này, ta cần print ra màn hình một message thông báo tên của method sắp được gọi chẳng hạn). Người ta thường khuyên rằng chỉ nên sử dụng những gì ta cần (Ý tưởng gần giống với nguyên tắc ISP – Interface Segregation Principle) và Spring.NET có một loại Advice khác với “around advice” mà ta có thể dùng được. Nếu chỉ cần làm gì đó trược ghi gọi hàm chính, cách đơn giản nhất là sử dụng “before advice”.
I.1 Before advice
- “before advice” là … một advice chạy trước khi method chính được gọi. Vì thế nó không thể và không nên return cái gì cả. Thực sự thì đó là một điều tốt bời vì bạn sẽ không bao giờ vô ý quên gọi function Proceed() để thực hiện xử lý logic chính; và bạn cũng sẽ không bao giờ quên return kết quả trả về của method chính. Nếu ta không cần đọc hoặc sửa đổi giá trị trả về và cũng không cần làm một thao tác gì đặc biệt sau khi hàm chính thực hiện thành công thì “before advice” là tất cả những gì ta cần.
- “before advice” trong Spring.NET được sử dụng bằng cách implement ImethodBehoreAdvice. Hãy xem code sample sau đây:
{
public void Before(MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine("Intercepted call to this method : " + method.Name);
Console.Out.WriteLine(" The target is : " + target);
Console.Out.WriteLine(" The arguments are : ");
if ( args != null )
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
}
}
Code 1
- Tương tự như lần trước, ta xem cách “apply” advice mới này vào lớp ServiceCommand bằng code như thế nào:
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 2
Và kết quả khi chạy chương trình
Intercepted call to this method : Execute
The target is : Spring.Examples.AopQuickStart.ServiceCommand
The arguments are :
Hình 1
- Kết quả hiện đúng như những gì chúng ta mong muốn. Như vậy sử dụng “before advice” sẽ dễ dàng hơn và chúng ta sẽ không phải nhớ gọi Proceed() ở hàm chính cũng như return value nếu có. Sau đây là config XML cho “before advice”:
type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 3
I.2 After advice
- Tương tự như “before advice”, “after advice” là advice được thực hiện sau khi method chính của chúng ta được thực thi. Khi sử dụng “after advice” trong Spring.NET, ta phải implement IAfterReturningAdvice. Hãy xem đoạn code sample cho lớp ConsoleLoggingAfterAdvice
{
public void AfterReturning(
object returnValue, MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine("This method call returned successfully : " + method.Name);
Console.Out.WriteLine(" The target was : " + target);
Console.Out.WriteLine(" The arguments were : ");
if ( args != null )
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
Console.Out.WriteLine(" The return value is : " + returnValue);
}
}
Code 4
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 5
- Và đây là kết quả khi chạy chương trình
This method call returned successfully : Execute
The target was : Spring.Examples.AopQuickStart.ServiceCommand
The arguments were :
The return value is : null
Hình 2
- Output của console cho ta thấy advice trên đã được gọi sau khi method chính được gọi. Cũng như “before advice” khi sử dụng “after advice” chúng ta cũng không sợ phải quên gọi method Proceed() để thực hiện xử lý chính. Tuy nhiên chúng ta có thể access đến giá trị trả về của Method chính (nếu có). Mặc dù không cần phải return giá trị trả về này như khi dùng “around advice”, chúng ta vẫn có thể thay đổi giá trị của nó.
- Có thể nói sử dụng kết hợp “before advice” và “after advice” sẽ tránh những lỗi vô ý như khi dùng “around advice”. Sau đây là config XML để khai báo “after advice”:
type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 6
I.3 Throws advice
- 3 loại advice đã bàn tới có thể nói đã thõa mãn mọi yêu cầu khi lập trình AOP. Tuy nhiên, vẫn còn một loại khác cũng rất hữu ích: “throws advice”
{
}
Code 7
- Như tên gọi của nó, “throws advice” sẽ được gọi khi một “advised method” tung ra một exception. Do đó nếu “advised method” của chúng ta chạy trơn tru không văng lỗi gì thì Advice loại này sẽ không được gọi đến. Chúng ta có thể sử dụng “throws advice” để quản lý lỗi trong chương trình bằng cách apply nó cho tất cả các class ta muốn; chẳng hạn ta có thể ghi log lại khi có lỗi, hoặc email cho support team khi có lỗi xảy ra…
- Để sử dụng “throws advice” trong Spring.NET, ta phải implement IthrowsAdvice. Hãy xem code sample dưới đây:
{
public void AfterThrowing(Exception ex)
{
Console.Out.WriteLine("Advised method threw this exception : " + ex);
}
}
Code 8
- Để test được Advice loại này, hãy thử sửa lại code của function Excute() như sau:
{
public void Execute()
{
throw new UnauthorizedAccessException();
}
}
Code 9
- Cách apply “throws advice” của chúng ta bằng code cũng tương tự như những loại khác:
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 10
- Đây là kết quả khi chạy chương trình:
Advised method threw this exception : System.UnauthorizedAccessException:
Attempted to perform an unauthorized operation.
Hình 3
- Trong Spring.NET, sử dụng “throws advice” có nghĩa là bạn phải tạo một lớp implement IthrowsAdvice, sau đó, cho mỗi loại Exception bạn muốn lớp advice này có thể bắt được (tương tự như ý tưởng try – catch), ta phải khai báo một method với tham số Exception như sau:
Code 11
- Tên của method là AfterThrowing là cố định và không thể thay đổi, nhưng ta có thể tạo các overloading khác nhau tương ứng với các loại Exception ta muốn advice của mình có thể bắt được:
Code 12
- Sau đây là config XMl cho throwsAdvice:
type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 13
I.4 Layering advice
- Nếu chúng ta apply nhiều advice vào trong cùng một đối tượng mong muốn, các advice đó sẽ được gọi theo đúng thứ tự được thêm vào, điều này hiển nhiên đúng với các advice cùng một loại. Nếu nhiều advice với các loại khác nhau được sử dụng, thì những advice sẽ được gọi theo nguyên tắc đơn giản “before advice” sẽ gọi trước “after advice” cho dù after around được thêm vào trước. Nếu xét ví dụ code sample dưới đây thì thứ tự được gọi sẽ là BeforeAdvice, AroundAdvice, xử lý chính, AroundAdvice, AfterAdvice:
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 14
- Và sau đây là config xml cho tất cả các advice, các advcie sẽ được “gắn” theo đúng thứ tự ta khai báo trong file congig:
type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/>
<object id="afterAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/>
<object id="beforeAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/>
<object id="aroundAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
<value>afterAdvice</value>
<value>beforeAdvice</value>
<value>aroundAdvice</value>
</list>
</property>
</object>
Code 15
- Ngoài ra, ta có thể implement thêm IOrdered cho các lớp Advice để buộc chúng thực thi theo thứ tự mong muốn.
II. Sử dụng Attributes để định nghĩa Pointcuts
- Các Pointcut có thể được xác định dựa vào một Attribute đánh dấu trên một “advised method”. Nhờ đó, một Advice loại này có thể đọc thông tin từ Attribute của Method và thực hiện một số thao tác nào đó. Lớp AttributeMatchMethodPointcut của Spring.NET đã hỗ trợ chúng ta chức năng này, ví dụ sau đây sẽ tìm tất cả các methods nào được khai báo với attribute ConsoleLogging (đây là lớp Attribute người dùng tạo ra, không phải lớp built-in của Spring hay của .NET framework) và gọi thực hiện xử lý của ConsoleLoggingAdvice trước khi thực hiện xử lý logic chính.
Sau đây là code của class ConsoleLoggingAttribute là một class Attribute được sử dụng khi khai báo các method trong chương trình AOP:
{
private ConsoleColor _color = ConsoleColor.Gray;
// Gets or sets the foreground color of the console.
public ConsoleColor Color
{
get { return _color; }
set { _color = value; }
}
public ConsoleLoggingAttribute(ConsoleColor color)
{
_color = color;
}
public ConsoleLoggingAttribute()
{
}
}
Code 16
- Ta tạo một lớp command khác tên là AnotherCommandService, method Excute của nó sẽ được đánh dấu bằng Attribute “ConsoleLogging” vừa được tạo ra ở trên:
{
[ConsoleLogging(Color = ConsoleColor.Green )]
public void Execute()
{
Console.Out.WriteLine("--- AnotherServiceCommand implementation : Execute()... ---");
}
}
Code 17
- Kế tiếp, ta sẽ tạo ra một lớp Advice khác, lớp Advice này sẽ đọc thông tin từ Attribute của “advised method” tương ứng và từ đó print text ra console với các màu sắc khác nhau:
{
private ConsoleColor _color = ConsoleColor.Gray;
public ConsoleColor Color
{
get { return _color; }
set { _color = value; }
}
public object Invoke(IMethodInvocation invocation)
{
ConsoleLoggingAttribute[] consoleLoggingInfo =
(ConsoleLoggingAttribute[])invocation.Method.GetCustomAttributes(typeof(ConsoleLoggingAttribute), false);
if (consoleLoggingInfo.Length > 0)
{
Color = consoleLoggingInfo[0].Color;
}
ConsoleColor currentColor = Console.ForegroundColor;
Console.ForegroundColor = Color;
Console.Out.WriteLine(String.Format(
"Intercepted call : a.Out to invoke method '{0}'", invocation.Method.Name));
Console.ForegroundColor = currentColor;
object returnValue = invocation.Proceed();
Console.ForegroundColor = Color;
Console.Out.WriteLine(String.Format(
"Intercepted call : after excuting method, returned '{0}'", returnValue));
Console.ForegroundColor = currentColor;
return returnValue;
}
}
Code 18
- Và đây là config xml để kết hợp tất cả các thành phần trên:
type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
</property>
<property name="Attribute"
value="Spring.AopQuickStart.Attributes.ConsoleLoggingAttribute, Spring.AopQuickStart.AttributePointcut" />
</object>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 19
- Kết quả trên màn hình khi chạy chương trình:
Intercepted call : about to invoke method 'Excute'
--- AnotherServiceCommand implementation : Execute()... ---
Intercepted call : after excuting method, returned ''
Hình 4
- Download sample code ở đây: http://nthoaiblog.googlepages.com/SampleSpringAOPAttributePointCut.zip
III. Custom pointcuts:
- Trong Spring.NET ta có thể định nghĩa những pointcut cho chính mình bằng cách overwrite method Match của superclass StaticMethodMatcherPointcut:
{
public override bool Matches(MethodInfo method, Type targetType)
{
// return true if YOUR custom criteria match
}
}
Code 20
IV.Spring.NET có nhiều chức năng rất hay mà tôi vẫn đang học. Bài viết trước và bài này tôi dịch và có thêm một số ý khác để người mới tìm hiểu có thể nắm bắt nhanh hơn. Tuy vậy vẫn có nhiều chức năng khác như: Introduction, TargetSource, … trong Spring AOP mà tôi không đề cập trong 2 bài viết này và xin để bạn tự tìm hiểu. Cuối cùng, xin cám ơn những ai đã đọc và comment.
Sử dụng AOP với Spring.NetPosted On Wednesday, September 17, 2008 at at 6:57 PM by Van Nguyen
I. AOP là cái gì?
- Ai chẳng biết AOP là từ viết tắt của Aspect Oriented Programming, mấy bác thích dịch ra tiếng Việt thì bảo rằng AOP là “phương pháp lập trình hướng khiá cạnh”. Có lẽ theo cách hiểu ấy nên không ít lần tôi nhìn thấy mấy cái slide kiểu trò chơi ghép hình trong các seminar về AOP hồi học đại học. Lúc đó thì OOP còn chưa lĩnh hội hết chứ đừng nói AOP, bởi vậy xem xong cũng chẳng hiểu chi cả. Đến bây giờ khi đã hiểu được những khái niệm cơ bản, tôi thích gọi AOP là “A Ô Pê” hơn, khỏi dịch ra chi cho lôi thôi. Trong bài viết này, tôi sẽ giới thiệu về AOP và cách sử dụng AOP với Spring.NET.
- Trong khi xây dựng các chương trình ứng dụng, chúng ta sẽ có rất nhiều những vấn đề liên quan đến phần mềm mà ta phải quan tâm. Tôi xin lấy ví dụ sau đây: tôi phải viết một chương trình để upload files, folders lên một server. Vấn đề khá đơn giản phải không? Nếu object được chọn là file thì chỉ việc upload cái file đó lên. Còn nếu object được chọn là một folder thì upload hết các file con bên trong folder đó, sau đó lại duyệt các folder con của nó và gọi hàm đệ quy. Hê hê, nhưng công việc không chỉ có vậy, tôi được yêu cầu phải thêm nhiều cái linh tinh khác như phải kiểm tra xem 1 folder, 1 file có tồn tại chưa, phải kiểm tra xem user hiện tại có đủ quyền hay không, phải ghi mọi thao tác ghi xóa hay thông báo lỗi xuống file log, phải hiển thị process bar, phải thế này, phải thế kia. Kết quả là hàm upload của tui trở thành một đống bùi nhùi, phần code phục vụ cho việc đọc, ghi file chỉ có vài dòng trong khi những code cho các việc kiểm tra, thông báo lỗi, hiển thị process bar chiếm rất nhiều nội dung của một hàm. Ngoài ra code của tôi bị phụ thuộc lẫn nhau ở nhiều nơi, chẳng hạn như những xử lý đọc ghi file do cần phải update process trên process bar nên nó lại phải reference đến project winform, đúng là rồi hơn chữ “RỐI”.
- Trong chương trình này, vấn đề quan tâm chính của tôi là upload folder/files và bên cạnh đó sẽ có một số vấn đề khác cần phải thực hiện. Các bạn có thể thấy rằng trong khi viết phần mềm, ta thường gặp hai loại “concern”: core concerns và cross-cutting concerns:
+ Core concern: là requirement chính của chương trình, ví dụ như upload file/folder, đọc danh sách user, …
+ Cross-cutting concerrns: là những xử lý phụ cần được thực hiện khi “core concern” được gọi. Cross-cuttong concerns thường xảy ra nhiều nơi trong chương trình, nó có thể xảy ra trong nhiều layer của ứng dụng, nhiều class, nhiều method; chẳng hạn như tính toán thời gian chạy các hàm đọc ghi database, ghi log lại mỗi lần cập nhật thông tin user, …
- Đã Từ lâu, người ta đã nghĩ ra chiêu thức AOP để giải quyết vấn đề trên. Aspect-Oriented Programming(AOP) còn được gọi là Aspect-Oriented Software Development (AOSD) là một nguyên tắc thiết kế giúp tách rời các yêu cầu hay các vấn đề được quan tâm (separation of concerns) trong chương trình thành các thành phần độc lập và từ đó tăng tính uyển chuyển cho chương trình. “Separation of concerns” là một trong những kĩ thuật được quan tâm nhất trong ngành phần mềm. Người ta cho rằng những vấn đề tương tự nhau nên được giải quyết trong một “unit of code”. Khi lập trình thủ tục, một “unit of code” là 1 function, 1 method. Còn trong lập trình hướng đối tượng thì “unit of code” là một class.
II. Những lợi ích của “separate of concerns”
- Trong AOP, từ “Aspect” chính là vấn đề [concern] người lập trình quan tâm và nó xuất hiện trong rất nhiều class cũng như nhiều method khác nhau. Kĩ thuật AOP thường được sử dụng để giải quyết các vấn đề như caching, tracking, security hay failure injections. Vì thế, nhiều tài liệu nói rằng AOP giúp module hóa ứng dụng, biến chương trình thành các module hoạt động độc lập, mỗi module làm một chức năng riêng, từ đó dễ bảo trì và nâng cấp.
- Ở vai trò của người thiết kế phần mềm, chúng ta nên đưa ra các cách làm đơn giản nhất. Để thỏa mãn yêu cầu của chương trình, người ta sẽ tạo ra thành phần chính của chương trình (gồm các class/component/method); các chức năng bổ sung như loging, tính toán performance, .. cũng sẽ được xem xét để tạo ra. Vì do các chứng năng bổ xung này không phải là yêu cầu chính của hệ thống nên người ta sẽ có yêu cầu bật tắt chúng theo ý muốn. Vậy làm thế nào để có thể tạo ra chương trình có thể linh hoạt được như thế? Câu trả lời là “Separate of concerns”
- Ở vai trò của người lập trình, chúng ta có hai vấn đề cần quan tâm là xử lý logic chính của chương trình và các xử lý logic cho những thành phần phụ. Do đó tất nhiên sẽ phải tạo ra các class/method cho các yêu cầu thực sự, và tạo ra những class/method khác độc lập để thực hiện các yêu cầu phụ kia. Tất cả các class/method này có thể được kết hợp lúc runtime theo ý muốn. Chẳng hạn như trong môi trường test, người ta có thể bật chức năng log, đo đạc performance để theo dõi nhưng khi cài đặt trên môi trường Production, các chức năng phụ này có thể được disable. Và trên nguyên tắc, trong code của những xử lý logic chính sẽ không có code để thực hiện các yêu cầu phụ.
- Tóm lại, ta có thể liệt kê một số lợi ích như sau:
+ Chức năng chính của chương trình không cần biết đến các chức năng phụ khác
+ Các chức năng phụ có thể được thêm thắt, bật tắt lúc runtime tùy theo yêu cầu
+ Các thay đổi, sửa lỗi, nâng cấp nếu có đối với các chức năng phụ sẽ không ảnh hưởng đến chương trình chính.
+ Hệ thống sẽ uyển chuyển và giảm thiểu tính phụ thuộc lẫn nhau của các module
III. Các thuật ngữ thường gặp trong AOP
+ Join point: Một điểm trong chương trình. Ví dụ như ta cần ghi log lại sau khi chạy method thì điểm ngay sau method đó được thực thi gọi là một jointpoint để có thể chạy những xử lý ghi file log. Join point theo nghĩa của nó có thể hiểu là những nơi có thể được chèn những “custom action” của bạn.
+ Pointcut: Có nhiều cách để xác định joinpoint, những cách như thế được gọi là pointcut.
+ Advice: là những xử lý phụ được thêm vào xử lý chính. Code để thực hiện các xử lý đó được gọi là Advice. Vì các xử lý phụ có thể thêm vào trước hoặc sau hoặc cả hai đối với những xử lý chính nên có nhiều loại Advice khác nhau mà ta sẽ đề cập trong những phần sau.
IV. AOP trong Spring.NET
- AOP là một trong những chức năng chính được hỗ trợ trong Spring.NET. Với AOP framework của Spring.NET bạn có thể thoải mái thêm thắt các “custom action” (Advice) của mình vào một hay nhiều function tại những vị trí (Joinpoint) mong muốn tùy theo những trường hợp cụ thể (Pointcut). Trong bài viết này, tôi sẽ trình bày một sample code thể hiện ví dụ đơn giản để sử dụng AOP framework của Spring.NET. Nếu bạn đã biết về Dependency Injection sử dụng Spring.NET thì bạn sẽ thấy rằng AOP của Spring.NET cách sử dụng gần như tương tự.
IV.1 Sử dụng Advice
- Xin nhắc lại điều này: tôi gọi Advice là những “custom action” mà bạn thêm vào chương trình của mình. Cụ thể hơn nữa, Advice là code bạn viết để xử lý những “custom action” đó, và tất nhiên theo nguyên tắc của AOP thì những code này không hề phụ thuộc vào chương trình chính. Có thể nói rằng bạn sẽ tạo một project độc lập với các project có sẵn trong solution của bạn, sau đó thêm vào các class, các method để xử lý các “custom action”. Tất nhiên các class Advice sử dụng AOP của Spring phải được viết theo quy tắc của Spring.NET AOP Framework, nó sẽ phải implement interface IMethodInterceptor (Có nhiều interface tương tự ứng với các loại Advice khác nhau). Và cuối cùng, các Advice mà bạn viết sẽ được gắn vào nơi cần thiết dựa vào xml config của Spring.NET.
- Chúng ta hãy xem một ví dụ đơn giản để thấy Advice hoạt động như thế nào. Code sample dưới đây sẽ thêm chức năng print vài messages ra console trước và sau khi method chính được gọi. Do Advice được hiểu như một “custom action” cho một method nào đó (các bạn chú ý đối tượng được thêm “custom action” thường là các method, và chúng được hiểu là các “target of the advice”), nên trong các tài liệu ta sẽ thấy ghi là “advised method”. Như vậy “advised method” sẽ là method chính, còn các “Advice” chứa các xử lý được thêm vào “advised method”. Cũng phải thừa nhận rằng sample dưới đây không thể hiện hết tất cả khả năng của AOP, nhưng nó sẽ giúp bạn hiểu rõ về khái niệm Advice trong AOP. Và khi đã nắm được rồi, bạn sẽ có thể apply những Advice của chính bạn viết cho những chương trình của bạn.
- Trước khi xem code của Advice trong sample này, chúng ta hãy xem qua các domain classes dưới đây. Khi method Excute() trong class ServiceCommand được gọi, nó sẽ print ra console một thông báo đơn giản. Class ServiceCommand này sẽ là “target of the advice”, method “Excute()” sẽ là “advised method”; trong các thuật ngữ của Spring.NET AOP, một instance của class ServiceCommand sẽ được gọi là “advised object”. Các bạn chú ý rằng interface ICommand và class ServiceCommand là những class và interface dùng để làm ví dụ chúng không phải là các type của Spring.AOP. Có thể bạn sẽ thắc mắc tại sao phải tạo interface ICommand rồi để class ServiceCommand implement nó, câu trả lời là để tăng tính “Loosed coupling”. Ngoài ra, cách sử dụng AOP trong Spring.NET rất giống với DI trong Spring, các object (trong trường hợp này là instance của ServiceCommand”) được Spring tạo ra theo cách bạn config trong file xml nên trong chương trình chính ta sẽ sử dụng ICommand để khai báo thay vì khai báo một class cụ thể ServiceCommand.
{
object Execute(object context);
}
public class ServiceCommand : ICommand
{
public object Execute(object context)
{
Console.Out.WriteLine("Service implementation : [{0}]", context);
return null;
}
}
Code 1
- Đoạn code phía dưới đây sẽ là code của một Advice, Advice này sau đó sẽ được “gắn” vào method Excute() của class ServiceCommand. Như tôi đã giới thiệu, có nhiều loại Advice khác nhau, trong ví dụ này ta sẽ tạo một “Around Advice”, nghĩa là ta có thể thêm những “custom action” vào cả trước và sau khi gọi method Excute() của class ServiceCommand. (Tham khảo Spring.NET Section 13.3.2 “Advice types”)
{
public object Invoke(IMethodInvocation invocation)
{
Console.Out.WriteLine("Advice executing; calling the advised method...");
object returnValue = invocation.Proceed();
Console.Out.WriteLine("Advice executed; advised method returned " + returnValue);
return returnValue;
}
}
Code 2
- Đến lúc này ta đã tạo ra 3 thành phần: interface (ICommand); class (ServiceCommand); và một Advice: class (ConsoleLoggingAroundAdvice). Phần còn lại là làm sao để “gắn” Advice này vào method Excute() của class ServiceCommand. Sau đây là cách kết nối các thành phần trên bằng code.
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute("This is the argument");
Code 3
Và đây là kết quả khi chạy chương trình
Advice executing; calling the advised method...
Service implementation : [This is the argument]
Advice executed; advised method returned
Hình 1
- Output của chương trình cho ta thấy rằng Advice của chúng ta đã được applied “around” phần xử lý logic của “advised method” Excute()
- Có thể bạn sẽ thắc mắc ProxyFactory là cái gì ở đây? Constructor của class ProxyFactory sử dụng một argument đầu vào là kiểu của một object ta muốn thêm vào những Advice (trong sample này, thì argument cho hàm khởi tạo là instance của class ServiceCommand). Sau đó ta sẽ thêm các Advice (một instance của class ConsoleLoggingAroundAdvice) bằng cách sử dụng method AddAdvice(). Kế tiếp ta gọi method GetProxy() để trả về một proxy (AOP proxy), proxy này có thể gọi là một object hành nhái thuộc kiểu ICommand. Khi gọi method Excute() của proxy này, các xử lý của Advice và “advised method” sẽ lần lược được thực hiện. Hình bên dưới thể hiện quy trình hoạt động của Spring.NET AOP proxy
- Có một điểm đáng lưu ý là AOP proxy trả về khi gọi method GetProxy() được cast về kiểu ICommand. Hiện nay, có vẽ như chúng ta buộc phải làm như vậy cho các “advised object”. Nói theo cách khác để cho các class của bạn có thể sử dụng được với AOP của Spring.NET, các class đó phải implement ít nhất một interface. Trong thực tế thì đòi hỏi này không đến nỗi phiền hà như vậy vì nói chung ta nên sử dụng interface khi có thể. (Chức năng hỗ trợ AOP cho class mà không cần implement interface đang được Spring hứa hẹn đưa ra trong các phiên bản sau).
- Khi sử dụng AOP của Spring.NET, ta sẽ không cần phải “gắn” các Advice bằng code theo kiểu ở trên. Đoạn config xml dưới đây sẽ làm y chang những gì phần code 3 thực hiện:
<object id="consoleLoggingAroundAdvice"
type="Spring.Examples.AopQuickStart.consoleLoggingAroundAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 4
command.Execute();
Code 5
- Lại có một số điểm cần lưu ý đối với đoạn config xml này; đoạn config này được thêm vào file config của chương trình (app.config hoặc web.config). Trong toàn bộ hệ thống thì ConsoleLoggingAroundAdvice bản thân nó cũng chỉ là một object thông thường, và vì thế nó được config theo cách thông thường như những class khác. Do đó nếu chúng ta muốn “inject” nó bằng những kĩ thuật như DI của Spring.NET thì cách làm cũng bình thường.
- Điểm lưu ý thứ 2: Class ProxyFactoryObject là một class implement từ IFactoryObject interface, nó có nhiệm vụ tạo ra một proxy object tương ứng với “target” được config. Trong trường hợp này, ta muốn chương trình của mình sẽ tạo ra một proxy của class ServiceCommand nên “target” sẽ được khai báo là namespace class ServiceCommand.
- Cuối cùng, ta nên lưu ý rằng Advice sẽ được “gắn” vào target object bằng cách khai báo trong phần interceptorNames. Trong ví dụ này, chỉ có một Advice được khai báo, nếu như có thêm các class Advice khác, chỉ việc thêm xml config cho chúng và thêm vào list của interceptorNames.
Các bạn có thể download code sample ở đây:
http://nthoaiblog.googlepages.com/SampleSpringAOP.zip
IV.2 Sử dụng Pointcut
- Trong ví dụ trước, ConsoleLoggingAroundAdvice được khai báo dù trong code hay trong file xml đều sẽ được “gắn” vào mọi method của ServiceCommand. Nếu chúng ta muốn nó chỉ “gắn” vào một hoặc nhiều method của class này theo một số tiêu chí nào đó thì đã đến lúc ta nghĩ đến việc sử dụng Pointcut. Các tiêu chí ở đây khá đa dạng: có thể bạn muốn những method với tên method bắt đầu bằng “Start” sẽ được “advised”; hoặc bạn chỉ muốn những method được gọi với các giá trị argument đặc biệt lúc runtime; hay là rất có thể bạn chỉ muốn các Advice được thêm vào những method được đánh dấu bằng attribute đặc biệt nào đó.
- Tất cả những yêu cầu trên được Spring.NET đóng gói trong interface IPointcut (tham khảo Spring AOP Section 13.2 “Pointcut API in Spring.NET"). Bây giờ chúng ta sửa class
ServiceCommand, thêm vào method DoExcute(), sau đó sẽ thực hiện một số thay đổi để chỉ method DoExcute() được “advised”:
{
void Execute();
void DoExecute();
}
public class ServiceCommand : ICommand
{
public void Execute()
{
Console.Out.WriteLine("Service implementation : Execute()...");
}
public void DoExecute()
{
Console.Out.WriteLine("Service implementation : DoExecute()...");
}
}
Code 6
- Tất nhiên class ConsoleLoggingAroundAdvice sẽ không cần phải thay đỏi gì cả. Tiếp đến chúng ta sẽ config cho class proxy như sau:
factory.AddAdvisor(new DefaultPointcutAdvisor(
new SdkRegularExpressionMethodPointcut("Do"),
new ConsoleLoggingAroundAdvice()));
ICommand command = (ICommand) factory.GetProxy();
command.DoExecute();
Code 7
- Và đây là kết quả khi chạy chương trình
Intercepted call : about to invoke next item in chain...
Service implementation...
Intercepted call : returned
Hình 3
- Tất nhiên khi ta gọi Excute() thay vì DoExcute(), những xử lý của Advice trên sẽ không hiện ra:
factory.AddAdvisor(new DefaultPointcutAdvisor(
new SdkRegularExpressionMethodPointcut("Do"),
new ConsoleLoggingAroundAdvice()));
ICommand command = (ICommand) factory.GetProxy();
// Note that there is no 'Do' in this method name
command.Execute();
Code 8
Service implementation...
Hình 4
- Sau đây là đoạn XML config cho tất cả những yêu cầu trên:
<object id="consoleLoggingAroundAdvice"
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor">
<property name="advice">
</property>
</object>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 9
- Như ta thấy đoạn config chỉ thay đổi ở phần consoleLoggingAroundAdvice. RegularExpressionMethodPointcutAdvisor đã được thay vào Advisor ban đầu; các property “pattern” và “Advice” được config để xác định Advice tương ứng và pattern tương ứng để match với method có chứa text “Do” trong các method của đối tượng được “advised”.
Download code sample cho phần Pointcut ở đây:
http://nthoaiblog.googlepages.com/SampleSpringAOPWithPointcut.zip
V. Kết luận
- Đây không phải là bài viết tốt nhất về AOP cũng như AOP trong Spring.NET. Tôi đã dịch lại và bổ xung một số ý từ các tài liệu ở phần tham khảo phía dưới. Ban đầu tôi cũng gặp khó khăn khi tìm hiểu cách sử dụng vì Spring.NET đưa ra khá nhiều các khái niệm, thuật ngữ về AOP. Nhưng cuối cùng tôi thấy cách sử dụng AOP với Spring.NET khá giống như chức năng Dependency Injection. Bài viết này chỉ nói đến những chức năng đơn giản của Spring.NET AOP nên trong bài viết sau ta sẽ nói đến các chức năng khác của AOP trong Spring.NET như các loại Advice khác, cách sử dụng Attribute để xác định Pointcuts, … Đối với ai mới research cách sử dụng AOP của Spring.NET, ngoài việc đọc bài này hehe , tôi đề nghị download Spring.NET framework về và chạy các sample AOP của nó, vô cùng dễ hiểu.
(Xem Phần 2)

markdown:
##
Posted On Tuesday, September 30, 2008 at
at
[Sample Asp.NET MVC AJAX với JQuery](http://nthoai.blogspot.com/2008/09/aspnet-mvc-ajax-with-jquery.html)
[5:13 PM](http://nthoai.blogspot.com/2008/09/aspnet-mvc-ajax-with-jquery.html)by Van Nguyen [](https://www.blogger.com/post-edit.g?blogID=3978293580246860892&postID=4844543876946485465&from=pencil)
- Theo tin trên blog của bác
[ScottGu](http://weblogs.asp.net/scottgu/default.aspx) thì [JQuery](http://jquery.com/) trong tương lai sẽ được ship cùng với .NET IDE của Microsoft. Phiên bản MVC Preview5 cũng mới được cho ra lò. Bản thân tui trước giờ vẫn thắc mắc là khi sử dụng MVC thì sẽ viết web Ajax như thế nào. Sau một lúc gu gồ, tui tìm thấy 1 link khá hay của bác [Phil Haack](http://haacked.com/). Vì nội blog entry đó chỉ giới thiệu 1 video podcast và không có code mẫu nên tui quyết định làm theo hướng dẫn và viết một entry cuối cùng trong tháng 9 thân yêu
.
- Trong bài viết này, tui sẽ làm y chang hướng dẫn của bác
[Phil Haack](http://haacked.com/). Như chúng ta biết, thì AJAX là chữ viết tắt của “Asynchronous JavaScript and XML”. Như vậy ta có thể làm một trang web có AJAX bằng cách kết hợp JavaScript với các WebService. Một trong những mục đích của AJAX là tránh postback về phía server khi thực hiện các thao tác đơn giản. Trường hợp điển hình nhất là những Quick Reply của các forum. Khi bạn trả lời một bài viết nào đó vào ô text box Quick Reply rồi click vào nút gửi đi, nội dung bạn đánh vào sẽ được lưu vào database, phần HTML hiển thị reply của bạn sẽ hiện ra ngay sau bài viết mới nhất và những gì bạn đánh vào text box sẽ bị xóa đi. Đó làm một chức năng AJAX điển hình mà ta có thể code bằng tay được.
- Nếu là tui, tui sẽ implement một webservice để nhận request save xuống database nội dung bài post khi người ta reply. Kết tiếp viết một số hàm JavaScript đơn giản để hiển thị thêm phần nội dung HTML trả lời vào dưới cùng của bài viết và xóa trắng text box nếu cần thiết. Và thật sự đó là cách tui làm ở cty trước, khi implement chức năng reply post cho trang web
[www.cofieu.com.vn/news](http://www.cofieu.com.vn/news). Khi đó tui chưa sử dụng [JQuery](http://jquery.com/) nên đã code hơi trâu bò một cách không cần thiết, trong khi những gì tui cần để select các DOM object trong HTML đã được [JQuery](http://jquery.com/) hỗ trợ hết. Cái thật sự cần làm là WebService để thực hiện request từ phía client.
- Để tóm tắt, tui xin trình bày cách mà bác Phil Haack đã sử dụng. Trong video podcast của bác Phil không cần implement một WebService mới mà chỉ implement một hàm Action, trả về một JSON object. Và ta sẽ phải viết một số hàm JavaScript đơn giản, nhận data đó , kết hợp với JQuery để update HTML vào chỗ thích hợp. Ý tưởng là vậy, ta sẽ bắt tay vào làm demo đó như sau:
- Trước hết để thực hiện được demo này, các bạn đã install .NET Framework 3.5 SP1, VS.NET 2008 SP1, Asp.net MVC Preview 5 Codeplex. Nếu chưa có chịu khó gu gồ nhe
.
- Trong tương lai thì các file js của thư viện JQuery sẽ được include sẵn mỗi khi mình tạo project web. Còn hiện nay MVC vẫn còn đang thử nghiệm nên chúng ta chịu khó download 2 file js ở các link sau về để include vào trong project:
Link 1:
[jquery-1.2.6.js](http://docs.jquery.com/Downloading_jQuery)
Link 2:
[jquery.color.js](http://www.google.com.vn/search?q=jquery.color.js)
- Sau khi đã install và download những thứ cần thiết, bạn hãy mở VS.NET 2008, tạo mới 1 project web ASP.NET
[MVC](http://nthoai.blogspot.com/search/label/MVC). Kéo 2 file js vừa download vào thư mục Content như hình: ![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Simple ASP.NET MVC AJAX with JQuery] body=[Thêm 2 file java script vào project web] Simple ASP.NET MVC AJAX with JQuery](http://img528.imageshack.us/img528/7518/12jsxn8.png)
- Mở file Site.Master để include hai file đó vào website. Các bạn có thể kéo thả cho lẹ
![50 :bbpraroi:](http://igame.vn/f/images/smilies/BBP_smilies/50.gif)
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Simple ASP.NET MVC AJAX with JQuery] body=[Thêm 2 file java script vào master page] Simple ASP.NET MVC AJAX with JQuery](http://img528.imageshack.us/img528/2259/2include2jskz4.png)
- Để thử cách viết Controller của MVC, ta hãy tạo mới một Controller khác và không đụng đến các Controller có sẵn khi tạo ra Project web. Như vậy ta sẽ tạo ra một trang mới là TestAjax/Main.aspx, và Controller tương ứng cho View này sẽ là TestAjaxController:
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Simple ASP.NET MVC AJAX with JQuery] body=[Thêm TestAjaxController và View tương ứng] Simple ASP.NET MVC AJAX with JQuery](http://img528.imageshack.us/img528/1892/3addcontrollerar2.png)
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Trong file Main.aspx, chúng ta sẽ thêm vào 2 span với id lần lượt là name và url để hiển thị thông tin nhận được khi request. Ta cũng sẽ tạo một button với sự kiện onClick ứng với hàm JavaScript ShowInformation() mà ta sẽ implement sau. Còn bây giờ hãy xem đại khái HTML của file Main.aspx:
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Simple ASP.NET MVC AJAX with JQuery] body=[Html code của Main.aspx] Simple ASP.NET MVC AJAX with JQuery](http://img528.imageshack.us/img528/1568/4htmlft6.png)
- Như vậy hàm JavaScript ShowInformation sẽ thực hiện việc Request lên server, sau đó nhận data từ server trả về và cập nhận html cho 2 thẻ span phía trên. Nội dung của hàm JavaScript như sau:
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Simple ASP.NET MVC AJAX with JQuery] body=[Code javascript của hàm ShowInformation] Simple ASP.NET MVC AJAX with JQuery](http://img528.imageshack.us/img528/159/5jsshowinformationvx3.png)
- Giải thích: Hàm ShowInformation sẽ request đến address “TestAjax/GetInformation để lấy dữ liệu. Request này sẽ được thực thi bởi hàm GetInformation trong class TestAjaxController
![50 :bbpraroi:](http://igame.vn/f/images/smilies/BBP_smilies/50.gif)
{
return Json( new { Message1 = "Nguyen Thoai",
}
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Điều đáng lưu ý là function này là nó tạo ra 1 Annonymouse Object và wrap trong một JSonResult Type để trả về cho client. Như ta thấy trong hình thì Anonymouse Object này sẽ có 2 properties là Message1 và Message2 và 2 field này được sử dụng trong hàm JavaScript ở trên. (Anonymous Object là 1 feature mới của .NET Framework 3.x)
- Cuối cùng tui có thêm vào một hàm JavaScript để tạo hiệu ứng đẹp mặt như hướng dẫn trong video podcast. Tóm lại nếu mới tìm hiểu ASP.NET MVC và chưa biết nó support AJAX tới đâu nhưng ta chắc chắn rằng sẽ có cách để làm những hiệu ứng AJAX rất nhanh và dễ dàng nhờ vào JQuery.
- Các bạn có thể download video trên ở đây:
[http://www.aspnetpodcast.com/VideoFiles/ASPNETPodcast20080727-MVCWithPhilHaack.wmv](http://www.aspnetpodcast.com/VideoFiles/ASPNETPodcast20080727-MVCWithPhilHaack.wmv)
- Link bài viết gốc của tác giả:
[http://haacked.com/archive/2008/07/29/super-simple-mvc-ajax-with-jquery-demo.aspx](http://haacked.com/archive/2008/07/29/super-simple-mvc-ajax-with-jquery-demo.aspx)
- Demo của tui
![17 :bbpcuoi3:](http://igame.vn/f/images/smilies/BBP_smilies/17.gif)
[http://nthoaiblog.googlepages.com/SampleMvcPreview5AjaxJQuery.zip](http://nthoaiblog.googlepages.com/SampleMvcPreview5AjaxJQuery.zip) [http://nthoaiblog.googlepages.com/SampleMVCRCAjaxJQuery.zip (MVC Release Candidate)](http://nthoaiblog.googlepages.com/SampleMVCRCAjaxJQuery.zip)
##
Posted On Saturday, September 20, 2008 at
at
[Sử dụng AOP với PostSharp Framework](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-postsharp-framework.html)
[9:03 PM](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-postsharp-framework.html)by Van Nguyen [](https://www.blogger.com/post-edit.g?blogID=3978293580246860892&postID=4545366582913993725&from=pencil)
- Spring.NET là một framework với nhiều chức năng trong đó có AOP. Nguyên tắc hoạt động của AOP trong Spring là gắn những “custom action” vào hệ thống lúc RunTime nhờ vào khả năng tùy biến rất cao của các file xml config. Có thể nói Spring.NET là framework hỗ trợ AOP mạnh nhất hiện nay. Ngoài Spring.NET còn có nhiều framework AOP khác cho .NET, trong đó mới nổi lên là
[PostSharp](http://www.postsharp.org/aop.net).
# I. Giới thiệu PostSharp framework
- Trước hết PostSharp là một free, open-source platform để “enhance” .NET assemblies, có nghĩa là PostSharp sẽ biến đổi assemblies của bạn trong khi bạn build source code. Điều này hoàn toàn khác với AOP của Spring.NET. Spring sẽ thực hiện những biến đổi của bạn lúc runtime, còn PostSharp sẽ trực tiếp thay đổi kết quả build source code ngay lúc chương trình được compile.
- PostSharp Laos (Lightweight Aspect – Oriented Solution) là một AOP framework cho .NET developer, và theo tôi sử dụng nó tương đối đơn giản hơn AOP trong Spring. Tuy nhiên bù lại nó sẽ mất đi tính uyển chuyển như AOP trong Spring.NET. Các “advised code” sẽ phải reference đến các class Aspect của bạn, và do PostSharp modify asemblies lúc build time nên lúc build code bạn sẽ thấy hơi lâu một chút xíu.
- Thực sự thì PostSharp sẽ “transform” các .NET assemblies với dll PostSharp Laos. Với PostSharp, developer sẽ viết những “custom attributes” rồi sử dụng các Attributes này khi khai báo class/method để thêm những “custom behavior” cho code của mình. Theo như tác giả Nuvio thì PostSharp Laos là một “leading AOP solutions” cho .NET Framework như theo tôi thì chưa chắc. Tuy vậy cũng rất đáng để tìm hiểu platform này để mở rộng nhiều lựa chọn khi ta muốn lập trình AOP.
![50 :bbpraroi:](http://www.igame.vn/f/images/smilies/BBP_smilies/50.gif)
## PostSharp thực sự hoạt động như thế nào?
- PostSharp là một post-compiler. Khi bạn thêm references đến những dll của PostSharp, nó sẽ tự động thêm phần code Aspect của bạn vào lúc bạn build chương trình. Có nghĩa là code chính của chương trình vẫn không đổi nhưng ouput assembly lại bị PostSharp biến đổi. PostSharp được tích hợp vào process build của MSBuild trong Visual Studio .NET 2005 và 2008, thậm chí nó được tích hợp vào bất kì IDE nào sử dụng MSBuild như SharpDevelop.
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[PostSharp AOP Platform] body=[Cách PostSharp hoạt động] PostSharp AOP](http://img145.imageshack.us/img145/7597/postsharpdd2.png)
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Theo như tác giả giới thiệu thì PostSharp platform có thể được sử dụng lúc Runtime như Spring AOP thay vì lúc compile nhưng tôi vẫn chưa có thời gian tìm hiểu sâu vào chức năng này. Trong phạm vi bài viết này, chúng ta sẽ tìm hiểu khái quát xem PostSharp có thể giúp gì khi muốn lập trình AOP, và làm một sample đơn giản để hiện thực điều đó.
![50 :bbpraroi:](http://www.igame.vn/f/images/smilies/BBP_smilies/50.gif)
# II. Lập trình AOP với PostSharp
- Để sử dụng PostSharp, ta có hai lựa chọn. Hoặc là các bạn download installer của PostSharp và install vào máy mình. Khi đó trình Installer của PostSharp sẽ làm hết mọi việc cho ta, từ đăng kí dll của PostSharp vào hệ thống đến cả tích hợp file help của nó vào VS.NET. Khi bạn build project có sử dụng dll của PostSharp, các bước biến đổi assembly lúc runtime sẽ tự động được gọi và ta không cần quan tâm đến nó.
- Lựa chọn thứ hai là ta chỉ download các dll của PostSharp, tạo project AOP bình thường và không cần Install bất cứ cái gì vào máy. Tuy nhiên bù lại ta phải thực hiện một số thay đổi vào file “csproj” của solution để buộc PostSharp hoạt động lúc ta build chương trình. Sample trong bài viết này sẽ thực hiện theo cách này vì tôi không thích cài bất cứ cái gì vào máy trong khi có thể làm được vói cách khác.
![23 :bbpnodo:](http://www.igame.vn/f/images/smilies/BBP_smilies/23.gif)
- Download những thứ cần thiết của PostSharp ở đây:
[http://www.postsharp.org/download/](http://www.postsharp.org/download/)
- Download PostSharp sample code ở đây:
[http://nthoaiblog.googlepages.com/SamplePostSharpAOP.zip](http://nthoaiblog.googlepages.com/SamplePostSharpAOP.zip)
- Khi lập trình AOP với PostSharp, ngoài những việc linh tinh như add references, sửa file project “csproj” nếu cần thiết,.. thì ta cần 2 bước chính.
## Bước 1: Tạo class Aspect
- Tạo ra class Aspect của chúng ta thực sự là ra một class Attribute. Class Aspect này sẽ phải “derived” một trong các Aspect base classes của PostSharp như: OnExceptionAspect, OnMethodBoundaryAspect, OnMethodInvocationAspect, …
- Nếu không “derived” các base class này, ta có thể implement một trong những interface tương ứng như IOnExceptionAspect, IOnMethodBoundaryAspect, IOnMethodInvocationAspect, …
- Class Attribute của ta khi implement 1 interface thích hợp sẽ có vài trò và khả năng tương ứng với interface mà nó implement. Ví dụ khi ta tạo một class Attribute implement interface OnMethodBoundaryAspect, ta sẽ phải overwrite các method như OnEntry, OnExit, … Code của method OnEntry sẽ được PostSharp compiler “gắn” vào phần đầu của bất cứ function nào được khai báo bằng Attribute ta đang tạo ra; và code của method OnExit sẽ được “gắn” vào cuối cùng của bất cứ function nào được khai báo bằng Attribute này. Khá đơn giản và khá giống với khi ta implement interface
[ImethodInterceptor](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-spring-net.html#SpringNetAOPp1Code2)trong Spring.NET AOP đúng không? Tôi nghĩ rằng không cần giải thích thêm thì bạn cũng biết khi implement OnExceptionAspect sẽ tạo ra một Attribute để dùng trong trường hợp nào. Trong file chm document của PostSharp có đề cập rất chi tiết từng lớp như trên và ta có thể tham khảo để tìm ra class mình cần. Bây giờ hãy xem một lớp Attribute trong sample được implement như thế nào: ![19 :bbpxtay:](http://www.igame.vn/f/images/smilies/BBP_smilies/19.gif)
public sealed class AroundLoggingAttribute : OnMethodBoundaryAspect
{
private ConsoleColor _color = ConsoleColor.Green;
private ConsoleColor _currentColor = Console.ForegroundColor;
public AroundLoggingAttribute(ConsoleColor outputColor)
{
_color = outputColor;
}
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
Console.ForegroundColor = _color;
Console.WriteLine("Entering {0}.", eventArgs.Method);
Console.ForegroundColor = _currentColor;
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Console.ForegroundColor = _color;
Console.WriteLine("Leaving {0}.\n", eventArgs.Method);
Console.ForegroundColor = _currentColor;
}
}
Code 1
Theo như hướng dẫn của tác giả Nuvio thì ta nên khai báo lớp là seal và sử dụng attribute Serializable. Lớp AroundLoggingAttribute đã derived từ class OnMethodBoundaryAspect. Nó implement 2 method OnEntry và OnExit, print ra console một số message với màu được dùng từ màu ở constructor. OK, bây giờ ta sẽ sang bước thứ hai
## Bước 2: Viết các xử lý của chương trình, sử dụng Attribute vừa được tạo:
- Trong Sample này, tôi cũng tạo ra một lớp ServiceCommand, chỉ print ra console vài message đơn giản, mỗi method của nó được khải báo với Attribute: [AroundLogging]. Các bạn chú ý tôi có sử dụng constructor cho Attribute để sử dụng các màu khác nhau cho 2 method SayHello và SayBye.
{
[AroundLogging(ConsoleColor.Green)]
public void SayHello()
{
Console.WriteLine("Hello Dudes!");
}
[AroundLogging(ConsoleColor.Red)]
public void SayBye()
{
Console.WriteLine("Bye. Visit:
**http://nthoai.blogspot.com**");
}
}
Code 2
- Ta có thể không cần khai báo trên mỗi function, nếu ta muốn Attribute này được apply hết cho toàn bộ class thì chỉ cần khai báo một lần Attribute này cho class ServiceCommand. Thậm chí, nếu muốn apply cho toàn bộ class trong một Assembly, thì có thể modify file AssemblyInfo.cs để làm điều đó.
![08 :bbpnen:](http://www.igame.vn/f/images/smilies/BBP_smilies/08.gif)
## Bước 3: Điều chỉnh file
**csproj** để PostSharp compiler được thêm vào quá trình build.
(
*Bước này sẽ không cần thiết nếu bạn sử dụng installer của PostSharp*)
- Vì tôi không install PostSharp mà chỉ sử dụng những dll của platform này, tôi buộc phải chỉnh file csproj chính của solution để buộc PostSharp build và đưa Aspect AroundLogging vào assembly của ServiceCommand như đã khai báo.
- Tôi mở file PostSharpSample.ConsoleApp.csproj (đây là file project chính của solution), tìm đến dòng ở gần cuối:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
Và thêm vào
<Import Project="..\..\buidscript\PostSharp-1.0.targets" />
<Import Project="..\..\buidscript\
**PostSharp-1.0.targets**" />
Hình 2
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Các bạn chú ý rằng tôi sử dụng đường dẫn tương đối để chỉ đến file PostSharp-1.0.targets. Các bạn có thể copy thư mục buildscript đến một nơi nào đó trong máy, sau đó sửa lại đường dẫn này theo đường dẫn mới, reload project khi VS.NET yêu cầu, save solution, đóng solution rồi mở lại solution và build lại lần nữa để chạy được chương trình. Nếu để ý bạn sẽ thấy quá trình build sẽ hơi chậm hơn bình thường.
![29 :bbpskien:](http://www.igame.vn/f/images/smilies/BBP_smilies/29.gif)
- Nếu vẫn chưa “clear” hoặc muốn tìm hiểu thêm những config này, hãy đọc chương đầu trong file document chm của PostSharp. Nói chung thì ta nên để thư mục chứa các file build ở một nơi nào đó trong máy, chỉ chỉnh file project lúc cần thiết là được. Và nếu muốn đơn giản hơn thì install PostSharp cho lành.
![17 :bbpcuoi3:](http://www.igame.vn/f/images/smilies/BBP_smilies/17.gif)
Và đây là kết quả khi chạy chương trình:
Entering void SayHello()
Hello Dudes!
Leaving void SayHello().
Entering void SayBye()
Bye. Visit: http://nthoai.blogspot.com
Leaving void SayBye().
Hình 3
Nếu bạn dùng chương trình
[.NET Reflector](http://downloads.red-gate.com/reflector.zip)để đọc assembly thì sẽ thấy code của chúng ta đã bị sửa đổi như hình ![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[PostSharp AOP Platform] body=[Code IL đã bị PostSharp platform thay đổi, xem bằng .NET Reflector] PostSharp AOP](http://img145.imageshack.us/img145/6135/postsharpsamplereflectoib4.png)
# III. Kết luận
- PostSharp có vẽ dễ sử dụng, bạn có thể viết nhiều plugin của mình để gắn vào chương trình bằng cách implement các Interface đơn giản. Tuy tác giả giới thiệu PostSharp có thể hoạt động lúc Runtime nhưng tôi vẫn chưa chắc về cái Runtime của PostSharp. Có lẽ PostSharp không flexible như AOP của Spring.NET nhưng community của PostSharp có làm
[các project cuả họ](http://www.postsharp.org/community/projects/)để kết hợp PostSharp với các framwork nổi tiếng như [Spring.NET](http://nthoai.blogspot.com/search/label/Spring.NET), [Entity Framework](http://msdn.microsoft.com/en-us/library/bb399572.aspx), ... Nói chung có đồ free xài là vui rồi, tuy nhiên ai có lòng tốt có thể donate cho bác Nuvio ở đây: [http://www.postsharp.org/donate/](http://www.postsharp.org/donate/) ![19 :bbpxtay:](http://www.igame.vn/f/images/smilies/BBP_smilies/19.gif)
##
Posted On Friday, September 19, 2008 at
at
[Sử dụng AOP với Spring.Net p2](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-springnet-p2.html)
[5:27 PM](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-springnet-p2.html)by Van Nguyen [](https://www.blogger.com/post-edit.g?blogID=3978293580246860892&postID=3032279998666111104&from=pencil)
- Trong
[bài viết trước](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-spring-net.html), chúng ta đã có cơ hội làm quen với AOP và cách sử dụng AOP trong Spring.NET với những khái niệm về Advice, Pointcut. Tất nhiên còn nhiều thứ khác được hỗ trợ để lập trình AOP với Spring.NET. Bài viết này sẽ tiếp tục giới thiệu các loại Advice khác nhau, các Pointcuts khác mà ta có thể sử dụng.
# I.Có nhiều loại Advice được hỗ trợ sẵn trong Spring.Net
- Ở code sample
[trong bài viết trước](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-spring-net.html), Advice được sử dụng là “around advice”. Đôi lúc, chúng ta chỉ muốn thêm “custom action” vào trước hoặc sau khi xử lý logic chính trong function của chúng ta được thực hiện. Tất nhiên sử dụng “around advice” cũng được thôi, nhưng có thể gây “confused” cho người khác khi đọc code. Nếu xem lại code của class ConsoleLoggingAroundAdvice và muốn sửa lại để nó chỉ thực hiện việc thông báo mỗi lần method chính được gọi thì sao? Thật ra ta chỉ cần thực hiện một “custom action” trước khi xử lý chính được gọi (trong trường hợp này, ta cần print ra màn hình một message thông báo tên của method sắp được gọi chẳng hạn). Người ta thường khuyên rằng chỉ nên sử dụng những gì ta cần (Ý tưởng gần giống với nguyên tắc ISP – [Interface Segregation Principle](http://www.objectmentor.com/resources/articles/isp.pdf)) và Spring.NET có một loại Advice khác với “around advice” mà ta có thể dùng được. Nếu chỉ cần làm gì đó trược ghi gọi hàm chính, cách đơn giản nhất là sử dụng “before advice”.
## I.1 Before advice
- “before advice” là … một advice chạy trước khi method chính được gọi. Vì thế nó không thể và không nên return cái gì cả. Thực sự thì đó là một điều tốt bời vì bạn sẽ không bao giờ vô ý quên gọi function Proceed() để thực hiện xử lý logic chính; và bạn cũng sẽ không bao giờ quên return kết quả trả về của method chính. Nếu ta không cần đọc hoặc sửa đổi giá trị trả về và cũng không cần làm một thao tác gì đặc biệt sau khi hàm chính thực hiện thành công thì “before advice” là tất cả những gì ta cần.
- “before advice” trong Spring.NET được sử dụng bằng cách implement ImethodBehoreAdvice. Hãy xem code sample sau đây:
{
public void Before(MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine("Intercepted call to this method : " + method.Name);
Console.Out.WriteLine(" The target is : " + target);
Console.Out.WriteLine(" The arguments are : ");
if ( args != null )
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
}
}
Code 1
- Tương tự như lần trước, ta xem cách “apply” advice mới này vào lớp ServiceCommand bằng code như thế nào:
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 2
Và kết quả khi chạy chương trình
Intercepted call to this method : Execute
The target is : Spring.Examples.AopQuickStart.ServiceCommand
The arguments are :
Hình 1
- Kết quả hiện đúng như những gì chúng ta mong muốn. Như vậy sử dụng “before advice” sẽ dễ dàng hơn và chúng ta sẽ không phải nhớ gọi Proceed() ở hàm chính cũng như return value nếu có. Sau đây là config XML cho “before advice”:
type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 3
## I.2 After advice
- Tương tự như “before advice”, “after advice” là advice được thực hiện sau khi method chính của chúng ta được thực thi. Khi sử dụng “after advice” trong Spring.NET, ta phải implement IAfterReturningAdvice. Hãy xem đoạn code sample cho lớp ConsoleLoggingAfterAdvice
{
public void AfterReturning(
object returnValue, MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine("This method call returned successfully : " + method.Name);
Console.Out.WriteLine(" The target was : " + target);
Console.Out.WriteLine(" The arguments were : ");
if ( args != null )
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
Console.Out.WriteLine(" The return value is : " + returnValue);
}
}
Code 4
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 5
- Và đây là kết quả khi chạy chương trình
This method call returned successfully : Execute
The target was : Spring.Examples.AopQuickStart.ServiceCommand
The arguments were :
The return value is : null
Hình 2
- Output của console cho ta thấy advice trên đã được gọi sau khi method chính được gọi. Cũng như “before advice” khi sử dụng “after advice” chúng ta cũng không sợ phải quên gọi method Proceed() để thực hiện xử lý chính. Tuy nhiên chúng ta có thể access đến giá trị trả về của Method chính (nếu có). Mặc dù không cần phải return giá trị trả về này như khi dùng “around advice”, chúng ta vẫn có thể thay đổi giá trị của nó.
- Có thể nói sử dụng kết hợp “before advice” và “after advice” sẽ tránh những lỗi vô ý như khi dùng “around advice”. Sau đây là config XML để khai báo “after advice”:
type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 6
## I.3 Throws advice
- 3 loại advice đã bàn tới có thể nói đã thõa mãn mọi yêu cầu khi lập trình AOP. Tuy nhiên, vẫn còn một loại khác cũng rất hữu ích: “throws advice”
{
}
Code 7
- Như tên gọi của nó, “throws advice” sẽ được gọi khi một “advised method” tung ra một exception. Do đó nếu “advised method” của chúng ta chạy trơn tru không văng lỗi gì thì Advice loại này sẽ không được gọi đến. Chúng ta có thể sử dụng “throws advice” để quản lý lỗi trong chương trình bằng cách apply nó cho tất cả các class ta muốn; chẳng hạn ta có thể ghi log lại khi có lỗi, hoặc email cho support team khi có lỗi xảy ra…
- Để sử dụng “throws advice” trong Spring.NET, ta phải implement IthrowsAdvice. Hãy xem code sample dưới đây:
{
public void AfterThrowing(Exception ex)
{
Console.Out.WriteLine("Advised method threw this exception : " + ex);
}
}
Code 8
- Để test được Advice loại này, hãy thử sửa lại code của function Excute() như sau:
{
public void Execute()
{
throw new UnauthorizedAccessException();
}
}
Code 9
- Cách apply “throws advice” của chúng ta bằng code cũng tương tự như những loại khác:
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 10
- Đây là kết quả khi chạy chương trình:
Advised method threw this exception : System.UnauthorizedAccessException:
Attempted to perform an unauthorized operation.
Hình 3
- Trong Spring.NET, sử dụng “throws advice” có nghĩa là bạn phải tạo một lớp implement IthrowsAdvice, sau đó, cho mỗi loại Exception bạn muốn lớp advice này có thể bắt được (tương tự như ý tưởng try – catch), ta phải khai báo một method với tham số Exception như sau:
Code 11
- Tên của method là AfterThrowing là cố định và không thể thay đổi, nhưng ta có thể tạo các overloading khác nhau tương ứng với các loại Exception ta muốn advice của mình có thể bắt được:
Code 12
- Sau đây là config XMl cho throwsAdvice:
type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 13
## I.4 Layering advice
- Nếu chúng ta apply nhiều advice vào trong cùng một đối tượng mong muốn, các advice đó sẽ được gọi theo đúng thứ tự được thêm vào, điều này hiển nhiên đúng với các advice cùng một loại. Nếu nhiều advice với các loại khác nhau được sử dụng, thì những advice sẽ được gọi theo nguyên tắc đơn giản “before advice” sẽ gọi trước “after advice” cho dù after around được thêm vào trước. Nếu xét ví dụ code sample dưới đây thì thứ tự được gọi sẽ là BeforeAdvice, AroundAdvice, xử lý chính, AroundAdvice, AfterAdvice:
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();
Code 14
- Và sau đây là config xml cho tất cả các advice, các advcie sẽ được “gắn” theo đúng thứ tự ta khai báo trong file congig:
type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/>
<object id="afterAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/>
<object id="beforeAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/>
<object id="aroundAdvice"
type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
<value>afterAdvice</value>
<value>beforeAdvice</value>
<value>aroundAdvice</value>
</list>
</property>
</object>
Code 15
- Ngoài ra, ta có thể implement thêm IOrdered cho các lớp Advice để buộc chúng thực thi theo thứ tự mong muốn.
# II. Sử dụng Attributes để định nghĩa Pointcuts
- Các Pointcut có thể được xác định dựa vào một Attribute đánh dấu trên một “advised method”. Nhờ đó, một Advice loại này có thể đọc thông tin từ Attribute của Method và thực hiện một số thao tác nào đó. Lớp AttributeMatchMethodPointcut của Spring.NET đã hỗ trợ chúng ta chức năng này, ví dụ sau đây sẽ tìm tất cả các methods nào được khai báo với attribute ConsoleLogging (đây là lớp Attribute người dùng tạo ra, không phải lớp built-in của Spring hay của .NET framework) và gọi thực hiện xử lý của ConsoleLoggingAdvice trước khi thực hiện xử lý logic chính.
Sau đây là code của class ConsoleLoggingAttribute là một class Attribute được sử dụng khi khai báo các method trong chương trình AOP:
{
private ConsoleColor _color = ConsoleColor.Gray;
// Gets or sets the foreground color of the console.
public ConsoleColor Color
{
get { return _color; }
set { _color = value; }
}
public ConsoleLoggingAttribute(ConsoleColor color)
{
_color = color;
}
public ConsoleLoggingAttribute()
{
}
}
Code 16
- Ta tạo một lớp command khác tên là AnotherCommandService, method Excute của nó sẽ được đánh dấu bằng Attribute “ConsoleLogging” vừa được tạo ra ở trên:
{
[ConsoleLogging(Color = ConsoleColor.Green )]
public void Execute()
{
Console.Out.WriteLine("--- AnotherServiceCommand implementation : Execute()... ---");
}
}
Code 17
- Kế tiếp, ta sẽ tạo ra một lớp Advice khác, lớp Advice này sẽ đọc thông tin từ Attribute của “advised method” tương ứng và từ đó print text ra console với các màu sắc khác nhau:
{
private ConsoleColor _color = ConsoleColor.Gray;
public ConsoleColor Color
{
get { return _color; }
set { _color = value; }
}
public object Invoke(IMethodInvocation invocation)
{
ConsoleLoggingAttribute[] consoleLoggingInfo =
(ConsoleLoggingAttribute[])invocation.Method.GetCustomAttributes(typeof(ConsoleLoggingAttribute), false);
if (consoleLoggingInfo.Length > 0)
{
Color = consoleLoggingInfo[0].Color;
}
ConsoleColor currentColor = Console.ForegroundColor;
Console.ForegroundColor = Color;
Console.Out.WriteLine(String.Format(
"Intercepted call : a.Out to invoke method '{0}'", invocation.Method.Name));
Console.ForegroundColor = currentColor;
object returnValue = invocation.Proceed();
Console.ForegroundColor = Color;
Console.Out.WriteLine(String.Format(
"Intercepted call : after excuting method, returned '{0}'", returnValue));
Console.ForegroundColor = currentColor;
return returnValue;
}
}
Code 18
- Và đây là config xml để kết hợp tất cả các thành phần trên:
type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
</property>
<property name="Attribute"
value="Spring.AopQuickStart.Attributes.ConsoleLoggingAttribute, Spring.AopQuickStart.AttributePointcut" />
</object>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 19
- Kết quả trên màn hình khi chạy chương trình:
Intercepted call : about to invoke method 'Excute'
--- AnotherServiceCommand implementation : Execute()... ---
Intercepted call : after excuting method, returned ''
Hình 4
- Download sample code ở đây:
[http://nthoaiblog.googlepages.com/SampleSpringAOPAttributePointCut.zip](http://nthoaiblog.googlepages.com/SampleSpringAOPAttributePointCut.zip)
# III. Custom pointcuts:
- Trong Spring.NET ta có thể định nghĩa những pointcut cho chính mình bằng cách overwrite method Match của superclass StaticMethodMatcherPointcut:
{
public override bool Matches(MethodInfo method, Type targetType)
{
// return true if YOUR custom criteria match
}
}
Code 20
# IV.Spring.NET có nhiều chức năng rất hay mà tôi vẫn đang học. Bài viết trước và bài này tôi dịch và có thêm một số ý khác để người mới tìm hiểu có thể nắm bắt nhanh hơn. Tuy vậy vẫn có nhiều chức năng khác như: Introduction, TargetSource, … trong Spring AOP mà tôi không đề cập trong 2 bài viết này và xin để bạn tự tìm hiểu. Cuối cùng, xin cám ơn những ai đã đọc và comment.
##
Posted On Wednesday, September 17, 2008 at
at
[Sử dụng AOP với Spring.Net](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-spring-net.html)
[6:57 PM](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-spring-net.html)by Van Nguyen [](https://www.blogger.com/post-edit.g?blogID=3978293580246860892&postID=2959606668114838378&from=pencil)
# I. AOP là cái gì?
- Ai chẳng biết AOP là từ viết tắt của Aspect Oriented Programming, mấy bác thích dịch ra tiếng Việt thì bảo rằng AOP là “phương pháp lập trình hướng khiá cạnh”. Có lẽ theo cách hiểu ấy nên không ít lần tôi nhìn thấy mấy cái slide kiểu trò chơi ghép hình trong các seminar về AOP hồi học đại học. Lúc đó thì OOP còn chưa lĩnh hội hết chứ đừng nói AOP, bởi vậy xem xong cũng chẳng hiểu chi cả.
![17 :bbpcuoi3:](http://igame.vn/f/images/smilies/BBP_smilies/17.gif)
![ AOP http://nthoai.blogspot.com](http://img139.imageshack.us/img139/6633/aopmm8.png)
- Trong khi xây dựng các chương trình ứng dụng, chúng ta sẽ có rất nhiều những vấn đề liên quan đến phần mềm mà ta phải quan tâm. Tôi xin lấy ví dụ sau đây: tôi phải viết một chương trình để upload files, folders lên một server. Vấn đề khá đơn giản phải không? Nếu object được chọn là file thì chỉ việc upload cái file đó lên. Còn nếu object được chọn là một folder thì upload hết các file con bên trong folder đó, sau đó lại duyệt các folder con của nó và gọi hàm đệ quy. Hê hê, nhưng công việc không chỉ có vậy, tôi được yêu cầu phải thêm nhiều cái linh tinh khác như phải kiểm tra xem 1 folder, 1 file có tồn tại chưa, phải kiểm tra xem user hiện tại có đủ quyền hay không, phải ghi mọi thao tác ghi xóa hay thông báo lỗi xuống file log, phải hiển thị process bar, phải thế này, phải thế kia. Kết quả là hàm upload của tui trở thành một đống bùi nhùi, phần code phục vụ cho việc đọc, ghi file chỉ có vài dòng trong khi những code cho các việc kiểm tra, thông báo lỗi, hiển thị process bar chiếm rất nhiều nội dung của một hàm. Ngoài ra code của tôi bị phụ thuộc lẫn nhau ở nhiều nơi, chẳng hạn như những xử lý đọc ghi file do cần phải update process trên process bar nên nó lại phải reference đến project winform, đúng là rồi hơn chữ “RỐI”.
![21 :bbptuc:](http://igame.vn/f/images/smilies/BBP_smilies/21.gif)
- Trong chương trình này, vấn đề quan tâm chính của tôi là upload folder/files và bên cạnh đó sẽ có một số vấn đề khác cần phải thực hiện. Các bạn có thể thấy rằng trong khi viết phần mềm, ta thường gặp hai loại “concern”: core concerns và cross-cutting concerns:
+ Core concern: là requirement chính của chương trình, ví dụ như upload file/folder, đọc danh sách user, …
+ Cross-cutting concerrns: là những xử lý phụ cần được thực hiện khi “core concern” được gọi. Cross-cuttong concerns thường xảy ra nhiều nơi trong chương trình, nó có thể xảy ra trong nhiều layer của ứng dụng, nhiều class, nhiều method; chẳng hạn như tính toán thời gian chạy các hàm đọc ghi database, ghi log lại mỗi lần cập nhật thông tin user, …
- Đã Từ lâu, người ta đã nghĩ ra chiêu thức AOP để giải quyết vấn đề trên. Aspect-Oriented Programming(AOP) còn được gọi là Aspect-Oriented Software Development (AOSD) là một nguyên tắc thiết kế giúp tách rời các yêu cầu hay các vấn đề được quan tâm (separation of concerns) trong chương trình thành các thành phần độc lập và từ đó tăng tính uyển chuyển cho chương trình. “Separation of concerns” là một trong những kĩ thuật được quan tâm nhất trong ngành phần mềm. Người ta cho rằng những vấn đề tương tự nhau nên được giải quyết trong một “unit of code”. Khi lập trình thủ tục, một “unit of code” là 1 function, 1 method. Còn trong lập trình hướng đối tượng thì “unit of code” là một class.
![50 :bbpraroi:](http://igame.vn/f/images/smilies/BBP_smilies/50.gif)
# II. Những lợi ích của “separate of concerns”
- Trong AOP, từ “Aspect” chính là vấn đề [concern] người lập trình quan tâm và nó xuất hiện trong rất nhiều class cũng như nhiều method khác nhau. Kĩ thuật AOP thường được sử dụng để giải quyết các vấn đề như caching, tracking, security hay failure injections. Vì thế, nhiều tài liệu nói rằng AOP giúp module hóa ứng dụng, biến chương trình thành các module hoạt động độc lập, mỗi module làm một chức năng riêng, từ đó dễ bảo trì và nâng cấp.
![50 :bbpraroi:](http://igame.vn/f/images/smilies/BBP_smilies/50.gif)
- Ở vai trò của người thiết kế phần mềm, chúng ta nên đưa ra các cách làm đơn giản nhất. Để thỏa mãn yêu cầu của chương trình, người ta sẽ tạo ra thành phần chính của chương trình (gồm các class/component/method); các chức năng bổ sung như loging, tính toán performance, .. cũng sẽ được xem xét để tạo ra. Vì do các chứng năng bổ xung này không phải là yêu cầu chính của hệ thống nên người ta sẽ có yêu cầu bật tắt chúng theo ý muốn. Vậy làm thế nào để có thể tạo ra chương trình có thể linh hoạt được như thế? Câu trả lời là “Separate of concerns”
- Ở vai trò của người lập trình, chúng ta có hai vấn đề cần quan tâm là xử lý logic chính của chương trình và các xử lý logic cho những thành phần phụ. Do đó tất nhiên sẽ phải tạo ra các class/method cho các yêu cầu thực sự, và tạo ra những class/method khác độc lập để thực hiện các yêu cầu phụ kia. Tất cả các class/method này có thể được kết hợp lúc runtime theo ý muốn. Chẳng hạn như trong môi trường test, người ta có thể bật chức năng log, đo đạc performance để theo dõi nhưng khi cài đặt trên môi trường Production, các chức năng phụ này có thể được disable. Và trên nguyên tắc, trong code của những xử lý logic chính sẽ không có code để thực hiện các yêu cầu phụ.
- Tóm lại, ta có thể liệt kê một số lợi ích như sau:
+ Chức năng chính của chương trình không cần biết đến các chức năng phụ khác
+ Các chức năng phụ có thể được thêm thắt, bật tắt lúc runtime tùy theo yêu cầu
+ Các thay đổi, sửa lỗi, nâng cấp nếu có đối với các chức năng phụ sẽ không ảnh hưởng đến chương trình chính.
+ Hệ thống sẽ uyển chuyển và giảm thiểu tính phụ thuộc lẫn nhau của các module
![19 :bbpxtay:](http://igame.vn/f/images/smilies/BBP_smilies/19.gif)
# III. Các thuật ngữ thường gặp trong AOP
+ Join point: Một điểm trong chương trình. Ví dụ như ta cần ghi log lại sau khi chạy method thì điểm ngay sau method đó được thực thi gọi là một jointpoint để có thể chạy những xử lý ghi file log. Join point theo nghĩa của nó có thể hiểu là những nơi có thể được chèn những “custom action” của bạn.
+ Pointcut: Có nhiều cách để xác định joinpoint, những cách như thế được gọi là pointcut.
+ Advice: là những xử lý phụ được thêm vào xử lý chính. Code để thực hiện các xử lý đó được gọi là Advice. Vì các xử lý phụ có thể thêm vào trước hoặc sau hoặc cả hai đối với những xử lý chính nên có nhiều loại Advice khác nhau mà ta sẽ đề cập trong những phần sau.
# IV. AOP trong Spring.NET
- AOP là một trong những chức năng chính được hỗ trợ trong Spring.NET. Với AOP framework của Spring.NET bạn có thể thoải mái thêm thắt các “custom action” (Advice) của mình vào một hay nhiều function tại những vị trí (Joinpoint) mong muốn tùy theo những trường hợp cụ thể (Pointcut). Trong bài viết này, tôi sẽ trình bày một sample code thể hiện ví dụ đơn giản để sử dụng AOP framework của Spring.NET. Nếu bạn đã biết về
[Dependency Injection sử dụng Spring.NET](http://nthoai.blogspot.com/search/label/Dependency%20Injection)thì bạn sẽ thấy rằng AOP của Spring.NET cách sử dụng gần như tương tự. ![29 :bbpskien:](http://igame.vn/f/images/smilies/BBP_smilies/29.gif)
## IV.1 Sử dụng Advice
- Xin nhắc lại điều này: tôi gọi Advice là những “custom action” mà bạn thêm vào chương trình của mình. Cụ thể hơn nữa, Advice là code bạn viết để xử lý những “custom action” đó, và tất nhiên theo nguyên tắc của AOP thì những code này không hề phụ thuộc vào chương trình chính. Có thể nói rằng bạn sẽ tạo một project độc lập với các project có sẵn trong solution của bạn, sau đó thêm vào các class, các method để xử lý các “custom action”. Tất nhiên các class Advice sử dụng AOP của Spring phải được viết theo quy tắc của Spring.NET AOP Framework, nó sẽ phải implement interface IMethodInterceptor (Có nhiều interface tương tự ứng với các loại Advice khác nhau). Và cuối cùng, các Advice mà bạn viết sẽ được gắn vào nơi cần thiết dựa vào xml config của Spring.NET.
- Chúng ta hãy xem một ví dụ đơn giản để thấy Advice hoạt động như thế nào.
[Code sample dưới đây](#SpringNetAOPp1Code1)sẽ thêm chức năng print vài messages ra console trước và sau khi method chính được gọi. Do Advice được hiểu như một “custom action” cho một method nào đó (các bạn chú ý đối tượng được thêm “custom action” thường là các method, và chúng được hiểu là các “target of the advice”), nên trong các tài liệu ta sẽ thấy ghi là “advised method”. Như vậy “advised method” sẽ là method chính, còn các “Advice” chứa các xử lý được thêm vào “advised method”. Cũng phải thừa nhận rằng sample dưới đây không thể hiện hết tất cả khả năng của AOP, nhưng nó sẽ giúp bạn hiểu rõ về khái niệm Advice trong AOP. Và khi đã nắm được rồi, bạn sẽ có thể apply những Advice của chính bạn viết cho những chương trình của bạn.
- Trước khi xem
[code của Advice trong sample này](#SpringNetAOPp1Code2), chúng ta hãy xem qua các domain classes dưới đây. Khi method Excute() trong class ServiceCommand được gọi, nó sẽ print ra console một thông báo đơn giản. Class ServiceCommand này sẽ là “target of the advice”, method “Excute()” sẽ là “advised method”; trong các thuật ngữ của Spring.NET AOP, một instance của class ServiceCommand sẽ được gọi là “advised object”. Các bạn chú ý rằng interface ICommand và class ServiceCommand là những class và interface dùng để làm ví dụ chúng không phải là các type của Spring.AOP. Có thể bạn sẽ thắc mắc tại sao phải tạo interface ICommand rồi để class ServiceCommand implement nó, câu trả lời là để tăng tính “Loosed coupling”. Ngoài ra, cách sử dụng AOP trong Spring.NET rất giống với [DI trong Spring](http://nthoai.blogspot.com/search/label/Dependency%20Injection), các object (trong trường hợp này là instance của ServiceCommand”) được Spring tạo ra theo cách bạn config trong file xml nên trong chương trình chính ta sẽ sử dụng ICommand để khai báo thay vì khai báo một class cụ thể ServiceCommand.
{
object Execute(object context);
}
public class ServiceCommand : ICommand
{
public object Execute(object context)
{
Console.Out.WriteLine("Service implementation : [{0}]", context);
return null;
}
}
Code 1
- Đoạn code phía dưới đây sẽ là code của một Advice, Advice này sau đó sẽ được “gắn” vào method Excute() của class ServiceCommand. Như tôi đã giới thiệu, có nhiều loại Advice khác nhau, trong ví dụ này ta sẽ tạo một “Around Advice”, nghĩa là ta có thể thêm những “custom action” vào cả trước và sau khi gọi method Excute() của class ServiceCommand. (Tham khảo
[Spring.NET Section 13.3.2 “Advice types”](http://www.springframework.net/doc-latest/reference/html/aop.html#aop-introduction-advice-types))
{
public object Invoke(IMethodInvocation invocation)
{
Console.Out.WriteLine("Advice executing; calling the advised method...");
object returnValue = invocation.Proceed();
Console.Out.WriteLine("Advice executed; advised method returned " + returnValue);
return returnValue;
}
}
Code 2
- Đến lúc này ta đã tạo ra 3 thành phần: interface (ICommand); class (ServiceCommand); và một Advice: class (ConsoleLoggingAroundAdvice). Phần còn lại là làm sao để “gắn” Advice này vào method Excute() của class ServiceCommand. Sau đây là cách kết nối các thành phần trên bằng code.
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute("This is the argument");
Code 3
Và đây là kết quả khi chạy chương trình
Advice executing; calling the advised method...
Service implementation : [This is the argument]
Advice executed; advised method returned
Hình 1
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Output của chương trình cho ta thấy rằng Advice của chúng ta đã được applied “around” phần xử lý logic của “advised method” Excute()
- Có thể bạn sẽ thắc mắc ProxyFactory là cái gì ở đây? Constructor của class ProxyFactory sử dụng một argument đầu vào là kiểu của một object ta muốn thêm vào những Advice (trong sample này, thì argument cho hàm khởi tạo là instance của class ServiceCommand). Sau đó ta sẽ thêm các Advice (một instance của class ConsoleLoggingAroundAdvice) bằng cách sử dụng method AddAdvice(). Kế tiếp ta gọi method GetProxy() để trả về một proxy (AOP proxy), proxy này có thể gọi là một object hành nhái thuộc kiểu ICommand. Khi gọi method Excute() của proxy này, các xử lý của Advice và “advised method” sẽ lần lược được thực hiện. Hình bên dưới thể hiện quy trình hoạt động của Spring.NET AOP proxy
![cssbody=[boxHoverBody] cssheader=[boxHoverHeader] header=[Spring.NET AOP] body=[Quy trình hoạt động của Spring.NET AOP proxy] Spring.NET AOP](http://www.springframework.net/doc-latest/reference/html/images/aop-chain.png)
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Có một điểm đáng lưu ý là AOP proxy trả về khi gọi method GetProxy() được cast về kiểu ICommand. Hiện nay, có vẽ như chúng ta buộc phải làm như vậy cho các “advised object”. Nói theo cách khác để cho các class của bạn có thể sử dụng được với AOP của Spring.NET, các class đó phải implement ít nhất một interface. Trong thực tế thì đòi hỏi này không đến nỗi phiền hà như vậy vì nói chung ta nên sử dụng interface khi có thể. (Chức năng hỗ trợ AOP cho class mà không cần implement interface đang được Spring hứa hẹn đưa ra trong các phiên bản sau).
- Khi sử dụng AOP của Spring.NET, ta sẽ không cần phải “gắn” các Advice
[bằng code theo kiểu ở trên](#SpringNetAOPp1Code3). Đoạn config xml dưới đây sẽ làm y chang những gì phần [code 3](#SpringNetAOPp1Code3)thực hiện:
<object id="consoleLoggingAroundAdvice"
type="Spring.Examples.AopQuickStart.consoleLoggingAroundAdvice"/>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 4
command.Execute();
Code 5
- Lại có một số điểm cần lưu ý đối với đoạn config xml này; đoạn config này được thêm vào file config của chương trình (app.config hoặc web.config). Trong toàn bộ hệ thống thì ConsoleLoggingAroundAdvice bản thân nó cũng chỉ là một object thông thường, và vì thế nó được config theo cách thông thường như những class khác. Do đó nếu chúng ta muốn “inject” nó bằng những kĩ thuật như DI của Spring.NET thì cách làm cũng bình thường.
- Điểm lưu ý thứ 2: Class ProxyFactoryObject là một class implement từ IFactoryObject interface, nó có nhiệm vụ tạo ra một proxy object tương ứng với “target” được config. Trong trường hợp này, ta muốn chương trình của mình sẽ tạo ra một proxy của class ServiceCommand nên “target” sẽ được khai báo là namespace class ServiceCommand.
- Cuối cùng, ta nên lưu ý rằng Advice sẽ được “gắn” vào target object bằng cách khai báo trong phần interceptorNames. Trong ví dụ này, chỉ có một Advice được khai báo, nếu như có thêm các class Advice khác, chỉ việc thêm xml config cho chúng và thêm vào list của interceptorNames.
Các bạn có thể download code sample ở đây:
[http://nthoaiblog.googlepages.com/SampleSpringAOP.zip](http://nthoaiblog.googlepages.com/SampleSpringAOP.zip)
## IV.2 Sử dụng Pointcut
- Trong ví dụ trước, ConsoleLoggingAroundAdvice được khai báo dù trong code hay trong file xml đều sẽ được “gắn” vào mọi method của ServiceCommand. Nếu chúng ta muốn nó chỉ “gắn” vào một hoặc nhiều method của class này theo một số tiêu chí nào đó thì đã đến lúc ta nghĩ đến việc sử dụng Pointcut. Các tiêu chí ở đây khá đa dạng: có thể bạn muốn những method với tên method bắt đầu bằng “Start” sẽ được “advised”; hoặc bạn chỉ muốn những method được gọi với các giá trị argument đặc biệt lúc runtime; hay là rất có thể bạn chỉ muốn các Advice được thêm vào những method được đánh dấu bằng attribute đặc biệt nào đó.
- Tất cả những yêu cầu trên được Spring.NET đóng gói trong interface IPointcut (tham khảo
[Spring AOP Section 13.2 “Pointcut API in Spring.NET"](http://www.springframework.net/doc-latest/reference/html/aop.html#aop-pointcuts)). Bây giờ chúng ta sửa class
ServiceCommand, thêm vào method DoExcute(), sau đó sẽ thực hiện một số thay đổi để chỉ method DoExcute() được “advised”:
{
void Execute();
void DoExecute();
}
public class ServiceCommand : ICommand
{
public void Execute()
{
Console.Out.WriteLine("Service implementation : Execute()...");
}
public void DoExecute()
{
Console.Out.WriteLine("Service implementation : DoExecute()...");
}
}
Code 6
- Tất nhiên class ConsoleLoggingAroundAdvice sẽ không cần phải thay đỏi gì cả. Tiếp đến chúng ta sẽ config cho class proxy như sau:
factory.AddAdvisor(new DefaultPointcutAdvisor(
new SdkRegularExpressionMethodPointcut("Do"),
new ConsoleLoggingAroundAdvice()));
ICommand command = (ICommand) factory.GetProxy();
command.DoExecute();
Code 7
- Và đây là kết quả khi chạy chương trình
Intercepted call : about to invoke next item in chain...
Service implementation...
Intercepted call : returned
Hình 3
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Tất nhiên khi ta gọi Excute() thay vì DoExcute(), những xử lý của Advice trên sẽ không hiện ra:
factory.AddAdvisor(new DefaultPointcutAdvisor(
new SdkRegularExpressionMethodPointcut("Do"),
new ConsoleLoggingAroundAdvice()));
ICommand command = (ICommand) factory.GetProxy();
// Note that there is no 'Do' in this method name
command.Execute();
Code 8
Service implementation...
Hình 4
[http://nthoai.blogspot.com](http://nthoai.blogspot.com/))
- Sau đây là đoạn XML config cho tất cả những yêu cầu trên:
<object id="consoleLoggingAroundAdvice"
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor">
<property name="advice">
</property>
</object>
<object id="myServiceObject"
type="Spring.Aop.Framework.ProxyFactoryObject">
</property>
</list>
</property>
</object>
Code 9
- Như ta thấy đoạn config chỉ thay đổi ở phần consoleLoggingAroundAdvice. RegularExpressionMethodPointcutAdvisor đã được thay vào Advisor ban đầu; các property “pattern” và “Advice” được config để xác định Advice tương ứng và pattern tương ứng để match với method có chứa text “Do” trong các method của đối tượng được “advised”.
Download code sample cho phần Pointcut ở đây:
[http://nthoaiblog.googlepages.com/SampleSpringAOPWithPointcut.zip](http://nthoaiblog.googlepages.com/SampleSpringAOPWithPointcut.zip)
# V. Kết luận
- Đây không phải là bài viết tốt nhất về AOP cũng như AOP trong Spring.NET. Tôi đã dịch lại và bổ xung một số ý từ các tài liệu ở phần tham khảo phía dưới. Ban đầu tôi cũng gặp khó khăn khi tìm hiểu cách sử dụng vì Spring.NET đưa ra khá nhiều các khái niệm, thuật ngữ về AOP. Nhưng cuối cùng tôi thấy cách sử dụng AOP với Spring.NET khá giống như chức năng Dependency Injection. Bài viết này chỉ nói đến những chức năng đơn giản của Spring.NET AOP nên trong bài viết sau ta sẽ nói đến các chức năng khác của AOP trong Spring.NET như các loại Advice khác, cách sử dụng Attribute để xác định Pointcuts, … Đối với ai mới research cách sử dụng AOP của Spring.NET, ngoài việc đọc bài này hehe
![17 :bbpcuoi3:](http://igame.vn/f/images/smilies/BBP_smilies/17.gif)
[Spring.NET framework](https://dist.springframework.org/milestone/NET/Spring.NET-1.2.0-M1.zip)về và chạy các sample AOP của nó, vô cùng dễ hiểu.
(
[Xem Phần 2](http://nthoai.blogspot.com/2008/09/su-dung-aop-voi-springnet-p2.html))

Server: GSE

Cache-Control: private, max-age=0


text:
Không có sản phẩm trong giỏ hàng!
TÌM THEO THƯƠNG HIỆU
• Bếp cảm ứng từ 2 bếp kích thước 770• 9 công suất nấu cho mỗi bếp• Khóa trẻ em• Chế độ hẹn giờ cho từng bếp• Cảnh báo nhiệt dư an toàn cho từng bếp• Chức năng tự động tắt bếp khi không sử dụng• Chất liêu mặt kính Schost ceran màu đen• Công suất 6000W• Xuất xứ: Tây Ban Nha• •
0 đánh giá / Viết đánh giá

markdown:
# Bếp từ Cata IB 772 BK
• Bếp cảm ứng từ 2 bếp kích thước 770
• 9 công suất nấu cho mỗi bếp
• Khóa trẻ em
• Chế độ hẹn giờ cho từng bếp
• Cảnh báo nhiệt dư an toàn cho từng bếp
• Chức năng tự động tắt bếp khi không sử dụng
• Chất liêu mặt kính Schost ceran màu đen
• Công suất 6000W
• Xuất xứ: Tây Ban Nha
•
•
Bảo hành
|Thời gian bảo hành||2 năm|

WARC-Type: response

Content-Type: application/http; msgtype=response

WARC-Truncated: length

Set-Cookie: jrv=583; expires=Wed, 24-May-2017 17:12:49 GMT; Max-Age=2592000; path=/; domain=osm.com.vn

Connection: close

Pragma: no-cache

Cache-Control: private, must-revalidate

Vary: User-Agent

Content-Type: text/html; charset=utf-8