偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

開發(fā) 前端
除了服務(wù)器程序,在一些需要頻繁進(jìn)行資源共享和線程協(xié)作的場(chǎng)景中,死鎖也可能隨時(shí)出現(xiàn)。比如在一個(gè)多線程的文件處理系統(tǒng)中,多個(gè)線程可能需要同時(shí)訪問和修改同一個(gè)文件,如果對(duì)文件資源的訪問控制不當(dāng),就很容易引發(fā)死鎖,導(dǎo)致文件處理出錯(cuò),數(shù)據(jù)丟失等嚴(yán)重后果。

面試回答

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

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

Pointcut(切點(diǎn)): 切點(diǎn)是匹配連接點(diǎn)的表達(dá)式,決定Advice在哪些方法上執(zhí)行。Spring使用AspectJ的表達(dá)式語(yǔ)言定義切點(diǎn)。

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

Advice參數(shù): 通過JoinPoint對(duì)象可以獲取目標(biāo)方法的簽名、參數(shù)等信息,還可以自定義參數(shù)綁定,實(shí)現(xiàn)更靈活的邏輯處理。


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

詳細(xì)解析

1. Aspect(切面)

切面是AOP的核心模塊化單元,它封裝了跨多個(gè)類的橫切關(guān)注點(diǎn)。

在Spring中,切面通過@Aspect注解定義,簡(jiǎn)單說來(lái)就是需要增強(qiáng)的代碼邏輯

package com.qy.aop;

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

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

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

2. Advice(通知)

通知定義了切面在特定連接點(diǎn)執(zhí)行的動(dòng)作,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 {

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

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

    // 返回通知:方法正常返回后
    @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());
    }

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

各種通知類型的特點(diǎn)和使用場(chǎng)景:

  • @Before: 適合做參數(shù)校驗(yàn)、權(quán)限檢查等前置工作
  • @After: 適合做資源釋放等必須執(zhí)行的操作
  • @AfterReturning: 適合對(duì)返回結(jié)果進(jìn)行處理或記錄
  • @AfterThrowing: 適合做異常處理、日志記錄等
  • @Around: 功能最強(qiáng)大,可以完全控制方法執(zhí)行,適合做性能監(jiān)控、事務(wù)控制等

3. Pointcut(切點(diǎn))

切點(diǎn)是匹配連接點(diǎn)的表達(dá)式,定義了Advice在哪些方法上執(zhí)行。Spring采用AspectJ表達(dá)式語(yǔ)言:

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() {}
    
    // 組合切點(diǎn):所有service或dao包中的方法
    @Pointcut("serviceLayer() || dataAccessLayer()")
    public void businessLogicLayer() {}
    
    // 帶有@Transactional注解的方法
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
}

常用的切點(diǎn)表達(dá)式:

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

4. JoinPoint(連接點(diǎn))

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

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) {
        // 獲取目標(biāo)方法簽名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 獲取目標(biāo)對(duì)象
        Object target = joinPoint.getTarget();
        
        // 獲取方法參數(shù)
        Object[] args = joinPoint.getArgs();
        
        System.out.println("目標(biāo)類: " + target.getClass().getName());
        System.out.println("方法名: " + method.getName());
        System.out.println("參數(shù)列表: " + Arrays.toString(args));
        System.out.println("方法修飾符: " + method.getModifiers());
        System.out.println("方法返回類型: " + method.getReturnType().getName());
    }
}

JoinPoint對(duì)象提供的關(guān)鍵信息:

  • getSignature(): 獲取方法簽名
  • getTarget(): 獲取目標(biāo)對(duì)象
  • getArgs(): 獲取方法參數(shù)
  • getThis(): 獲取代理對(duì)象
  • getKind(): 獲取連接點(diǎn)類型

環(huán)繞通知中使用的是ProceedingJoinPoint,它擴(kuò)展了JoinPoint接口,添加了proceed()方法用于執(zhí)行目標(biāo)方法。

5. Advice參數(shù)

