使用Spring AOP進行面向方面的編程

面向方面的編程(AOP)是指將輔助功能或支持功能與主程序的業務邏輯隔離開來的編程范例。 AOP是用于分離橫切關注點的有前途的技術,這在面向對象的編程中通常很難做到。 以此方式增加了應用程序的模塊化,并且維護變得非常容易。

橫切關注點最突出的例子是伐木 。 日志記錄主要用于通過跟蹤方法調用和總體執行流程來調試和故障排除問題。 由于日志記錄策略必然會影響系統的每個日志記錄部分,因此可以將其視為橫切關注點。 因此,日志記錄會橫切所有已記錄的類和方法。

請注意,AOP和OOP不是唯一的。 相反,它們是相輔相成的,它們的組合使用可以幫助我們生產健壯且可維護的軟件。 使用AOP,我們首先使用OO語言來實現我們的項目,然后通過實現方面來分別處理代碼中的橫切關注點。

在我們最喜歡的Spring框架的幫助下,AOP的使用和采用得到了提高。 Spring使AOP使用非侵入性方法更易于集成到我們的項目中。 Justin在JavaCodeGeeks的題為 “ 使用Spring AspectJ和Maven進行面向方面的編程 ”的文章中曾談到過Spring AOP。 但是,我們最新的JCG合作伙伴 SivaLabs的 Siva也寫了一篇關于Spring AOP的很好的文章 ,我想與您分享,這里是。

(注意:對原始帖子進行了少量編輯以提高可讀性)

在為企業開發軟件應用程序時,我們會從需求收集團隊或我們的業務分析師那里收到項目的需求。 通常,這些需求是代表業務活動的功能需求。 但是,在開發軟件應用程序時,除了功能需求之外,我們還應該考慮其他一些方面,例如性能,事務管理,安全性,日志記錄等。這些被稱為非功能需求。

讓我們考慮一個BookStore應用程序,它提供對書店的網絡訪問。 用戶可以瀏覽各種類別的書籍,將一些書籍添加到購物車中,最后結帳,付款并獲得書籍。 對于此應用程序,我們可能會收到來自業務分析師的要求,如下所示:

  • 登錄/注冊屏幕以進入BookStore。
  • 用戶應該能夠瀏覽各種類別的書籍
  • 用戶應該能夠按名稱,作者姓名,出版商搜索書籍
  • 用戶應該能夠在購物車中添加/刪除圖書
  • 用戶應該能夠查看其購物車中當前存在哪些物品
  • 用戶應該能夠通過某些支付網關進行結帳并支付相應的金額
  • 應該向用戶顯示一條成功消息,其中包含購買的所有詳細信息。
  • 應向用戶顯示失敗消息,并說明失敗原因。
  • 應該授予BookStore管理員/經理訪問添加/刪除/更新圖書詳細信息的權限。

以上所有要求都屬于“功能要求”類別。 在執行上述操作時,即使未明確提及,我們也應注意以下事項:

  • 基于角色的用戶界面訪問。 在這里,只有管理員/管理員才有權添加/刪除/更新書籍詳細信息。 [基于角色的授權]
  • 采購中的原子性。 假設一個用戶登錄到BookStore并將5本書添加到他的購物車中,簽出并完成了付款。 在后端實施中,我們可能需要在3個表中輸入此購買詳細信息。 如果將數據插入2個表后系統崩潰,則應回滾整個操作。 [交易管理]。
  • 沒有人是完美的,沒有系統是完美的。 因此,如果出現問題,并且開發團隊必須找出問題所在,則日志記錄將非常有用。 因此,日志記錄的實現方式應使開發人員應該能夠弄清楚應用程序的確切故障原因并進行修復。 [記錄中]

上面的隱式要求稱為非功能性要求。 除上述之外,對于所有面向公眾的網站,性能顯然應該是至關重要的非功能性要求。

因此,利用上述所有功能需求,我們可以通過將系統分解為各個組件帶來構建系統,同時照顧整個組件的非功能需求。

