Spring AOP 구현 프로세스 및 소스 코드 분석

머리말

AOP의 문자 적 ​​해석은 aspect 지향 프로그래밍입니다. Aspect 지향 프로그래밍은 프로그래밍 모델입니다. 우리는 JAVA가 객체 지향, 즉 OOP라는 것을 알고 있습니다. OOP의 객체 지향 프로그래밍은 수직 관계를 정의하는 데 적합하지만 그렇습니다. 수평 관계를 정의하는 데 적합하지 않습니다. 따라서 이러한 OOP 존재의 이러한 단점을 해결하기 위해 AOP 측면 지향 프로그래밍 모델은 객체 지향의 보완으로 사용되며, 이는 비즈니스와 관련이 없지만 일반적인 동작 및 논리를 추출하고 캡슐화하는 데 사용됩니다. 재사용 가능한 모듈로서이 모듈은 "Aspect"로 명명되어 시스템에서 반복되는 코드를 줄이고 모듈 간의 결합을 줄이며 시스템의 유지 보수성을 향상시킵니다. 권한 인증, 로그 및 트랜잭션 처리에 사용할 수 있습니다.

AOP 프록시 소개

AOP의 구현은 Spring AOP가 아닙니다. 우선 AOP 구현의 핵심은 proxy 모드에 있습니다. AOP proxy는 dynamic proxy와 static proxy로 나뉩니다. 아시다시피 Spring AOP는 dynamic에 속합니다. 프록시이며 정적 프록시의 대표는 AspectJ입니다.

정적 프록시

AspectJ는 정적 프록시의 향상입니다. 소위 정적 프록시는 AOP 프레임 워크가 컴파일 단계 중에 AOP 프록시 클래스를 생성한다는 것을 의미하므로 이는 컴파일 시간 향상이기도합니다. 정적 프록시는 AspectJ (aspects)를 컴파일 단계 동안의 자바 바이트 코드., 실행되면 확장 된 AOP 객체입니다.

동적 프록시

SpringAOP는 동적 프록시입니다. 이른바 동적 프록시는 AOP 프레임 워크가 바이트 코드 파일을 수정하지 않고 실행될 때마다 메모리에 0 시간에 해당 메소드에 대한 AOP 객체를 생성 함을 의미합니다.이 AOP 객체는 모든 메소드를 포함합니다. 대상 개체의. 그리고 특정 측면에서 향상된 처리 및 원래 개체의 콜백 메서드;

차이점

정적 프록시와 동적 프록시의 차이점은 AOP 프록시 객체를 생성하는 타이밍이 다르다는 점입니다 .AspectJ의 정적 프록시 방법에 비해 성능이 더 좋지만 AspectJ는 처리를 위해 특정 컴파일러가 필요하지만 SpringAOP는 특정 컴파일러가 필요하지 않습니다. 방법.

SpringAOP 동적 프록시

우리는 이미 SpringAOP가 동적 프록시에 의해 구현된다는 것을 알고 있습니다. 동적 프록시에는 JDK 동적 프록시와 CGLIB 동적 프록시의 두 가지 구현 방법이 있습니다! 프록시 클래스가 JDK 동적 프록시에서 InvocationHandler 인터페이스를 구현하지 않는 경우 SpringAOP는 CGLIB를 사용하여 대상 클래스를 동적으로 프록시하도록 선택합니다! 기본적으로 @EnableAspectJAutoProxy는 proxyTargetClass = true를 지정하지 않으며 (기본값은 false로 작성되지 않음) 비즈니스 클래스는 InvocationHandler 인터페이스 인터페이스를 구현합니다. 그러면 AOP는 JDK 동적 프록시를 사용하고 InvocationHandler 인터페이스를 구현하지 않는 경우 다음을 사용합니다. CGLIB 프록시, @EnableAspectJAutoProxy에 Specify proxyTargetClass = true가있는 경우, 비즈니스 클래스가 InvocationHandler 인터페이스를 구현하는지 여부에 관계없이 CGLIB를 사용해야하며, exposeProxy = 인 경우 참조 향상 (메소드 B가 메소드 A에서 호출 됨)에 관계없이 CGLIB를 사용해야합니다. true는 @EnableAspectJAutoProxy 주석에 지정되어 있습니다 (기본값은 false). 그러면 프록시 개체가 스레드 변수에 노출되므로 호출 할 때 스레드 변수에서 프록시 개체를 가져와 프록시 개체를 통해 호출 한 다음 방법이 향상되고 B 방법도 향상됩니다! 트랜잭션 A 메서드가 트랜잭션 메서드 B를 호출하는 것과 유사합니다.

  • JDK 동적 프록시는 인터페이스 프록시 만 제공하며 클래스 프록시는 지원하지 않습니다. 핵심은 Proxy 클래스입니다. InvocationHandle은 invoke () 메소드 리플렉션을 통해 대상 클래스의 코드를 호출하여 교차 절단 논리와 비즈니스를 동적으로 결합한 다음 프록시 InvocationHandle을 사용하여 특정 인터페이스를 준수하는 인스턴스를 동적으로 생성하여 대상 클래스의 프록시 객체를 생성합니다.

  • CGLIB (Code Generation Library)는 코드 생성을위한 클래스 라이브러리로 런타임에 지정된 클래스의 하위 클래스 개체를 동적으로 생성하고 특정 메서드를 재정의하고 AOP를 달성하기 위해 향상된 코드를 추가 할 수 있습니다. CGLIB는 통합 된 방식을 통한 동적 프록시이므로 클래스가 final로 표시되면 CGLIB를 동적 프록시로 사용할 수 없습니다.
    InvocationHandler