Advice方法可以接收參數(shù),增強(qiáng)其靈活性:

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獲取參數(shù)
    @Before("execution(* com.qy.service.UserService.findById(Long))")
    public void beforeFindById(JoinPoint joinPoint) {
        Long userId = (Long) joinPoint.getArgs()[0];
        System.out.println("查詢用戶ID: " + userId);
    }
    
    // 直接綁定參數(shù)
    @Before("execution(* com.qy.service.UserService.findById(Long)) && args(userId)")
    public void beforeFindByIdWithParam(Long userId) {
        System.out.println("準(zhǔn)備查詢用戶ID: " + userId);
    }
    
    // 綁定注解參數(shù)
    @Before("@annotation(audit)")
    public void auditMethod(JoinPoint joinPoint, com.qy.annotation.Audit audit) {
        System.out.println("審計(jì)記錄: " + audit.operation());
        System.out.println("操作方法: " + joinPoint.getSignature().getName());
    }
}

參數(shù)綁定的主要方式:

  1. 通過JoinPoint對(duì)象獲取
  2. 通過切點(diǎn)表達(dá)式中的args()綁定
  3. 通過注解屬性綁定

AOP的工作流程

  1. 定義切面類(Aspect),包含切點(diǎn)(Pointcut)和通知(Advice)
  2. Spring容器啟動(dòng)時(shí)識(shí)別@Aspect注解的Bean
  3. 根據(jù)切點(diǎn)表達(dá)式匹配目標(biāo)Bean的方法,創(chuàng)建代理對(duì)象
  4. 當(dāng)調(diào)用目標(biāo)方法時(shí),代理對(duì)象攔截調(diào)用并按順序執(zhí)行相應(yīng)的通知

實(shí)際應(yīng)用場(chǎng)景

AOP在實(shí)際開發(fā)中有廣泛應(yīng)用:

  1. 日志記錄:記錄方法調(diào)用、參數(shù)、執(zhí)行時(shí)間等
  2. 事務(wù)管理:聲明式事務(wù)控制
  3. 安全控制:權(quán)限檢查、認(rèn)證
  4. 性能監(jiān)控:統(tǒng)計(jì)方法執(zhí)行時(shí)間
  5. 緩存處理:方法結(jié)果緩存
  6. 異常處理:統(tǒng)一異常處理和日志
  7. 重試機(jī)制:失敗自動(dòng)重試

使用AOP實(shí)現(xiàn)日志

下面是一個(gè)完整的使用Spring AOP實(shí)現(xiàn)日志功能的例子

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

業(yè)務(wù)服務(wù)類

package com.qy.service;

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

@Service
publicclass UserService {
    
    public User getUserById(Long id) {
        // 模擬業(yè)務(wù)邏輯
        if (id <= 0) {
            thrownew IllegalArgumentException("用戶ID必須大于0");
        }
        
        // 模擬從數(shù)據(jù)庫(kù)查詢用戶
        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;
    }
}

用戶實(shí)體類

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 + "'}";
    }
}

主應(yīng)用類

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 {
            // 測(cè)試正常情況
            User user = userService.getUserById(1L);
            System.out.println("獲取到用戶: " + user);
            
            // 修改用戶并更新
            user.setUsername("修改后的用戶名");
            userService.updateUser(user);
            
            // 測(cè)試異常情況
            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 {
    
}

當(dāng)應(yīng)用運(yùn)行時(shí),AOP會(huì)自動(dòng)攔截UserService中的方法調(diào)用并輸出日志,日志輸出示例

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


責(zé)任編輯:武曉燕 來(lái)源: 玄武后端技術(shù)棧
相關(guān)推薦

2023-02-01 09:15:41

2024-12-20 17:29:34

SpringBootAOP開發(fā)

2021-10-27 10:12:54

DockerContainerdRunC

2023-02-24 08:03:24

ChatGPT人臉識(shí)別分支

2025-03-12 09:36:23

AspectJAOP開發(fā)

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

區(qū)塊鏈加密貨幣比特幣

2024-12-30 07:20:00

Redis數(shù)據(jù)庫(kù)MySQL

2016-03-21 10:40:53

RDDSpark SQL數(shù)據(jù)集

2024-06-11 00:04:00

對(duì)象AdvisorAdvice

2018-05-21 21:26:59

Apache HiveHbaseSQL

2015-02-26 10:29:41

Google百度

2024-12-09 07:10:00

限流系統(tǒng)

2022-10-26 15:10:46

CSS數(shù)據(jù)開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)