成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

說說AOP 中的 Aspect、Advice、Pointcut、JointPoint 和 Advice 參數分別是什么?

開發 前端
除了服務器程序,在一些需要頻繁進行資源共享和線程協作的場景中,死鎖也可能隨時出現。比如在一個多線程的文件處理系統中,多個線程可能需要同時訪問和修改同一個文件,如果對文件資源的訪問控制不當,就很容易引發死鎖,導致文件處理出錯,數據丟失等嚴重后果。

面試回答

Aspect(切面): 切面是橫切關注點的模塊化,它包含AdvicePointcut,是AOP的基本單位。切面可以理解為我們要增強的邏輯模塊,例如日志、事務等功能。

Advice(通知): 通知定義了切面在特定連接點要執行的動作。Spring支持5種通知類型:@Before(前置)、@After(后置)、@AfterReturning(返回后)、@AfterThrowing(異常后)和@Around(環繞)。

Pointcut(切點): 切點是匹配連接點的表達式,決定Advice在哪些方法上執行。Spring使用AspectJ的表達式語言定義切點。

JoinPoint(連接點): 連接點是程序執行過程中可以插入切面的點,如方法調用、異常拋出等。在Spring AOP中,連接點總是方法的執行點。

Advice參數: 通過JoinPoint對象可以獲取目標方法的簽名、參數等信息,還可以自定義參數綁定,實現更靈活的邏輯處理。


AOP的核心概念圍繞著"在什么地方(Pointcut)"執行"什么操作(Advice)",并將這些封裝在切面(Aspect)中。通過連接點(JoinPoint)和參數綁定,我們可以獲取方法執行的上下文信息,實現更靈活的橫切關注點模塊化,提高代碼的可維護性和復用性。

詳細解析

1. Aspect(切面)

切面是AOP的核心模塊化單元,它封裝了跨多個類的橫切關注點。

在Spring中,切面通過@Aspect注解定義,簡單說來就是需要增強的代碼邏輯

package com.qy.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    // 這里包含Pointcut和Advice定義
}

切面的職責是將Pointcut(定義在哪里切入)和Advice(定義做什么)整合在一起。

2. Advice(通知)

通知定義了切面在特定連接點執行的動作,Spring支持5種類型:

package com.qy.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
publicclass LoggingAspect {

    // 前置通知:方法執行前
    @Before("execution(* com.qy.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("前置通知:準備執行方法 " + joinPoint.getSignature().getName());
    }

    // 后置通知:方法執行后(無論是否異常)
    @After("execution(* com.qy.service.*.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("后置通知:方法 " + joinPoint.getSignature().getName() + " 已執行完畢");
    }

    // 返回通知:方法正常返回后
    @AfterReturning(pointcut = "execution(* com.qy.service.*.*(..))", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("返回通知:方法 " + joinPoint.getSignature().getName() + " 返回值: " + result);
    }

    // 異常通知:方法拋出異常后
    @AfterThrowing(pointcut = "execution(* com.qy.service.*.*(..))", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        System.out.println("異常通知:方法 " + joinPoint.getSignature().getName() + " 拋出異常: " + ex.getMessage());
    }

    // 環繞通知:完全控制方法執行
    @Around("execution(* com.qy.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("環繞通知開始:準備調用方法 " + joinPoint.getSignature().getName());
        long startTime = System.currentTimeMillis();
        
        Object result = null;
        try {
            // 調用原方法
            result = joinPoint.proceed();
        } catch (Exception e) {
            System.out.println("環繞通知捕獲異常: " + e.getMessage());
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println("環繞通知結束:方法執行耗時 " + (endTime - startTime) + "ms");
        }
        
        return result;
    }
}

各種通知類型的特點和使用場景:

  • @Before: 適合做參數校驗、權限檢查等前置工作
  • @After: 適合做資源釋放等必須執行的操作
  • @AfterReturning: 適合對返回結果進行處理或記錄
  • @AfterThrowing: 適合做異常處理、日志記錄等
  • @Around: 功能最強大,可以完全控制方法執行,適合做性能監控、事務控制等

3. Pointcut(切點)

切點是匹配連接點的表達式,定義了Advice在哪些方法上執行。Spring采用AspectJ表達式語言:

package com.qy.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
publicclass SystemArchitecture {
    
    // 所有service包中的方法
    @Pointcut("execution(* com.qy.service.*.*(..))")
    public void serviceLayer() {}
    
    // 所有dao包中的方法
    @Pointcut("execution(* com.qy.dao.*.*(..))")
    public void dataAccessLayer() {}
    
    // 組合切點:所有service或dao包中的方法
    @Pointcut("serviceLayer() || dataAccessLayer()")
    public void businessLogicLayer() {}
    
    // 帶有@Transactional注解的方法
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
}

常用的切點表達式:

  • execution(* com.qy.service.*.*(..)): 匹配service包中所有類的所有方法
  • @annotation(com.qy.annotation.LogExecutionTime): 匹配帶有特定注解的方法
  • within(com.qy.service.*): 匹配service包中所有類的方法
  • this(com.qy.service.UserService): 匹配實現了UserService接口的代理對象的方法
  • target(com.qy.service.UserService): 匹配實現了UserService接口的目標對象的方法
  • args(java.lang.String,..): 匹配第一個參數為String類型的方法