AOP 용어

Joinpoint

개선해야 할 방법입니다

PointCut

일련의 연결점을 접선 점이라고합니다.

양상

컷 포인트, 연결 포인트, 알림으로 구성된 클래스입니다.

조언

  • 사전 알림 (이전) : 비즈니스 코드를 실행하기 전에 연결 개체 획득과 같은 몇 가지 작업 수행
  • 알림 후 (이후) : 업무 코드 실행 후 작업을 수행하면 연결 객체를 닫는 등 예외 발생 여부에 관계없이 실행됩니다.
  • 예외 알림 (afterThrowing) : 트랜잭션 롤백과 같이 비즈니스 코드 실행 후 예외가 발생할 때 수행해야하는 작업
  • 반품 통지 (애프터 리턴), 업무 코드 실행 후 예외없이 수행되는 작업
  • 알림 (주변)과 관련하여 현재 논의중인 거래에 해당하는 작업이 없으므로 당분간은 설명하지 않겠습니다.


강화가 필요한 대상 비즈니스 객체

직조

위빙은 대상 클래스의 특정 연결 지점에 대한 개선 사항을 추가하는 프로세스입니다. Weaving은 생생한 용어로, 특히 프록시 개체를 생성하고 측면 콘텐츠를 비즈니스 프로세스에 통합하는 프로세스입니다.

대리

클래스가 AOP에 의해 짜여지고 향상되면 프록시 클래스가 생성됩니다.

SpringAOP 예제 코드

표적

@Service
public class A {
    
    
    public void f(){
    
    
        System.out.println("我被执行了");
    }
}

부분

@Component
@Aspect
public class MyAspect {
    
    
	
	//切点
    @Pointcut("execution(* com.xxx.xxx..*(..))")
    private void anyOldTransfcr(){
    
    }
	//com.xxx.xxx..*(..))这个中描述的每个方法为织入点

	//通知
    @Before("anyOldTransfcr()")
    public void advice(JoinPoint joinPoint){
    
    
        //String methodName=joinPoint.getSignature().getName();
        //System.out.println("执行的方法--->"+methodName);
        //System.out.println(Arrays.asList(joinPoint.getArgs()));
        System.out.println("--------开始执行-------");
    }

	//通知
    @After("anyOldTransfcr()")
    public void advice2(){
    
    
        System.out.println("--------结束执行-------");
    }

}

메인 시작 클래스

@EnableAspectJAutoProxy(proxyTargetClass = true)

补充:这里在切面中有个@Aspect这个注解,和上面提到的AspectJ静态代理其实是有点关系的,因为Spring在刚开始实现AOP的时候写的语法是不太友好的,然后Spring后面在迭代的过程中实现AOP就参照了AspectJ的语法,注意是语法!

시작 후 결과 실행
여기에 사진 설명 삽입

SpringAOP 소스 코드 구현 프로세스 분석

