AOP切面編程:讓你的代碼更優雅

一、AOP切面編程原理

AOP全稱為Aspect-Oriented Programming,中文翻譯為「面向切面編程」。AOP和OOP(Object-Oriented Programming)作為兩種編程範式,都追求實現代碼的模塊化、可重用性和可維護性。AOP是通過將橫切關注點抽象出來來實現這一點,將其稱為「切面」(Aspect) 。AOP採用的是將這些關注點與原方法進行分離的方式來降低代碼的耦合度的。

在Java中,AOP動態代理的實現,使得我們可以在執行目標方法之前和之後,插入一些特定的處理,而不是在主業務中添加大量冗餘代碼。AOP在實現中使用到了反射技術,對java層面的代碼進行攔截和切入操作。

二、AOP切面編程是什麼

AOP切面編程指的是一種面向切面的編程思想和設計模式,它將各個業務邏輯之間相同或類似的處理抽象成一個切面,達到了代碼重用,簡化程序設計的目的。

例如,當我們需要在應用程序中統計某個方法的運行時間,如果在每次調用該方法時都手動添加代碼,代碼將變得冗餘且不易維護。但是,使用AOP切面編程,我們可以將計時的代碼組織成一個切面,並將其織入到目標方法中,達到代碼重用的目的。

三、AOP切面編程三種實現方式

1. 靜態代理

靜態代理是通過在編譯期間,手動編寫代理類的方式進行的,需要我們手動為每一個類編寫代理類,存在大量的冗餘代碼。其原理是在代理類中生成一個目標類的對象並對其進行包裝,通過調用代理類中的方法來實現對目標類方法的調用。

2. JDK動態代理

JDK動態代理是面向接口代理,實現InvocationHandler接口,重寫invoke方法。在運行期間,使用Java反射機制,在內存中生成代理類,並且實現了目標類的接口,直接調用代理類中的方法會觸發InvocationHandler的invoke方法,我們可以在這個方法中添加需要的操作。JDK動態代理可以通過proxy.newProxyInstance()方法來動態的生成代理類,支持多個目標類的代理。

3. CGLIB動態代理

CGLIB動態代理是直接生成目標類的子類,因而可以對類進行代理(而不僅僅是接口),不過,需要引入cglib-nodep依賴。CGLIB動態代理同樣實現了InvocationHandler接口,通過創建Enhancer來動態生成目標類的子類,然後在子類中織入相關增強邏輯,最後創建子類對象。

四、切面編程是什麼意思

切面編程是AOP編程的實現方式之一,它將通用或者常見的功能,像日誌記錄、性能分析、權限控制等,稱為切面。這些切面可以縱向地貫穿應用的各層,比如Controller,Service,Dao等,也可以橫向地貫穿應用的多個模塊和組件。

切面可以看做是一組針對特定類某一類方法的函數,利用指定的切面可以更方便、更直觀、更快捷地處理代碼,它對代碼的可讀性和維護性都有很好的幫助。

五、AOP面向切面編程應用場景

通過AOP編程實現的切面,可以在任何地方被調用,可以方便地通過合適的實現方式,實現各種不同的應用場景,包括:

1. 日誌記錄

記錄關鍵方法的執行情況,比如接口調用、運行時間、日誌級別等,例如下面是一個簡單的打印日誌的AOP切面:

public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void doBefore() {
        logger.info("method start...");
    }

    @After("execution(* com.example.demo.service.*.*(..))")
    public void doAfter() {
        logger.info("method end...");
    }
}

2. 權限管理

處理用戶權限相關問題,比如用戶是否具有操作某項資源的權限、用戶是否處於登錄狀態等。在使用Spring Security等安全框架時,會經常使用這種方式對請求進行認證和授權。

3. 數據緩存

對於一些數據的計算和處理非常耗費時間,可以將結果緩存起來,再次用到時直接從緩存中獲取數據,提高效率。例如通過redis工具實現數據緩存:

@Around("execution(* com.example.demo.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    String methodName = pjp.getSignature().getName();
    Object[] args = pjp.getArgs();
    //生成redis的key
    String key = RedisKeyUtils.getKey(methodName, Arrays.toString(args));
    //先從redis中查找數據
    Object obj = stringRedisTemplate.opsForValue().get(key);
    if (obj != null) {
        logger.info("Redis exists key - " + key);
        return obj;
    }
    logger.info("Redis not exists key - " + key);
    //如果redis中不存在數據則進行計算,然後添加到redis中
    Object result = pjp.proceed();
    if (result != null) {
        stringRedisTemplate.opsForValue().set(key, result.toString(), 300, TimeUnit.SECONDS);
        logger.info("Redis add key - " + key);
    }
    return result;
}

4. 聲明式事務

通過AOP的方式來幫助我們完成事務的操作,比如管理數據庫連接、開啟或關閉事務、定義事務的邊界等。在Spring整合Mybatis時,經常會用到這種方式來實現聲明式事務,只需要在需要使用事務的方法上添加@Transactional註解即可:

@Transactional
public void insertUser(User user) {
    userDao.insert(user);
}

六、AOP切面編程思想

AOP切面編程思想,是其在設計模式上的具體落地。通過將不同的功能劃分為不同的切面或者模塊,使其更具有可讀性、可維護性和可擴展性。大大減少了重複代碼和代碼冗餘,使得代碼更加簡介,減輕了程序員的工作量,提高開發效率。

七、AOP切面編程架構

AOP切面編程架構通常有三個核心模塊,即切面模塊、切入點模塊和通知模塊。

1. 切面模塊