4. JoinPoint(連接點)

連接點是程序執行過程中可以插入切面的點。在Spring AOP中,連接點總是方法執行點。JoinPoint對象提供了訪問連接點信息的方法:

package com.qy.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
publicclass JoinPointDemoAspect {
    
    @Before("execution(* com.qy.service.*.*(..))")
    public void demonstrateJoinPoint(JoinPoint joinPoint) {
        // 獲取目標方法簽名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 獲取目標對象
        Object target = joinPoint.getTarget();
        
        // 獲取方法參數
        Object[] args = joinPoint.getArgs();
        
        System.out.println("目標類: " + target.getClass().getName());
        System.out.println("方法名: " + method.getName());
        System.out.println("參數列表: " + Arrays.toString(args));
        System.out.println("方法修飾符: " + method.getModifiers());
        System.out.println("方法返回類型: " + method.getReturnType().getName());
    }
}

JoinPoint對象提供的關鍵信息:

  • getSignature(): 獲取方法簽名
  • getTarget(): 獲取目標對象
  • getArgs(): 獲取方法參數
  • getThis(): 獲取代理對象
  • getKind(): 獲取連接點類型

環繞通知中使用的是ProceedingJoinPoint,它擴展了JoinPoint接口,添加了proceed()方法用于執行目標方法。

5. Advice參數

Advice方法可以接收參數,增強其靈活性:

package com.qy.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
publicclass ParameterDemoAspect {
    
    // 通過JoinPoint獲取參數
    @Before("execution(* com.qy.service.UserService.findById(Long))")
    public void beforeFindById(JoinPoint joinPoint) {
        Long userId = (Long) joinPoint.getArgs()[0];
        System.out.println("查詢用戶ID: " + userId);
    }
    
    // 直接綁定參數
    @Before("execution(* com.qy.service.UserService.findById(Long)) && args(userId)")
    public void beforeFindByIdWithParam(Long userId) {
        System.out.println("準備查詢用戶ID: " + userId);
    }
    
    // 綁定注解參數
    @Before("@annotation(audit)")
    public void auditMethod(JoinPoint joinPoint, com.qy.annotation.Audit audit) {
        System.out.println("審計記錄: " + audit.operation());
        System.out.println("操作方法: " + joinPoint.getSignature().getName());
    }
}

參數綁定的主要方式:

  1. 通過JoinPoint對象獲取
  2. 通過切點表達式中的args()綁定
  3. 通過注解屬性綁定

AOP的工作流程

  1. 定義切面類(Aspect),包含切點(Pointcut)和通知(Advice)
  2. Spring容器啟動時識別@Aspect注解的Bean
  3. 根據切點表達式匹配目標Bean的方法,創建代理對象
  4. 當調用目標方法時,代理對象攔截調用并按順序執行相應的通知

實際應用場景

AOP在實際開發中有廣泛應用:

  1. 日志記錄:記錄方法調用、參數、執行時間等
  2. 事務管理:聲明式事務控制
  3. 安全控制:權限檢查、認證
  4. 性能監控:統計方法執行時間
  5. 緩存處理:方法結果緩存
  6. 異常處理:統一異常處理和日志
  7. 重試機制:失敗自動重試

使用AOP實現日志

下面是一個完整的使用Spring AOP實現日志功能的例子

package com.qy.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
publicclass LoggingAspect {
    
    // 定義切點:攔截service包下所有類的所有方法
    @Pointcut("execution(* com.qy.service.*.*(..))")
    public void serviceLog() {}
    
    // 環繞通知:記錄方法執行前后的日志
    @Around("serviceLog()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 獲取方法簽名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 獲取目標類
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 創建日志對象
        Logger logger = LoggerFactory.getLogger(targetClass);
        
        // 記錄方法開始執行的日志
        String methodName = signature.getName();
        String className = targetClass.getSimpleName();
        Object[] args = joinPoint.getArgs();
        
        logger.info("開始執行: {}.{},參數: {}", className, methodName, Arrays.toString(args));
        
        long startTime = System.currentTimeMillis();
        Object result = null;
        
        try {
            // 執行目標方法
            result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            
            // 記錄方法正常結束的日志
            logger.info("方法執行成功: {}.{},耗時: {}ms,返回值: {}", 
                    className, methodName, (endTime - startTime), result);
            
            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            
            // 記錄方法異常的日志
            logger.error("方法執行異常: {}.{},耗時: {}ms,異常信息: {}", 
                    className, methodName, (endTime - startTime), e.getMessage());
            
            // 拋出原始異常,不影響業務邏輯
            throw e;
        }
    }
}

業務服務類

package com.qy.service;

import com.qy.model.User;
import org.springframework.stereotype.Service;

