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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...