JAVA入門[14]-Spring MVC AOP

来源:http://www.cnblogs.com/janes/archive/2017/05/19/6873732.html
-Advertisement-
Play Games

一、基本概念 1.AOP簡介 DI能夠讓相互協作的軟體組件保持鬆散耦合;而面向切麵編程(aspect-oriented programming,AOP)允許你把遍佈應用各處的功能分離出來形成可重用的組件。把這些橫切關註點與業務邏輯相分離正是面向切麵編程(AOP)所要解決的問題 常見場景:日誌、安全、 ...


一、基本概念

1.AOP簡介

DI能夠讓相互協作的軟體組件保持鬆散耦合;而面向切麵編程(aspect-oriented programming,AOP)允許你把遍佈應用各處的功能分離出來形成可重用的組件。把這些橫切關註點與業務邏輯相分離正是面向切麵編程(AOP)所要解決的問題

常見場景:日誌、安全、事物、緩存

Image(2)

2.AOP用到的一些術語

項目中每個模塊的核心功能都是為特定業務領域提供服務,但是這些模塊都需要類似的輔助功能,例如安全和事務管理,這時候需要引入AOP的概念。

Image(3)

通知定義了切麵是什麼以及何時使用, Spring切麵可以應用5種類型的通知:

  • 前置通知(Before):在目標方法被調用之前調用通知功能;
  • 後置通知(After):在目標方法完成之後調用通知,此時不會關心方法的輸出是什麼;
  • 返回通知(After-returning):在目標方法成功執行之後調用通知;
  • 異常通知(After-throwing):在目標方法拋出異常後調用通知;
  • 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和調用之後執行自定義的行為。

連接點(join potint)是在應用執行過程中能夠插入切麵的一個點。這個點可以是調用方法時、拋出異常時、甚至修改一個欄位時。切麵代碼可以利用這些點插入到應用的正常流程之中,並添加新的行為

切點(poincut)的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱,或是利用正則表達式定義所匹配的類和方法名稱來指定這些切點

 

二、準備service模塊

1.service bean

public class CategoryService1 {
    public void add(int id) {
        System.out.println("CategoryService1.add()");
    }
}

public class CategoryService2{
    public void add(int id) {
        System.out.println("CategoryService2.add()");
    }
}

2.配置bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

<bean id="categoryServiceImpl" class="service.CategoryService1"></bean>
<bean id="CategoryServiceImpl2" class="service.CategoryService2"></bean>
</beans>

3.單元測試

@Test
public void test(){
    ApplicationContext context=new ClassPathXmlApplicationContext("aop.xml");

    CategoryService1 service1=context.getBean(CategoryService1.class);
    service1.add(1);

    CategoryService2 service2=context.getBean(CategoryService2.class);
    service2.add(2);
}

運行結果:

CategoryService1.add()

CategoryService2.add()

 

三、XML方式聲明AOP

Spring所創建的通知都是用標準的Java類編寫的, 定義通知所應用的切點通常會使用註解或在Spring配置文件里採用XML來編寫,這兩種語法對於Java開發者來說都是相當熟悉的。

註意Spring只支持方法級別的連接點。

切入點表達式

execution指示器是我們在編寫切點定義時最主要使用的指示器

Image(4)

 

Demo

我們要實現的一個簡單示例是:在service方法調用前和調用後列印日誌“write log”。

public class LogHandler {
    public void log(){
        System.out.println("write log.");
    }
}

aop.xml添加配置:

<bean id="logHandler" class="pointcut.LogHandler"></bean>
    <aop:config>
        <aop:aspect id="log" ref="logHandler">
            <aop:pointcut id="addLog" expression="execution(* service.*.*(..))"></aop:pointcut>
            <aop:before method="log" pointcut-ref="addLog"></aop:before>
            <aop:after method="log" pointcut-ref="addLog"></aop:after>
        </aop:aspect>
    </aop:config> 

單元測試:

public class AopTests {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        CategoryService1 service1 = context.getBean(CategoryService1.class);
        service1.add(1);
        CategoryService2 service2 = context.getBean(CategoryService2.class);
        service2.add(2);
    }
}

運行報錯:

org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException

原來是忘了pom依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency> 

運行結果:

write log.

CategoryService1.add()

write log.

write log.

CategoryService2.add()

write log.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>DemoStore</groupId>
    <artifactId>DemoAOP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.5.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>

</project>
完整的pom.xml

 

四、aop:around

通過使用環繞通知,可以實現前置通知和後置通知所實現的功能,而且只需要在一個方法中實現。

