Spring 소스 코드 분석의 4 --- AOP 원리 와 소스 코드 분석

12690 단어 Spring
AOP 를 소개 하기 전에 많은 사람들 이 AOP 가 동적 대리 와 반 사 를 바탕 으로 이 루어 졌 다 고 들 었 을 것 이다. 그러면 AOP 를 보기 전에 동적 대리 와 반 사 를 확실히 알 아야 한다.
공교롭게도 나 에 게 도 있다.
JDK 의 동적 에이전트 의 실현 과 소스 코드 분석, 나의 이 두 글 참조
JDK 의 동적 에이전트 소스 분석 중 하나   (   http://blog.csdn.net/weililansehudiefei/article/details/73655925  )
JDK 의 동적 에이전트 소스 분석 2 ( http://blog.csdn.net/weililansehudiefei/article/details/73656923 )
위의 두 개 는 알 아 보 았 으 니 이 글 을 볼 수 있 습 니 다. 자바 반사 에 대해 서 는 알 면 됩 니 다. 하지만 더욱 공교롭게도 제 블 로그 에 자바 반 사 를 소개 하 는 한 편 이 있 습 니 다.
자바 반사 의 실현 과 기본 원리, 나의 이 글 참조
자바 반사 메커니즘 (http://blog.csdn.net/weililansehudiefei/article/details/70194940)
그럼 AOP 로 넘 어 가 는 코너 로 넘 어가 도록 하 겠 습 니 다.
AOP 는 절단면 을 대상 으로 프로 그래 밍 을 하 는 것 입 니 다. AOP 를 처음 배 웠 을 때 각종 AOP 의 개념 만 얼떨떨 하 게 만 들 었 습 니 다. 어떤 절단면, 접점, 알림, 짜 임, 연결 점, 목표 대상... AOP 의 원 리 를 보지 도 않 았 는데 이런 단어의 뜻 은 이미 사람들 이 보고 싶 지 않 습 니 다. 본인 은 AOP 를 실현 할 때 제 가 이해 하 는 AOP 의 용어, 대응 하 는 AOP 의 코드 와 움직임 을 설명 할 것 입 니 다.하 다
본 고 는 먼저 코드 에서 AOP 를 실현 하 는 데 착안 한 다음 에 AOP 의 바 텀 코드 와 그 원 리 를 분석 할 것 이다.
나중에 모든 프로젝트 구현 코드 를 GitHub 에 올 려 놓 고 그 때 는 글 을 업데이트 하 겠 습 니 다.
AOP 의 데모
만약 우리 대상 의 상속 관계 가 수직 관계 로 간주 된다 면, 마치 한 그루 의 나무 와 같다. 여러 개의 서로 다른 상속 관 계 는 한 줄 의 나무 와 같다. AOP 의 장점 은 바로 이 나무 들 에 대해 같은 조작 을 하고 싶다 는 것 이다. 이 럴 때, 세로 로 모든 나무 에 대해 조작 방법 을 정의 하지 않 고, 가로로 한 칼 로 잘라 서 그들 에 게 공 통 된 조작 방법 을 주어 야 한 다 는 것 이다.
Spring 의 AOP 는 JDK 의 동적 에이전트 와 Cglib 의 동적 에이 전 트 를 지원 합 니 다. JDK 의 동적 에이 전 트 는 인 터 페 이 스 를 위 한 것 이 고, Cglib 는 클래스 를 위 한 것 입 니 다. 본 고 는 JDK 의 동적 에이 전 트 를 위 한 것 입 니 다.
먼저 인 터 페 이 스 를 정의 합 니 다. 이름 을 지 을 때 이 인터페이스 이름 에 특별히 인 터 페 이 스 를 가 져 왔 습 니 다. 그러면 뒤에 더욱 주 의 를 끌 수 있 습 니 다. 인 터 페 이 스 는 간단 합 니 다. 그 안에 추상 적 인 방법 eat () 가 있 습 니 다.
package com.weili.cn;

/**
 * Created by weili on 17/6/27.
 */
public interface AnimalInterface {
    public abstract void eat();
}

실현 클래스: 하나의 먹 방 으로서 실현 클래스 에 당연히 chi 를 인쇄 해 야 합 니 다. chi  치.. 배 불 러 죽 겠 어!!!
이 실현 류 에는 오직 한 가지 방법 이 있 습 니 다. 이 방법 이 바로 AOP 의 절 점 입 니 다. 절 점 이라는 개념 자체 가 반드시 Method 는 아니 지만 Spring 에 서 는 모든 절 점 이 Method 입 니 다. 우리 가 강화 하 는 것 은 방법 입 니 다.
package com.weili.cn;

/**
 * Created by weili on 17/6/27.
 */
public class Animal implements AnimalInterface{

    public void eat() {
        System.out.println("Animal    chi  chi  chi");
    }
}

절단면 류 는 증강 류 라 고도 부른다. 우 리 는 이러한 방법 으로 원래 의 절 점 방법 을 강화 해 야 하기 때문이다. 절단면 류 에서 우리 가 실행 해 야 할 방법 을 통지 라 고 한다. 이른바 짜 서 통지 하 는 것 은 절단면 류 안의 방법 을 절 점 하 는 방법 과 연계 하 는 것 이다.
package com.weili.cn;

import org.aopalliance.intercept.Joinpoint;

/**
 * Created by weili on 17/6/27.
 */
public class AdviceAnimal {
    public void animalEmpty(){
        //System.out.println("joint before "+ joinPoint.getClass().getName());
        System.out.println("   ");
    }

    public void animalFull(){
        System.out.println("   ");
    }

    public void animalEat(){
        System.out.println("   ");
    }

}

다음은 xml 설정 을 통 해 xml 파일 에 AOP 를 설정 합 니 다.
설정 할 때 expressi 표현 식 을 통 해 com. weili. cn 이 가방 에 있 는 모든 종류의 방법 을 착안점 으로 정의 합 니 다. 즉, 이 가방 에 있 는 모든 방법 은 호출 이 실 행 될 때 Spring 에 의 해 강 화 됩 니 다. 구체 적 으로 이 절 점 방법 을 실행 하기 전과 그 후에 각각 animalEmpty 와 animalFull 방법 을 실행 합 니 다.









    
        
        
        
    


마지막 으로 호출 하 는 방법 입 니 다.
우리 가 spring - op. xml 분석 을 할 때 op 은 아직 실현 되 지 않 았 습 니 다. 두 번 째 줄 getBean 에서 야 진정 으로 op 을 진행 할 수 있 습 니 다. 구체 적 인 소스 코드 에 대해 서 는 설명 하 겠 습 니 다.
package com.weili.cn;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
        AnimalInterface animal = (AnimalInterface) ctx.getBean("animal");
        animal.eat();
    }
}

이 어 Output 입 니 다. 그래서 얻 은 bean 은 확실히 강 화 된 bean 입 니 다. 그럼 소스 코드 를 보 세 요.
   
Animal    chi  chi  chi
   

2. AOP 소스 코드 분석
원본 코드 는 이것 을 분석 합 니 다. 우선 bean 로 딩 입 니 다. 앞서 말 했 듯 이 AOP 탭 도 사용자 정의 탭 입 니 다. 이 탭 의 해석 도 이전 사용자 정의 탭 과 마찬가지 로 사용자 정의 탭 의 해석 절 차 를 밟 습 니 다. 다른 것 은 AOP 가 AOP 자체 의 해석 기 를 호출 하기 때 문 입 니 다. Spring 소스 코드 분석 2 - 사용자 정의 태그 분석 및 등록 사용자 정의 탭 의 분석 절 차 를 상세 하 게 설명 하 였 기 때문에 bean 탭 의 분석 등록 을 일일이 보지 않 습 니 다.
따라서 AOP 의 소스 코드 분석 은 호출 클래스 의 두 번 째 줄 인 ctx. getBean ("animal") 부터 시작 합 니 다. 디 버 깅 을 통 해 여기까지 왔 을 때 ctx 에서 분석 과 등 록 된 bean 을 볼 수 있 습 니 다. 먼저 살 펴 보 겠 습 니 다.
다음 그림 입 니 다. 이것 은 첫 번 째 줄 코드 가 실 행 된 후, ctx 의 각 속성 입 니 다. 다음 그림 에서 볼 수 있 습 니 다. singlenton Objects 에는 이미 대리 생 성 된 animal 이 저장 되 어 있 습 니 다. 생 층 bean 의 과정 은 이전의 과정 에서 비교적 명확 하 게 설명 되 었 습 니 다. 여 기 는 더 이상 설명 하지 않 습 니 다. AOP 잖 아 요. 우 리 는 그것 이 우리 가 실행 해 야 할 방법 전후 에 우 리 를 어떻게 하 는 지 알 아야 합 니 다.실행 할 방법 이 필요 합 니 다.
Spring源码解析之四 ------ AOP原理和源码分析_第1张图片
ctx. getBean ("animal") 에서 animal bean 을 가 져 온 다음 eat () 방법 을 사용 합 니 다. 이때 JdkDynamicAopProxy 류 의 invoke 방법 에 들 어 갑 니 다. 이 invoke 방법 에 서 는 프 록 시 targetClass 를 가 져 온 다음 method 와 targetClass 에 따라 이 방법 에 대응 하 는 차단 기 를 가 져 와 체인 체인 체인 을 실행 합 니 다.
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        TargetSource targetSource = this.advised.targetSource;
        Class> targetClass = null;
        Object target = null;

        Boolean var10;
        try {
            if(this.equalsDefined || !AopUtils.isEqualsMethod(method)) {
                if(!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    Integer var18 = Integer.valueOf(this.hashCode());
                    return var18;
                }

                Object retVal;
                if(!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                    return retVal;
                }

                if(this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                if(target != null) {
                    targetClass = target.getClass();
                }

                List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//     
                if(chain.isEmpty()) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                } else {
                    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    retVal = invocation.proceed();
                }

                Class> returnType = method.getReturnType();
                if(retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    retVal = proxy;
                } else if(retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                    throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
                }

                Object var13 = retVal;
                return var13;
            }

            var10 = Boolean.valueOf(this.equals(args[0]));
        } finally {
            if(target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }

            if(setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }

        }

        return var10;
    }