여기서 SpringIOC의 전체 프로세스를 살펴보아야합니다. 직설적으로 말하자면 SpringAOP는 IOC의 특정 부분에서 구현 된 함수입니다! SpringIOC 프로세스에 대해서는 Spring 소스 코드 분석 (프로세스 콤빙)을 참조하시기 바랍니다 . 이 글에서는 아래 그림이 있습니다.
여기에 사진 설명 삽입
AOP가 프록시 객체를 생성하는 과정은 위 그림에서 볼 수 있습니다. Bean 생성 과정 AOP 과정은 다음과 같이 나뉩니다.

  1. 패싯 조회
  2. 캐시 측면 알림
  3. pointcut 표현식을 통해 현재 빈이 위빙 포인트에 속하는지 확인
  4. 프록시 개체를 만드는 직조 지점입니다.
  5. 실행 대상 방법

1. 섹션 검색

Bean이 생성 될 때마다 AbstractAutowireCapableBeanFactory 클래스에 createBean 메소드로 들어갑니다.이 메소드에는 AOP에 대한 매우 중요한 코드가 있습니다.
여기에 사진 설명 삽입

		try {
    
    
			// 给BeanPostProcessors一个返回代理而不是目标bean实例的机会
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
    
    
				return bean;
			}
		}

이 코드 줄을 입력 한 후 여기에
여기에 사진 설명 삽입
계속 입력하는
여기에 사진 설명 삽입
것이 특히 중요합니다. AOP 함수 주석을 활성화 할 때 @EnableAspectJAutoProxy (proxyTargetClass = true)

이 주석은 AspectJAutoProxyRegistrar 클래스를 가져
여기에 사진 설명 삽입
오는 데 도움이됩니다. AspectJAutoProxyRegistrar 클래스를 입력하십시오.이 클래스는 ImportBeanDefinitionRegistrar 인터페이스를 구현하고 registerBeanDefinitions를 다시 작성합니다. 따라서 여기에 확실히 Bean을
여기에 사진 설명 삽입
여기에 사진 설명 삽입
등록 하고이 메소드를 입력 하여 AnnotationAwareAspectJAutoProxyCreator 등록 할 수 있습니다. 네,이 콩은 매우 중요합니다!

AOP 프로세스 코드로 돌아 가기

Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);

이 코드 줄은 ibp가 위에 주입 된 AnnotationAwareAspectJAutoProxyCreator 일 때 AbstractAutoProxyCreator 클래스의 postProcessBeforeInstantiation에 들어갑니다 .AnnotationAwareAspectJAutoProxyCreator의 최상위 수준이 AbstractAutoProxyCreator를 상속하므로 AbstractAutoProxyCreator 클래스에서 postProcessBeforeInstantiation 메서드를 호출 할 수 있기 때문입니다.
여기에 사진 설명 삽입
이 메소드 shouldSkip (beanClass, beanName)은 AspectJAwareAdvisorAutoProxyCreator 클래스의 shouldSkip 메소드를 호출합니다. 여기서
여기에 사진 설명 삽입
여기에 사진 설명 삽입
호출은 BeanFactoryAspectJAdvisorsBuilder 클래스의 buildAspectJAdvisors로 이동합니다.이 메소드는 aspect 클래스를 찾은 다음 aspect 클래스에 선언 된 알림 유형 세트를 반환하는 것입니다. .

2. 캐시 측면 알림

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

이 코드 줄에서 알림 유형 객체 컬렉션이 생성되고 aspect 클래스가 키로 사용되고 알림 객체 컬렉션이 현재 aspect 클래스의 값으로 키로 저장됩니다!
여기에 사진 설명 삽입
이 단계에서 aspect 클래스를 획득하고 aspect 클래스의 알림에 따라 알림 객체를 생성하는 과정은 시간이 많이 걸리므로 파싱 된 알림 객체가 캐시에 추가된다. 이후 Bean 생성이 여기에 오면, 먼저 aspectBeanNames가 비어 있는지 확인합니다. 비어 있으면 캐시에있는 데이터를 가져 와서

aspect 클래스의 알림 유형에 따라 알림 객체를 생성하는
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); 코드 라인을 항목으로 사용하고 마지막으로 ReflectiveAspectJAdvisorFactory 클래스에 getAdvice 메소드를 입력합니다.
여기에 사진 설명 삽입