public class LogTimeHandler {
    public void log(ProceedingJoinPoint jp) throws Throwable {
        try {
            System.out.println("1.before log "+new Date().getTime());//記錄開始時間
            jp.proceed();
            System.out.println("2.after log "+new Date().getTime());//記錄結束時間
        }catch (Exception e){
            System.out.println("log fail ");
        }
    }
}

  

在aop1.xml中配置aop:round通知

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="categoryService" class="service.CategoryService1"></bean>
    <bean id="logHanlder" class="pointcut.LogTimeHandler"></bean>
    <aop:config>
        <aop:aspect id="log" ref="logHanlder">
            <aop:pointcut id="addlog" expression="execution(* service.*.*(..))"></aop:pointcut>
            <aop:around method="log" pointcut-ref="addlog"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

  

單元測試:

public class AopTest1 {
    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("aop1.xml");
        CategoryService1 service1=context.getBean(CategoryService1.class);
        service1.add(1);
    }
}

運行結果:

1.before log 1489990832246
CategoryService1.add()
2.after log 1489990832263

  

五、註解方式創建AOP

定義切麵需要給類添加@Aspect註解。然後需要給方法添加註解來聲明通知方法,各通知類型對應的註解:

  • @After 通知方法會在目標方法返回或拋出異常後
  • @AfterReturning 通知方法會在目標方法返回後調用
  • @AfterThrowing 通知方法會在目標方法拋出異常後調用
  • @Around 通知方法會將目標方法封裝起來
  • @Before 通知方法會在目標方法調用之前執行
@Component
@Aspect
public class LogHelper3 {

    @Before("execution(* service.*.*(..))")
    public void logStart(){
        System.out.println("log start "+new Date().getTime());
    }
}

然後定義JavaConfig類,註意需要給類添加@EnableAspectJAutoProxy註解啟用自動代理功能。

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackageClasses = {service.CategoryService3.class,pointcut.LogHelper3.class})
public class BeanConfig {
}

  單元測試:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanConfig.class)
public class AopTest3 {

    @Autowired
    CategoryService3 service;

    @Test
    public void testConfigAop(){
        service.add(100);
    }
}

運行結果:

log start 1489990977264
add category id=100

  

結尾:

參考:《spring實戰》

源碼下載:https://github.com/cathychen00/learnjava/tree/master/DemoAOP


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 <bean name="u" class="com.bjsxt.dao.impl.UserDAOImpl"></bean> 1 <bean id="u" class="com.bjsxt.dao.impl.UserDAOImpl"></bean> 用id和name的效果是一樣的,唯一的不同是 n ...
  • Ant path 匹配原則 在Spring MVC中經常要用到攔截器,在配置需要要攔截的路徑時經常用到<mvc:mapping/>子標簽,其有一個path屬性,它就是用來指定需要攔截的路徑的。例如: <mvc:interceptor><mvc:mapping path="/**" /><bean c ...
  • PHP中include和require關鍵字,都可以在一個腳本文件中包含另一個腳本文件,但是兩者卻有幾點不同處: 1.include包含文件,出錯時會產生一個E_WARNING(警告),但是腳本仍舊可以繼續運行 2.require包含文件,會產生一個E_COMPILE_ERROR(錯誤),腳本終止 ...
  • 給出一個長為n的數列,以及n個操作,操作涉及區間加法,單點查值。 這是一道能用許多數據結構優化的經典題,可以用於不同數據結構訓練。 數列分塊就是把數列中每m個元素打包起來,達到優化演算法的目的。 以此題為例,如果我們把每m個元素分為一塊,共有n/m塊,每次區間加的操作會涉及O(n/m)個整塊,以及區間 ...
  • 分塊:顧名思義,把一個區間分成不同的塊,然後由原來的每個點暴力轉換為每個塊的暴力,這樣就大大減小了時間複雜度 可能涉及的幾個詞語解釋: 區間:數列中連續一段的元素 區間操作:將某個區間[a,b]的所有元素進行某種改動的操作 塊:我們將數列劃分成若幹個不相交的區間,每個區間稱為一個塊 整塊:在一個區間 ...
  • a) setter(重要) b) 構造方法(可以忘記),簡單例子: 用的不多,具體的構造函數重構應用可以參考源文檔 c) 介面註入(可以忘記)。 代碼鏈接: http://pan.baidu.com/s/1pKAe5Vt 密碼: qvyy jar 包: 鏈接: http://pan.baidu.co ...
  • R語言數據可視化之ggplot2包,從柱狀圖開始。從簡單的業務量統計開始。 ...
  • ggplot2介紹:內容包含什麼是ggplot2、與lattice包的比較、基本概念、一個例子。 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...