인터뷰어: Spring Aop 공통 주석 및 실행 순서

풀어 주다: 2023-08-15 16:32:49
앞으로
810명이 탐색했습니다.

최근 많은 분들을 대상으로 이력서 수정 및 모의면접을 하던 중 몇몇 친구들이 Spring AOP 면접 질문에 대한 피드백을 줬는데, 오늘 물어보겠습니다.

처음에 Spring의 가장 강력한 점은 IOC/AOP의 두 가지 핵심 기능입니다. 오늘은 Spring AOP의 공통 주석과 실행 순서에 대해 알아 보겠습니다.

Spring 인터뷰 핵심 포인트:

IOC, AOP, Bean 주입, Bean 수명 주기, Bean 순환 종속성

먼저 Spring Aop에서 일반적으로 사용되는 몇 가지 주석을 검토해 보겠습니다.

  • @Before 사전 알림: 대상 메서드 전에 실행됨 @Before 前置通知:目标方法之前执行
  • @After 后置通知:目标方法之后执行(始终执行)
  • @AfterReturning 返回之后通知:执行方法结束之前执行(异常不执行)
  • @AfterThrowing 异常通知:出香异常后执行
  • @Around
@After 게시물 알림 : 실행됨 대상 메소드 이후(항상 실행됨)

@ AfterReturning 반환 후 알림: 실행 메서드가 끝나기 전에 실행됨(예외는 실행되지 않음) @AfterThrowing 예외 알림: 향 예외 이후 실행

@Around Around 알림: Surround 대상 메서드 실행

🎜🎜🎜🎜🎜🎜FAQ🎜🎜🎜 🎜🎜🎜1. 꼭 알아두셔야 할 점 Spring, Aop의 모든 알림 순서에 대해 이야기해 보겠습니다. Spring Boot 또는 Spring Boot 2는 Aop의 실행 순서에 어떤 영향을 줍니까? 🎜🎜2. AOP에서 겪은 함정에 대해 알려주세요. 🎜

샘플 코드

Spring AOP에 대한 몇 가지 세부 사항을 논의하기 위해 Spring AOP 데모 프로그램을 빠르게 구축해 보겠습니다.

구성 파일

빠른 프로젝트 구축을 위해 spring-boot를 직접 활용하기 쉽도록 아이디어의 spring-boot 프로젝트 빠른 생성 기능을 이용하거나 에 들어가시면 됩니다. ="글꼴 크기: 14px; 패딩: 2px 4px; 테두리 반경: 4px; 여백 오른쪽: 2px; 여백 왼쪽: 2px; 배경 색상: rgba(27, 31, 35, 0.05); 글꼴 계열: "Operator Mono" , Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">start.spring.io 위의 스프링- 부팅 응용 프로그램. start.spring.io 上面去快速创建spring-boot 应用。

因为本人经常手动去网上贴一些依赖导致,依赖冲突服务启动失败等一些问题。

plugins {
    id 'org.springframework.boot' version '2.6.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group 'io.zhengsh'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/milestone' }
    maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
    # 其实这里也可以不增加 web 配置,为了试验简单,大家请忽略 
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}
로그인 후 복사

接口类

首先我们需要定义一个接口。我们这里可以再来回顾一下 JDK 的默认代理实现的选择:

  • 如果目标对象实现了接口,则默认采用JDK动态代理
  • 如果目标对象没有实现接口,则采用进行动态代理
  • 如果目标对象实现了接口,且强制Cglib,则使用cglib代理

这块的逻辑在 DefaultAopProxyFactory

몇 가지 종속성을 온라인에 수동으로 게시하는 경우가 많기 때문에 종속성 충돌이 발생합니다. 서비스 시작 실패와 같은 일부 문제.

public interface CalcService {

    public int div(int x, int y);
}
로그인 후 복사

Interface class

먼저 인터페이스를 정의해야 합니다. 여기에서 JDK의 기본 프록시 구현 선택을 검토할 수 있습니다.

  • 대상 객체인 경우 인터페이스가 구현되면 JDK 동적 프록시가 기본적으로 사용됩니다.
  • 대상 객체가 인터페이스를 구현하지 않는 경우 동적 프록시를 사용하세요
  • 대상 객체가 인터페이스를 구현하고 Cglib를 강제하는 경우 cglib 프록시를 사용하세요

논리 이 부분은 < code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px; background-color: rgba(27, 31, 35, 0.05);font- family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">DefaultAopProxyFactory 관심이 있으시면 , 살펴볼 수 있습니다.
@Service
public class CalcServiceImpl implements CalcService {