이 chain 의 내용 은 다음 과 같 습 니 다. 이름 을 통 해 알 수 있 듯 이 하 나 는 after Advice 이 고 하 나 는 before Advice 입 니 다. chain 을 가 져 온 후 MethodInvoke 방법 을 만 들 고 proceed 방법 을 실행 합 니 다.

proceed 방법 에 들 어 갑 니 다. current InterceptorIndex 의 초기 화 값 은 - 1 입 니 다. 이 어 invoke 방법 과 같 습 니 다. 여기 this 는 우리 의 eat 방법 입 니 다.
  public Object proceed() throws Throwable {
        if(this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.invokeJoinpoint();
        } else {
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)?dm.interceptor.invoke(this):this.proceed();
            } else {
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }
invoke 방법 에서 여기 미 는 우리 인터페이스 에 있 는 eat 방법 입 니 다. 그리고 미의 proceed () 방법 을 실행 합 니 다.
    public Object invoke(MethodInvocation mi) throws Throwable {
        MethodInvocation oldInvocation = (MethodInvocation)invocation.get();
        invocation.set(mi);

        Object var3;
        try {
            var3 = mi.proceed();
        } finally {
            invocation.set(oldInvocation);
        }

        return var3;
    }
이 럴 때 는 계속 시작 하 는 proced 방법 으로 돌아 갑 니 다. 이때 얻 은 것 은?
interceptorOrInterceptionAdvice,         list      ,after     。        ,         before  。
     before     ,
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
      invokeJoinpoint  ,
 
  
    public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable {
        try {
            ReflectionUtils.makeAccessible(method);
            return method.invoke(target, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method + "] on target [" + target + "]", var5);
        } catch (IllegalAccessException var6) {
            throw new AopInvocationException("Could not access method [" + method + "]", var6);
        }
    }
}
 
  
 
  
    after  。

좋은 웹페이지 즐겨찾기