@Service
publicclass UserService {
    
    public User getUserById(Long id) {
        // 模擬業務邏輯
        if (id <= 0) {
            thrownew IllegalArgumentException("用戶ID必須大于0");
        }
        
        // 模擬從數據庫查詢用戶
        User user = new User();
        user.setId(id);
        user.setUsername("用戶" + id);
        user.setEmail("user" + id + "@qq.com");
        
        return user;
    }
    
    public boolean updateUser(User user) {
        // 模擬更新用戶信息
        System.out.println("更新用戶信息: " + user);
        returntrue;
    }
}

用戶實體類

package com.qy.model;

publicclass User {
    private Long id;
    private String username;
    private String email;
    
    // 省略getter和setter方法
    
    @Override
    public String toString() {
        return"User{id=" + id + ", username='" + username + "', email='" + email + "'}";
    }
}

主應用類

package com.qy;

import com.qy.model.User;
import com.qy.service.UserService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
publicclass LoggingAopApplication {
    
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(LoggingAopApplication.class, args);
        
        // 獲取UserService
        UserService userService = context.getBean(UserService.class);
        
        try {
            // 測試正常情況
            User user = userService.getUserById(1L);
            System.out.println("獲取到用戶: " + user);
            
            // 修改用戶并更新
            user.setUsername("修改后的用戶名");
            userService.updateUser(user);
            
            // 測試異常情況
            userService.getUserById(-1L);
        } catch (Exception e) {
            System.out.println("捕獲到異常: " + e.getMessage());
        }
    }
}

Spring配置類

package com.qy.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.qy")
public class AppConfig {
    
}

當應用運行時,AOP會自動攔截UserService中的方法調用并輸出日志,日志輸出示例

com.qy.service.UserService - 開始執行: UserService.getUserById,參數: [1]
com.qy.service.UserService - 方法執行成功: UserService.getUserById,耗時: 3ms,返回值: User{id=1, username='用戶1', email='user1@qq.com'}
獲取到用戶: User{id=1, username='用戶1', email='user1@qq.com'}
com.qy.service.UserService - 開始執行: UserService.updateUser,參數: [User{id=1, username='修改后的用戶名', email='user1@qq.com'}]
更新用戶信息: User{id=1, username='修改后的用戶名', email='user1@qq.com'}
com.qy.service.UserService - 方法執行成功: UserService.updateUser,耗時: 1ms,返回值: true
com.qy.service.UserService - 開始執行: UserService.getUserById,參數: [-1]
com.qy.service.UserService - 方法執行異常: UserService.getUserById,耗時: 1ms,異常信息: 用戶ID必須大于0
捕獲到異常: 用戶ID必須大于0


責任編輯:武曉燕 來源: 玄武后端技術棧
相關推薦

2023-02-01 09:15:41

2024-12-20 17:29:34

SpringBootAOP開發

2021-10-27 10:12:54

DockerContainerdRunC

2023-02-24 08:03:24

ChatGPT人臉識別分支

2025-03-12 09:36:23

AspectJAOP開發

2025-02-06 08:44:11

MySQLEXISTSIN

2021-10-27 08:54:11

Pythonencodeencoding

2019-07-17 13:41:36

VueReactJSX

2023-10-23 11:07:37

HTTPRPC

2022-10-19 12:00:32

CSS 偽類偽元素

2023-10-24 09:07:14

CookieSessionHTTP

2022-11-15 10:03:34

2021-12-09 22:47:44

區塊鏈加密貨幣比特幣

2024-12-30 07:20:00

Redis數據庫MySQL

2016-03-21 10:40:53

RDDSpark SQL數據集

2024-06-11 00:04:00

對象AdvisorAdvice

2018-05-21 21:26:59

Apache HiveHbaseSQL

2015-02-26 10:29:41

Google百度

2024-12-09 07:10:00

限流系統

2022-10-26 15:10:46

CSS數據開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 人人九九精 | 国产成人精品一区二区三区视频 | 日韩黄色小视频 | 中国一级特黄真人毛片 | 国产女人与拘做受视频 | 日韩av免费在线电影 | 久久精品亚洲精品 | 久久精品一区二区 | 成人久久18免费网站麻豆 | 91资源在线 | 久久精品免费观看 | 亚洲成人一区 | av毛片 | 在线免费激情视频 | 免费观看av网站 | 在线观看日本高清二区 | 国产色婷婷精品综合在线手机播放 | 精品国产一区二区三区久久久久久 | 超碰高清| 久视频在线观看 | 97伦理影院 | 日本视频免费 | 久久伊人影院 | 亚洲人成一区二区三区性色 | 国产高清在线精品一区二区三区 | 91视频网址 | 国产精品欧美一区二区三区 | 日日做夜夜爽毛片麻豆 | 一区二区av | 国产精品久久久久久久久久妇女 | 久久综合伊人 | 国产网站在线免费观看 | 国产一级一级 | 美女视频久久 | 国产黄色在线观看 | 在线免费黄色小视频 | 成人欧美一区二区 | 欧美日韩一区二区在线 | 99视频免费在线 | 国产精品乱码一区二区三区 | 国产精品成人一区 |