切面模塊是AOP切面編程的核心模塊,它由一個或多個切面組成。每一個切面都包含了一個或多個通知和一個或多個切點。

2. 切入點模塊

切入點模塊用於定位在目標類中的切點位置。在整個AOP過程中,先針對具體的方法進行攔截,攔截到方法後再根據用戶定義的切面進行處理。

3. 通知模塊

通知模塊則包括了具體的增強代碼,而這些增強代碼都是用戶自己定義的。通知可以包括Before Advice、After Advice、Around Advice、After Throwing Advice和After Returning Advice等。

八、AOP切面編程作用

AOP切面編程能夠讓我們的代碼更加優雅、簡潔、易於維護、擴展和復用,並且可以大大降低多個組件之間的耦合度,並且大大減少重複開發、代碼冗餘以及出錯幾率。性能方面也有所提高,例如Redis緩存、聲明式事務等。

九、AOP切面編程代碼示例

下面是一個簡單的Spring Boot應用,使用AOP切面編程來記錄用戶新增和刪除操作的日誌。

1. 新增記錄日誌切面

@Aspect
@Component
public class UserAddAspect {

    @Autowired
    private HttpServletRequest request;

    private static final Logger logger = LoggerFactory.getLogger(UserAddAspect.class);

    @Before("@annotation(com.example.demo.annotation.UserAdd)")
    public void addLog() {
        String methodName = "新增用戶";
        String params = request.getParameter("username");
        logger.info("執行 " + methodName + " 方法,參數為:" + params);
    }

}

2. 刪除記錄日誌切面

@Aspect
@Component
public class UserDeleteAspect {

    @Autowired
    private HttpServletRequest request;

    private static final Logger logger = LoggerFactory.getLogger(UserDeleteAspect.class);

    @Before("@annotation(com.example.demo.annotation.UserDelete)")
    public void deleteLog() {
        String methodName = "刪除用戶";
        String params = request.getParameter("userId");
        logger.info("執行 " + methodName + " 方法,參數為:" + params);
    }

}

3. 控制器中使用

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @UserAdd //記錄新增用戶日誌
    @PostMapping("/add")
    public String addUser(@RequestParam String username, @RequestParam String password) {
        userService.addUser(username, password);
        return "success";
    }

    @UserDelete //記錄刪除用戶日誌
    @DeleteMapping("/delete/{userId}")
    public String deleteUser(@PathVariable String userId) {
        userService.deleteUser(userId);
        return "success";
    }

}

將註解UserAdd和UserDelete,分別標記在新增用戶和刪除用戶的方法上。當我們調用這兩個方法時,將會在控制台上打印出相應的日誌,從而實現了記錄用戶操作的目的。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/227400.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-09 16:29
下一篇 2024-12-09 16:29

相關推薦

  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • Python官網中文版:解決你的編程問題

    Python是一種高級編程語言,它可以用於Web開發、科學計算、人工智能等領域。Python官網中文版提供了全面的資源和教程,可以幫助你入門學習和進一步提高編程技能。 一、Pyth…

    編程 2025-04-29
  • Python字符串寬度不限制怎麼打代碼

    本文將為大家詳細介紹Python字符串寬度不限制時如何打代碼的幾個方面。 一、保持代碼風格的統一 在Python字符串寬度不限制的情況下,我們可以寫出很長很長的一行代碼。但是,為了…

    編程 2025-04-29
  • Python基礎代碼用法介紹

    本文將從多個方面對Python基礎代碼進行解析和詳細闡述,力求讓讀者深刻理解Python基礎代碼。通過本文的學習,相信大家對Python的學習和應用會更加輕鬆和高效。 一、變量和數…

    編程 2025-04-29
  • 掌握magic-api item.import,為你的項目注入靈魂

    你是否曾經想要導入一個模塊,但卻不知道如何實現?又或者,你是否在使用magic-api時遇到了無法導入的問題?那麼,你來到了正確的地方。在本文中,我們將詳細闡述magic-api的…

    編程 2025-04-29
  • 倉庫管理系統代碼設計Python

    這篇文章將詳細探討如何設計一個基於Python的倉庫管理系統。 一、基本需求 在着手設計之前,我們首先需要確定倉庫管理系統的基本需求。 我們可以將需求分為以下幾個方面: 1、庫存管…

    編程 2025-04-29
  • Python滿天星代碼:讓編程變得更加簡單

    本文將從多個方面詳細闡述Python滿天星代碼,為大家介紹它的優點以及如何在編程中使用。無論是剛剛接觸編程還是資深程序員,都能從中獲得一定的收穫。 一、簡介 Python滿天星代碼…

    編程 2025-04-29
  • 寫代碼新手教程

    本文將從語言選擇、學習方法、編碼規範以及常見問題解答等多個方面,為編程新手提供實用、簡明的教程。 一、語言選擇 作為編程新手,選擇一門編程語言是很關鍵的一步。以下是幾個有代表性的編…

    編程 2025-04-29
  • Python實現簡易心形代碼

    在這個文章中,我們將會介紹如何用Python語言編寫一個非常簡單的代碼來生成一個心形圖案。我們將會從安裝Python開始介紹,逐步深入了解如何實現這一任務。 一、安裝Python …

    編程 2025-04-29
  • 怎麼寫不影響Python運行的長段代碼

    在Python編程的過程中,我們不可避免地需要編寫一些長段代碼,包括函數、類、複雜的控制語句等等。在編寫這些代碼時,我們需要考慮代碼可讀性、易用性以及對Python運行性能的影響。…

    編程 2025-04-29

發表回復

登錄後才能評論