주요 프로세스 돌아 가기, 다음 주 프로세스는 측면 클래스를 발견하고 통지 객체는, 다음에 원래 방법 및 반환 시작 여기에 캐시 된
여기에 사진 설명 삽입
주요 공정 및 콩을 만드는 과정을 입력하기 시작,
여기에 사진 설명 삽입
우리가 건너 의 과정을 속성 할당.보기 및 AOP는
여기에 사진 설명 삽입
초기화가 완료된 후 Bean 초기화 프로세스로 직접 수행되며
여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 키가 있으며 @EnableAspectJAutoProxy 주석 후 여기에서 열린 AOP 위에서 언급 한 BeanPostProcessor는
여기에 사진 설명 삽입
프로세서 가 AnnotationAwareAspectJAutoProxyCreator에 더 가깝습니다. 여기 AnnotationAwareAspectJAutoProxyCreator 객체 인 경우 AbstractAutoProxyCreator 클래스에 postProcessAfterInitialization 메소드를 입력합니다.
여기에 사진 설명 삽입
여기에 사진 설명 삽입
이 코드 줄은 1로 이동합니다. Aspect 클래스를 찾습니다. 2. Aspect 클래스에서 알림 객체를 캐시하지만이 작업 만 수행합니다. shouldSkip 메서드가 처음 호출 된 경우는 이미 1 번의 두 작업을 완료 한 후입니다. aspect 클래스를 찾고 2. aspect 클래스에서 알림 객체를 캐시하면 로직이 여기서 다시 실행되지 않지만 buildAspectJAdvisors ()를 입력 한 후에 메소드를 사용하면 buildAspectJAdvisors ()의 첫 번째 실행으로 직접 반환됩니다. 메소드에 의해 캐시 된 알림 객체의 결과 집합입니다!

3. pointcut 표현식을 통해 현재 빈이 위빙 포인트에 속하는지 확인

여기에 사진 설명 삽입
반환 값이 null이면 초기화가 필요한 Bean이 Proxy 객체를 생성 할 필요가 없음을 의미하며, 반환 값이 null이 아니면 Proxy 객체 생성을 시작한다.

4. 프록시 개체를 만드는 위빙 포인트

여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입
CGLIB를 사용하여 프록시 객체 생성여기에 사진 설명 삽입
하기 기사의 시작 부분에있는 Spring AOP 소개에서는 CGLIB가 동적 프록시를 완성하는 데 사용되는 이유를 설명합니다!

5. 대상 방법의 구현

여기에 사진 설명 삽입
f 메소드가 호출되면 프록시 객체에 들어가는 f 메소드입니다
여기에 사진 설명 삽입
.CGLIB는 동적 프록시를 완료하는 데 사용되기 때문에 지원하는 프록시 객체 실행자도 실행을 완료합니다 .CGLIB는 CglibAopProxy와 JDK는 JdkDynamicAopProxy에 의해 수행됩니다! 실제로 ReflectiveMethodInvocation 클래스의 progress () 메서드를 통해 최종적으로 실행됩니다!
여기에 사진 설명 삽입

//当前拦截器的的索引
this.currentInterceptorIndex

//拦截器集合
this.interceptorsAndDynamicMethodMatchers

//当拦截器索引等于拦截器集合数组长度-1时,执行目标方法
this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1

//执行目标方法
return invokeJoinpoint();

//完成当前拦截器获取和当前拦截器索引++操作
Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

//使用当前索引获取到的拦截器执行invoke方法逻辑
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

这里在invoke方法中传入的是this就是为了递归循环调用proceed()方法自己

여기에 사진 설명 삽입
각 재귀는 실제로 논리 코드 블록을 코드 블록에 추가 한 다음 실행합니다. 완전한 재귀 스켈레톤 코드는 위와 같습니다. 위에서 아래로 실행하십시오!

  1. AOP Aronud 이전에…
  2. 조언 전 AOP…
  3. 대상 메서드가 실행됩니다 ...
  4. AOP Aronud 이후…
  5. AOP After Advice…
  6. AOP AfterReturning Advice : (정상 실행이 완료되면 현재 메소드 실행)
  7. AOP AfterThrowing Advice ... (예외가 발생하면 현재 메소드가 실행 됨)

이 호출 흐름은 책임 + 재귀 체인에 의해 완료됩니다.

추천

출처blog.csdn.net/CSDN877425287/article/details/114328007