    @Override
    public int div(int x, int y) {
        int result = x / y;
        System.out.println("====> CalcServiceImpl 被调用了,我们的计算结果是:" + result);
        return result;
    }
}
로그인 후 복사
구현 클래스

🎜여기서는 간단히 나누기 연산을 수행해 보겠습니다. 이는 정상적인 연산을 시뮬레이션할 수 있고 오류도 쉽게 시뮬레이션할 수 있습니다. 🎜
@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* io.zhengsh.spring.service.impl..*.*(..))")
    public void divPointCut() {

    }

    @Before("divPointCut()")
    public void beforeNotify() {
        System.out.println("----===>> @Before 我是前置通知");
    }

    @After("divPointCut")
    public void afterNotify() {
        System.out.println("----===>> @After  我是后置通知");
    }

    @AfterReturning("divPointCut")
    public void afterReturningNotify() {
        System.out.println("----===>> @AfterReturning 我是前置通知");
    }

    @AfterThrowing("divPointCut")
    public void afterThrowingNotify() {
        System.out.println("----===>> @AfterThrowing 我是异常通知");
    }

    @Around("divPointCut")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object retVal;
        System.out.println("----===>> @Around 环绕通知之前 AAA");
        retVal = proceedingJoinPoint.proceed();
        System.out.println("----===>> @Around 环绕通知之后 BBB");
        return retVal;
    }
}
로그인 후 복사
로그인 후 복사
🎜🎜🎜aop Interceptor🎜🎜🎜🎜인터셉터를 선언하고 현재 객체에 @Aspect와 @Component를 추가해야 합니다. 작성자는 이전에 그런 구덩이를 밟았을 뿐이고 하나만 추가했습니다. 🎜

其实这块我刚开始也不是很理解,但是我看了 Aspect 注解的定义我就清楚了

인터뷰어: Spring Aop 공통 주석 및 실행 순서

这里面根本就没有 Bean 的定义。所以我们还是乖乖的加上两个注解。

还有就是如果当测试的时候需要开启Aop 的支持为配置类上增加@EnableAspectJAutoProxy 注解。

其实 Aop 使用就三个步骤:

  • 定义 Aspect 定义切面
  • 定义 Pointcut 就是定义我们切入点
  • 定义具体的通知,比如: @After, @Before 等。
@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* io.zhengsh.spring.service.impl..*.*(..))")
    public void divPointCut() {

    }

    @Before("divPointCut()")
    public void beforeNotify() {
        System.out.println("----===>> @Before 我是前置通知");
    }

    @After("divPointCut")
    public void afterNotify() {
        System.out.println("----===>> @After  我是后置通知");
    }

    @AfterReturning("divPointCut")
    public void afterReturningNotify() {
        System.out.println("----===>> @AfterReturning 我是前置通知");
    }

    @AfterThrowing("divPointCut")
    public void afterThrowingNotify() {
        System.out.println("----===>> @AfterThrowing 我是异常通知");
    }

    @Around("divPointCut")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object retVal;
        System.out.println("----===>> @Around 环绕通知之前 AAA");
        retVal = proceedingJoinPoint.proceed();
        System.out.println("----===>> @Around 环绕通知之后 BBB");
        return retVal;
    }
}
로그인 후 복사
로그인 후 복사

测试类

其实我这个测试类,虽然用了 @Test 注解,但是我这个类更加像一个 main 方法把:如下所示:

인터뷰어: Spring Aop 공통 주석 및 실행 순서

执行结论

结果记录:spring 4.x, spring-boot 1.5.9

无法现在依赖,所以无法试验

我直接说一下结论:Spring 4 中环绕通知是在最里面执行的

结果记录:spring 版本5.3.15 springboot 版本2.6.3

인터뷰어: Spring Aop 공통 주석 및 실행 순서
img

多切面的情况

多个切面的情况下,可以通过@Order指定先后顺序,数字越小,优先级越高。如下图所示:

인터뷰어: Spring Aop 공통 주석 및 실행 순서

代理失效场景

下面一种场景会导致 aop 代理失效,因为我们在执行 a 方法的时候其实本质是执行 AServer#a 的方法拦截器(MethodInterceptor)链, 当我们在 a 方法内直接执行b(), 其实本质就相当于 this.b() , 这个时候由执行 a方法是调用到 a 的原始对象相当于是 this 调用,那么会导致 b() 方法的代理失效。这个问题也是我们开发者在开发过程中最常遇到的一个问题。

@Service
public class AService {
    
    public void a() {
        System.out.println("...... a");
        b();
    }
    
    public void b() {
        System.out.println("...... b");
    }

}
로그인 후 복사

위 내용은 인터뷰어: Spring Aop 공통 주석 및 실행 순서의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:Java后端技术全栈
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