Spring 사용자 정의 주석 이 유효 하지 않 은 원인 분석 및 해결 방법

사용자 정의 주석 이 유효 하지 않 은 원인 분석 및 해결 방법
배경:
프로젝트 에 서 는 spring AOP 를 기반 으로 자바 캐 시 주 해 를 실 현 했 습 니 다.그러나 최근 한 가지 상황 이 발생 했다. 캐 시가 효력 이 발생 하지 않 았 고 대량의 요청 이 db 층 에 뚫 려 서 db 의 압력 이 너무 크다.지금 우 리 는 구체 적 인 코드 상황 을 살 펴 보 자.
interface A {
    int method1(..);
    int method2(..);
    ... ...
}

class AImpl implements A {
    @Override
    @CacheMM(second=600)      //   @CacheMM             
    public int method1(..) {
        ... ...
        method2(..);
        ... ...
    }
    
    @Override
    @CacheMM(second=600)
    public int method2(..) {
        ... ...
    }
}

위의 코드 와 같이 method 1 을 호출 할 때 method 2 주석 이 유효 하지 않 음 을 발견 합 니 다.
분석:
왜 그 럴 까요?서 두 르 지 마라, 우 리 는 이 문 제 를 가지 고 주해 의 실현 류 를 보 러 갔다.(여 기 는 캐 시 주해 의 실현 코드 를 붙 이지 않 습 니 다) 제 사용자 정의 주 해 는 extends AbstractBean Factory PointcutAdvisor 류 를 직접 확장 한 다음 에 getPointcut () 과 getAdvice () 를 실현 합 니 다.(사실 여 기 는 op 서 라운드 알림 을 직접 사용 할 수 있 습 니 다. 원리 가 많 지 않 습 니 다. 저 는 소스 코드 를 익히 기 위해 이렇게 썼 습 니 다)
그 다음 에 우 리 는 계속 분석 을 해 보 았 습 니 다. 우 리 는 spring op 을 바탕 으로 하 는 주 해 를 알 고 있 습 니 다. spring 에서 op 이 실현 되면 용 기 는 이러한 대리 류 를 주입 합 니 다. 이곳 의 대리 류 는 op 동적 대리 가 생 성 하 는 대리 류 입 니 다.Spring op 의 동적 대 리 는 두 가지 가 있 습 니 다. 하 나 는 jdk 의 동적 대리 이 고 하 나 는 CGLIB 를 바탕 으로 합 니 다.이 두 가지 차 이 는 더 이상 말 하지 않 겠 습 니 다. 만약 당신 의 업무 유형 이 인터페이스 에 기반 하여 이 루어 진다 면 jdk 동적 대 리 를 사용 하 십시오. 그렇지 않 으 면 CGLIB 동적 대 리 를 사용 합 니 다.내 가 여기 서 사용 하 는 것 은 인터페이스 실현 이기 때문에 우 리 는 생각 을 따라 jdk 동적 대리 의 구체 적 인 실현 을 살 펴 보 자.
위의 업무 코드 류 를 나 는 이미 붙 였 다.프 록 시 대상 (proxy) 을 만 들 고 두 단계 로 나 누 어야 합 니 다.
  • 대리 대상 생 성 시 대리 대상 (proxy) 과 실제 대상 (AImpl) 의 대리 관계 구축
  • 대리 방법 실현
  • JDK 동적 에이전트 에서 인 터 페 이 스 를 구현 해 야 합 니 다: java. lang. reflect. InvocationHandler.
    import java.lang.reflect.InvocationHandler;  
    import java.lang.reflect.Method;   
    import java.lang.reflect.Proxy;   
      
    public class AProxy implements InvocationHandler   
    {   
        private Object target;   
          
        /**  
         *       ,          .  
         * @param target         
         * @return      */   
        public Object bind(Object target)   
        {   
            this.target = target;   
              
            //      ,   .   
            Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //        
                                  target.getClass().getInterfaces(), //     ,                
                                  this);//     ,this        ,        InvocationHandler   invoke     
              
            return proxy;   
        }   
              
        /**  
         *         ,       AProxy     ,                 。  
         * @param proxy       
         * @param method          
         * @param args       
         * @return       。  
         * @throws Throwable       */   
         @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable   
         {   
            System.err.println("         ");   
            Object obj = method.invoke(target, args);//   AImpl        .   
            System.err.println("         ");   
              
            return obj;   
        }  
    }  

    코드 에 서 는 Object obj = method. invoke (target, args) 가 실제 대상 을 반사 적 으로 스케줄 링 하 는 방법 이 중요 합 니 다.우 리 는 사실 op 이 대리 대상 을 통 해 추가 적 인 조작 을 실현 하지만 진정한 방법 으로 호출 하 는 것 은 실제 대상 을 반사 적 으로 호출 하 는 것 임 을 알 고 있다.이때 우 리 는 문 제 를 돌 이 켜 보 았 다. 우리 AImpl 에는 두 가지 방법 이 있 는데 그 중에서 method 2 는 method 1 내부 에서 호출 된 것 이다.method 1 을 호출 할 때 spring 내 부 는 에이전트 류 AProxy 류 의 invoke 를 호출 합 니 다. 이 때 실제 대상 방법 돈 을 실행 하여 method 1 의 추가 작업 을 수행 합 니 다.그리고 반 사 를 통 해 대응 하 는 AImpl 류 에 들 어가 method 1 방법 을 호출 합 니 다.이 때 는 프 록 시 대상 에서 작 동 하지 않 습 니 다. method 2 의 호출 은 method 1 내부 에서 호출 되 었 기 때문에 여기 서 실제 method 2 를 호출 하 는 것 은 실제 대상 이지 프 록 시 대상 이 아 닙 니 다.따라서 method 2 의 캐 시 주석 이 유효 하지 않 습 니 다.
    해결:
    자, 이제 문제 의 원인 을 알 게 된 후에 (동적 대리 의 구덩이 야, 내부 호출 이 대리 류 를 가지 지 않 기 때문에 실 현 된 부가 작업 은 반드시 실행 되 지 않 을 것 이다) 우 리 는 목적 성 있 게 해결 할 것 이다.우 리 는 지금 이것 이 실제 실 행 된 것 이 대리 류 가 아니 기 때문에 발생 한 것 이라는 것 을 알 고 있다. 그러면 우리 가 해결 하 는 방향 은 method 2 의 대리 류 를 호출 시 키 면 된다.(이렇게 간단 하 다)
    AProxy 류 는 spring 용기 에서 얻 을 수 있 습 니 다.다음은 수 정 된 해결 방안 입 니 다.
    method1(..) {
        ... ...
         //                ,                   ,         ,        
            if(null != AopContext.currentProxy()){  
                AopContext.currentProxy().method2();  
            }else{  
                method2();  
            }      
    
    }

    이곳 의 AopContext. currentProxy () 가 받 은 것 은 실제 대리 대상 입 니 다. 이렇게 하면 대리 대상 을 통 해 method 2 를 호출 하 는 데 문제 가 없 을 것 입 니 다.
    또 하나의 해결 방법 은 동적 프 록 시 를 사용 하지 않 고 aspectJ 를 사용 하여 짜 는 것 이다. aspectJ 는 프 록 시 방식 이 아니 라 소스 클래스 에 바이트 코드 를 직접 삽입 하 는 것 이다.
    여기 서 AspectJ 컴 파일 시 짜 기 (Compile Time Weaving, CTW) 를 참고 할 수 있 습 니 다.
    이렇게 바 꾸 는 것 이 비교적 크기 때문에, 현재 나 는 여전히 첫 번 째 방안 을 채택 하여 문 제 를 해결 하 였 다.이로써 문 제 는 해결 되 었 다.
    요약:
    Spring op 동적 에이전트 의 실현 원 리 를 결합 하여 두 가지 동적 대 리 를 제공 합 니 다: JDK 대리 와 CGLIB 대리
    JDK 에이 전 트 는 인 터 페 이 스 를 실현 한 클래스 만 생 성 할 수 있 을 뿐 클래스 를 대상 으로 할 수 없습니다.CGLIB 는 클래스 에 대해 대 리 를 수행 하 는 것 으로 지 정 된 클래스 에 하위 클래스 를 생 성하 고 덮어 쓰 는 방법 이 며, 계승 이기 때문에 final 로 클래스 나 방법 을 수식 할 수 없다.그래서 이런 종류 나 방법 은 final 로 밝 히 지 않 는 게 좋 을 것 같 아 요.
    더 상세 한 설명 은 이 박문 에서 어떤 방법 으로 Spring AOP 업 무 를 실시 할 수 없 는 지 참고 할 수 있다.

    좋은 웹페이지 즐겨찾기