public class OrderService
{private OrderDAO orderDAO;public boolean placeOrder(Order order){boolean flag = false;logger.info("Entered into OrderService.placeOrder(order) method");try{flag = orderDAO.saveOrder(order);}catch(Exception e){logger.error("Error occured in OrderService.placeOrder(order) method");}logger.info("Exiting from OrderService.placeOrder(order) method");return flag;}
}
public class OrderDAO
{public boolean saveOrder(Order order){boolean flag = false;logger.info("Entered into OrderDAO.saveOrder(order) method");Connection conn = null;try{conn = getConnection();//get database connectionconn.setAutoCommit(false);// insert data into orders_master table which generates an order_id// insert order details into order_details table with the generated order_id// insert shipment details into order_shipment tableconn.commit();conn.setAutoCommit(true);flag = true;}catch(Exception e){logger.error("Error occured in OrderDAO.saveOrder(order) method");conn.rollback();}logger.info("Exiting from OrderDAO.saveOrder(order) method");return flag;}
}

在上面的代碼中,功能需求實現和非功能需求實現混合在同一位置。 記錄跨OrderService和OrderDAO類。 同時,事務管理涉及多個DAO。

使用這種方法,我們有幾個問題:

  1. 需要更改類以更改功能或非功能需求。 例如:在開發的某個時刻,如果團隊決定將方法進入/退出信息與TimeStamp一起記錄下來,我們幾乎需要更改所有類。
  2. 事務管理代碼在開始時將自動提交設置為false,在執行數據庫操作,提交/回滾操作邏輯時,將在所有DAO中重復執行。

跨越模塊/組件的這種要求稱為“交叉切割問題”。 為了更好地設計系統,我們應該將這些跨領域的關注點與實際的業務邏輯分開,以便日后更容易更改或增強或維護應用程序。

面向方面的編程是一種使跨領域關注點與實際業務邏輯分離的方法。 因此,讓我們遵循AOP方法并重新設計上述兩類,將交叉關注點分開。

public interface IOrderService
{public boolean placeOrder(Order order);
}
public class OrderService implements IOrderService
{private OrderDAO orderDAO;public boolean placeOrder(Order order){return orderDAO.saveOrder(order);}
}
public class OrderDAO
{public boolean saveOrder(Order order){boolean flag =false;Connectoin conn = null;try{conn = getConnection();//get database connection// insert data into orders_master table which generates an order_id// insert order details into order_details table with the generated order_id// insert shipment details into order_shipment tableflag = true;}catch(Exception e){logger.error(e);            }        return flag;}
}

現在,讓我們創建一個LoggingInterceptor來實現應如何進行日志記錄,并為OrderService創建一個代理,該Proxy接受來自調用方的調用,使用LoggingInterceptor記錄進入/退出條目,最后委托給實際的OrderService。

通過使用動態代理,我們可以從實際業務邏輯中分離出跨領域關注點的實現(例如日志記錄),如下所示:

public class LoggingInterceptor
{public void logEntry(Method m){logger.info("Entered into "+m.getName()+" method");}public void logExit(Method m){logger.info("Exiting from "+m.getName()+" method");}
}
public class OrderServiceProxy implements IOrderService extends LoggingInterceptor
{private OrderService orderService;public boolean placeOrder(Order order){boolean flag =false;Method m = getThisMethod();//get OrderService.placeOrder() Method objectlogEntry(m);flag = orderService.placeOrder(order);logExit(m);return flag;}
}

現在,OrderService調用方(OrderController)可以獲取OrderServiceProxy并將訂單下達為:

public class OrderController
{public void checkout(){Order order = new Order();//set the order detailsIOrderService orderService = getOrderServiceProxy();orderService.placeOrder(order);}
}

可以使用幾種AOP框架來將實現與交叉關注點分離開。

  • SpringAOP
  • AspectJ
  • JBoss AOP

讓我們看看如何使用Spring AOP將日志記錄與實際業務邏輯分開。 在此之前,首先我們需要了解以下術語:

  • JoinPoint:連接點是應用程序執行中可以插入方面的點。此點可以是調用方法,引發異常或甚至修改字段。
  • 切入點:切入點定義與應在其中編織建議的一個或多個連接點相匹配。 通常,您使用顯式的類和方法名稱或通過定義匹配的類和方法名稱模式的正則表達式來指定這些切入點。
  • 方面:方面是建議和切入點的合并。
  • 建議:方面的工作稱為建議。 這是我們應用于現有模型的附加代碼。

SpringAOP支持幾種類型的建議,即:

  • 之前:此建議在方法調用之前編織了方面。
  • AfterReturning:此建議在方法調用后編織方面。
  • AfterThrowing:當方法拋出Exception時,此建議將編織方面。
  • 圍繞:此建議在方法調用之前和之后編織方面。

假設我們有以下ArithmeticCalculator接口和實現類。

package com.springapp.aop;public interface ArithmeticCalculator
{public double add(double a, double b);public double sub(double a, double b);public double mul(double a, double b);public double div(double a, double b);
}
package com.springapp.aop;
import org.springframework.stereotype.Component;@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator
{public double add(double a, double b){double result = a + b;System.out.println(a + " + " + b + " = " + result);return result;}public double sub(double a, double b){double result = a - b;System.out.println(a + " - " + b + " = " + result);return result;}public double mul(double a, double b){double result = a * b;System.out.println(a + " * " + b + " = " + result);return result;}public double div(double a, double b){if(b == 0){throw new IllegalArgumentException("b value must not be zero.");}double result = a / b;System.out.println(a + " / " + b + " = " + result);return result;}
}

以下LoggingAspect類展示了使用Spring AOP應用Logging Advice的各個方面:

package com.springapp.aop;import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect
{private Log log = LogFactory.getLog(this.getClass());@Pointcut("execution(* *.*(..))")protected void loggingOperation() {}@Before("loggingOperation()")@Order(1)public void logJoinPoint(JoinPoint joinPoint){log.info("Join point kind : " + joinPoint.getKind());log.info("Signature declaring type : "+ joinPoint.getSignature().getDeclaringTypeName());log.info("Signature name : " + joinPoint.getSignature().getName());log.info("Arguments : " + Arrays.toString(joinPoint.getArgs()));log.info("Target class : "+ joinPoint.getTarget().getClass().getName());log.info("This class : " + joinPoint.getThis().getClass().getName());}@AfterReturning(pointcut="loggingOperation()", returning = "result")@Order(2)public void logAfter(JoinPoint joinPoint, Object result){log.info("Exiting from Method :"+joinPoint.getSignature().getName());log.info("Return value :"+result);}@AfterThrowing(pointcut="execution(* *.*(..))", throwing = "e")@Order(3)public void logAfterThrowing(JoinPoint joinPoint, Throwable e){log.error("An exception has been thrown in "+ joinPoint.getSignature().getName() + "()");log.error("Cause :"+e.getCause());}@Around("execution(* *.*(..))")@Order(4)public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{log.info("The method " + joinPoint.getSignature().getName()+ "() begins with " + Arrays.toString(joinPoint.getArgs()));try{Object result = joinPoint.proceed();log.info("The method " + joinPoint.getSignature().getName()+ "() ends with " + result);return result;} catch (IllegalArgumentException e){log.error("Illegal argument "+ Arrays.toString(joinPoint.getArgs()) + " in "+ joinPoint.getSignature().getName() + "()");throw e;}        }}

這是我們的applicationContext.xml應該包括的內容:

這是一個用于測試功能的獨立測試客戶端。

package com.springapp.aop;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringAOPClient
{public static void main(String[] args){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");ArithmeticCalculator calculator = (ArithmeticCalculator) context.getBean("arithmeticCalculator");double sum = calculator.add(12, 23);System.out.println(sum);double div = calculator.div(1, 10);System.out.println(div);}}

所需的庫如下:

  • Spring.jar(2.5.6或以上)
  • commons-logging.jar
  • aopalliance.jar
  • Aspectjrt.jar
  • Aspectjweaver.jar
  • cglib-nodep-2.1_3.jar

我們可以使用注釋@ Before,@ AfterReturning,@ Around等來定義建議的類型。我們可以以不同的方式定義切入點。 例如:

@Around(“ execution(* *。*(..))”)表示這是一個“環繞”建議,將應用于所有包和所有方法中的所有類。

假設我們只想對com.myproj.services包中的所有服務應用建議。 然后切入點聲明將是:

@Around(“執行(* com.myproj.services。*。*(..))”)

在這種情況下,“(..)”表示帶有任何類型的參數。

如果我們想對許多建議應用相同的切入點,我們可以在方法上定義一個切入點,以后可以參考以下內容。

@Pointcut("execution(* *.*(..))")
protected void loggingOperation() {}@Before("loggingOperation()")
public void logJoinPoint(JoinPoint joinPoint){}

如果必須在同一切入點上應用多個建議,則可以使用@Order批注指定要在其上應用建議的訂單。 在前面的示例中,將首先應用@Before。 然后,在調用add()方法時將應用@Around。

就是這樣。 這是我們JCG合作伙伴之一的Siva提供的非常簡單的說明性教程。

快樂的AOP編碼。 別忘了分享!

相關文章:

  • 使用Spring AspectJ和Maven進行面向方面的編程
  • GWT 2 Spring 3 JPA 2 Hibernate 3.5教程– Eclipse和Maven 2展示
  • GWT Spring和Hibernate進入數據網格世界
  • 帶有Spring和Maven教程的JAX–WS

翻譯自: https://www.javacodegeeks.com/2011/01/aspect-oriented-programming-spring-aop.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374911.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374911.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374911.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試題24 二叉搜索樹的后序遍歷序列

題目描述 輸入一個整數數組&#xff0c;判斷該數組是不是某二叉搜索樹的后序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。1 class Solution {2 public:3 bool VerifySquenceOfBST(vector<int> sequence) {4 if (seque…

習題6-5 使用函數驗證哥德巴赫猜想 (20 分)

本題要求實現一個判斷素數的簡單函數&#xff0c;并利用該函數驗證哥德巴赫猜想&#xff1a;任何一個不小于6的偶數均可表示為兩個奇素數之和。素數就是只能被1和自身整除的正整數。注意&#xff1a;1不是素數&#xff0c;2是素數。 函數接口定義&#xff1a; int prime( int…

Linux學習筆記 (六)用戶管理命令

一、用戶帳號 1、超級用戶&#xff1a;具有操作系統中的最高權限&#xff0c;用來管理和維護操作系統。root用戶。 2、普通用戶&#xff1a;由root用戶來創建&#xff0c;在宿主目錄中具有完全權限。 3、程序用戶&#xff1a;由應用程序添加&#xff0c;維護某個應用程序運行。…

使用Spring Security保護GWT應用程序

在本教程中&#xff0c;我們將看到如何將GWT與Spring的安全模塊&#xff08;即Spring Security&#xff09;集成在一起。 我們將看到如何保護GWT入口點&#xff0c;如何檢索用戶的憑據以及如何記錄各種身份驗證事件。 此外&#xff0c;我們將實現自定義身份驗證提供程序&#x…

用Fragment制作的Tab頁面產生的UI重疊問題

本文出處&#xff1a;http://blog.csdn.net/twilight041132/article/details/43812745 在用Fragment做Tab頁面&#xff0c;發現有時候進入應用會同時顯示多個Tab內容&#xff0c;UI發生重疊。 當應用被強行關閉后&#xff08;通過手機管家軟件手動強關&#xff0c;或系統為節省…

習題6-6 使用函數輸出一個整數的逆序數 (20 分)

本題要求實現一個求整數的逆序數的簡單函數。 函數接口定義&#xff1a; int reverse( int number );其中函數reverse須返回用戶傳入的整型number的逆序數。 我的代碼&#xff1a; int reverse( int number ) {int n number,res 0,t 0;n (n>0)?n:-n;while(n ! 0){t…

Tomcat 7上具有RESTeasy JAX-RS的RESTful Web服務-Eclipse和Maven項目

開發Web服務的RESTful方法不斷受到越來越多的關注&#xff0c;并且似乎正在將SOAP淘汰。 我不會討論哪種方法更好&#xff0c;但是我相信我們都同意REST更輕量級。 在本教程中&#xff0c;我將向您展示如何使用RESTeasy開發RESTful服務以及如何將它們部署在Tomcat服務器上。 在…

appcmd命令導入導出站點與應用程序池

在IIS7上導出所有應用程序池的方法: %windir%\system32\inetsrv\appcmd list apppool /config /xml > c:\apppools.xml 這個命令會將服務器上全部的應用程序池都導出來,但有些我們是我們不需要的,要將他們刪掉.比如: DefaultAppPoolClassic .Net AppPool如果在導入時發現同名…

卸載apache

1、運行services.msc&#xff0c;在服務中停止 apache 服務。2、運行命令行程序&#xff0c;輸入 sc delete apache&#xff0c;刪除該服務3、刪除apache文件夾。轉載于:https://www.cnblogs.com/jiangjieqim/p/5357950.html

使用wowza和xuggler將RTMP轉換為RTSP

注意&#xff1a;這是我們的“ Xuggler開發教程 ”系列的一部分。 大家好&#xff01; 在過去的三個月中&#xff0c;我們一直在進行電話會議項目。 我們認為&#xff0c;使用諸如Flex之類的技術的基于Web的應用程序將是此類要求苛刻的項目的最佳方法。 隨著軟件的復雜性和電信…

如何設置網頁標題圖標

1、先制作一個名為favicon.ico的小圖標&#xff0c;并將其放到網站根目錄下 2、在html頁面<head></head>標簽內加入&#xff1a; <link rel"shortcut icon" href"favicon.ico" />OK轉載于:https://www.cnblogs.com/moshengr/p/4600281.h…

C語言pow函數的精度問題

&#xff08;1&#xff09;pow函數返回值是double類型 &#xff08;2&#xff09;pow原型 double pow(double x,double y); (3)sqrt原型 double sqrt(double x); 當輸出時函數值賦給整型就會出錯&#xff1a; int main()d {int N 1;scanf("%d",&N);for(int i …

習題2-1 求整數均值 (10 分)

吐槽一下&#xff0c;因為少打了一個空格&#xff0c;PTA上老是不給我過&#xff0c;還一直報結果錯誤&#xff0c;誤導我好久&#xff0c;即使是吹毛求疵&#xff0c;也應該提示我格式錯誤吧&#xff01;&#xff01; 原題&#xff1a; 本題要求編寫程序&#xff0c;計算4個整…

iframe高度自適應,終于解決了

一直被iframe的高度自適應的問題困擾著&#xff0c;在項目中也是多次遇到。網上也有不少相關的代碼&#xff0c;但是總不能滿足自己的要求。在頭痛了幾次之后終于下定決心解決這個問題。 本代碼主要解決的問題是&#xff1a;最外層滾動條隨著iframe高度動態變化的問題。如果ifr…

在領域驅動的設計,貧乏的領域模型,代碼生成,依賴項注入等方面……

埃里克埃文斯&#xff08;Eric Evans&#xff09;已制定了什么是域驅動設計&#xff08;DDD&#xff09;。 Martin Fowler是DDD的大力支持者和擁護者。 這些都是非凡的名字&#xff0c;幾乎可以肯定的是&#xff0c;他們正在支持一些有價值的東西。 我不是在這里對此爭論。 也許…

Javascript模塊化工具require.js教程

轉自&#xff1a;http://www.w3cschool.cc/w3cnote/requirejs-tutorial-1.html, http://www.w3cschool.cc/w3cnote/requirejs-tutorial-2.html 隨著網站功能逐漸豐富&#xff0c;網頁中的js也變得越來越復雜和臃腫&#xff0c;原有通過script標簽來導入一個個的js文件這種方式已…

數據值、列類型和數據字段屬性

數據值&#xff1a;數值型、字符型、日期型和空值等。數據列類型 2.1 數值類的數據列類型2.2 字符串類數據列類型 2.3 日期和時間型數據數據列類型 另外&#xff0c;也可以使用整形列類型存儲UNIX時間戳&#xff0c;代替日期和時間列類型&#xff0c;這是基于PHP的web項目中常…

全文搜索Apache Lucene簡介

在本教程中&#xff0c;我想談談Apache Lucene 。 Lucene是一個開源項目&#xff0c;提供基于Java的索引和搜索技術。 使用其API&#xff0c;很容易實現全文搜索 。 我將處理Lucene Java版本 &#xff0c;但請記住&#xff0c;還有一個名為Lucene.NET的.NET端口&#xff0c;以及…

函數scanf

本節介紹輸入函數 scanf 的用法。scanf 和 printf 一樣&#xff0c;非常重要&#xff0c;而且用得非常多&#xff0c;所以一定要掌握。 概述 scanf 的功能用一句話來概括就是“通過鍵盤給程序中的變量賦值”。該函數的原型為&#xff1a; # include <stdio.h> int scanf(…

C語言中定義變量位置

C標準的問題 C89規定&#xff0c;在任何執行語句之前&#xff0c;在塊的開頭聲明所有局部變量。 即應該如下&#xff1a;定義變量只能在最開始&#xff0c;scanf等執行語句之前 int N 0;double sum 0;scanf("%d",&N);在C99以及C中則沒有這個限制&#xff0c;即…