Spring 은 개인 적 인 방법 을 실현 하 는 몇 가지 문제(친 측 통용 해결 방안)

현실 적 인 업무 장면 에 서 는 Spring 의 실현 류 의 개인 적 인 방법 을 테스트 해 야 할 수도 있다.
필드 설명:
예 를 들 어 XXXService 에는 두 개의 함수 a,함수 b 가 있다.
그리고 실현 류 XXXServiceImpl 에서 함수 a,함수 b 를 실 현 했 고 개인 적 인 방법 함수 c 와 함수 d 도 포함 했다.
XXXTTestController 를 써 서 XXXTServiceImpl 의 함수 c 를 호출 하려 고 합 니 다.
몇 가지 문제 에 직면 하 다.
1.인 터 페 이 스 를 주입 하면 실현 류 의 개인 류 를 호출 할 수 없습니다.
2.구현 클래스 를 주입 하려 면 구현 클래스 의 개인 적 인 방법 을 공유 로 바 꾸 고@EnableaspectJAutoProxy(proxy TargetClass=true)를 설정 하여 CGLIB 프 록 시 방식 을 사용 해 야 합 니 다.
단순히 테스트 를 위해 인터페이스 에서 실현 류 의 사유 방법 을 정의 하거나 테스트 를 위해 사유 방법 을 임시로 공유 방법 으로 바 꾸 는 것 은 적절 하지 않다.
해결 방안:
CGLIB 를 통 해 구현 클래스 의 하위 클래스 를 주입 할 수 있 으 며,Gradle 프로젝트 라면 Aspect 플러그 인 을 사용 할 수 있 으 며,절단면 코드 를 컴 파일 러 에서 구현 클래스 에 주입 하 는 유형 은 구현 클래스 로 만 든 다음 반사 설정 을 통 해 접근 가능 한 개인 방법 으로 호출 할 수 있 습 니 다.
방안 은 BeanUtils.findDeclared Method 반사 방법 을 사용 합 니 다.
반사 호출 코드:
BeanInvokeUtil

public class BeanInvokeUtil {
 
  public class InvokeParams {
 
//     (  )
    private String methodName;
 
//         
    private Class<?>[] paramTypes;
//      
    private Object object;
 
//       (    null,   paramTypes  )
    private Object[] args;
 
    public InvokeParams() {
      super();
    }
 
    public InvokeParams(Object object, String methodName, Class<?>[] paramTypes, Object[] args) {
      this.methodName = methodName;
      this.paramTypes = paramTypes;
      this.object = object;
      this.args = args;
    }
 
    public String getMethodName() {
      return methodName;
    }
 
    public void setMethodName(String methodName) {
      this.methodName = methodName;
    }
 
    public Class<?>[] getParamTypes() {
      return paramTypes;
    }
 
    public void setParamTypes(Class<?>[] paramTypes) {
      this.paramTypes = paramTypes;
    }
 
    public Object getObject() {
      return object;
    }
 
    public void setObject(Object object) {
      this.object = object;
    }
 
    public Object[] getArgs() {
      return args;
    }
 
    public void setArgs(Object[] args) {
      this.args = args;
    }
  }
 
  public static Object invokePrivateMethod(InvokeParams invokeParams) throws InvocationTargetException, IllegalAccessException {
    //     
    checkParams(invokeParams);
    //   
    return doInvoke(invokeParams);
  }
 
  private static Object doInvoke(InvokeParams invokeParams) throws InvocationTargetException, IllegalAccessException {
    Object object = invokeParams.getObject();
    String methodName = invokeParams.getMethodName();
    Class<?>[] paramTypes = invokeParams.getParamTypes();
    Object[] args = invokeParams.getArgs();
 
    Method method;
    if (paramTypes == null) {
      method = BeanUtils.findDeclaredMethod(object.getClass(), methodName);
    } else {
      method = BeanUtils.findDeclaredMethod(object.getClass(), methodName, paramTypes);
    }
    method.setAccessible(true);
    if (args == null) {
      return method.invoke(object);
    }
    return method.invoke(object, args);
 
  }
 
  private static void checkParams(InvokeParams invokeParams) {
    Object object = invokeParams.getObject();
    if (object == null) {
      throw new IllegalArgumentException("object can not be null");
    }
    String methodName = invokeParams.getMethodName();
    if (StringUtils.isEmpty(methodName)) {
      throw new IllegalArgumentException("methodName can not be empty");
    }
 
    //               
    Class<?>[] paramTypes = invokeParams.getParamTypes();
    Object[] args = invokeParams.getArgs();
 
    boolean illegal = true;
    if (paramTypes == null && args != null) {
      illegal = false;
    }
    if (args == null && paramTypes != null) {
      illegal = false;
    }
    if (paramTypes != null && args != null && paramTypes.length != args.length) {
      illegal = false;
    }
    if (!illegal) {
      throw new IllegalArgumentException("paramTypes length != args length");
    }
  }
}
사용 방법:
사용 시 CGLIB 방식 으로 구현 클래스 를 주입 하거나 절단면 코드 컴 파 일 러 를 구현 클래스 에 짜 서 Bean 을 주입 합 니 다.

@Autowired private XXXService xxxService;
그 다음 에 호출 대상 을 입력 하고 호출 할 개인 적 인 방법,매개 변수 유형 배열 과 매개 변수 배열 을 입력 합 니 다.

BeanInvokeUtil.invokePrivateMethod(new BeanInvokeUtil()
            .new InvokeParams(xxxService, "somePrivateMethod", null, null));
이때 주입 한 xxxService 의 유형 은 xxxServiceImpl 입 니 다.
반환 값 이 필요 하 다 면 이 호출 방법의 반환 값 을 가 져 올 수 있 습 니 다.
프로젝트 2:jdk 와 cglib 도 구 를 사용 하여 실제 대상 을 가 져 옵 니 다.
테스트 클래스

public class Test {
  @Autowired
  ServiceImpl serviceImpl;

  @Test
  public void test() throws Exception {
  Class<?> clazz = Class.forName("ServiceImpl");
  Method method = clazz.getDeclaredMethod("MethodA");
  method.setAccessible(true);
  Object target = ReflectionUtil.getTarget(serviceImpl);
  //   ,       serviceImpl,      spring  ,
  //   serviceImpl        ,            ,             
  method.invoke(target);
  }
}
spring 프 록 시 대상 의 실제 인 스 턴 스 를 가 져 오 는 도구 류 는 두 가지 동적 프 록 시 상황 에 적 용 됩 니 다:jdk 와 cglib

public class ReflectionUtil {
  /**
   *   spring          
   * @param proxy     
   * @return
   * @throws Exception
   */
  public static Object getTarget(Object proxy) throws Exception {
    if(!AopUtils.isAopProxy(proxy)) {
      return proxy;//      
    }

    if(AopUtils.isJdkDynamicProxy(proxy)) {
      return getJdkDynamicProxyTargetObject(proxy);
    } else { //cglib
      return getCglibProxyTargetObject(proxy);
    }
  }

  private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
    Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
    h.setAccessible(true);
    Object dynamicAdvisedInterceptor = h.get(proxy);
    Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
    advised.setAccessible(true);
    Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
    return target;
  }

  private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
    Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
    h.setAccessible(true);
    AopProxy aopProxy = (AopProxy) h.get(proxy);
    Field advised = aopProxy.getClass().getDeclaredField("advised");
    advised.setAccessible(true);
    Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
    return target;
  }
}
그리고 더 좋 은 오픈 소스 도구 인 PowerMock 도 있 습 니 다https://github.com/powermock/powermock관심 있 는 학생 은 연구 해 보 세 요.
이상 은 Spring 실현 류 사유 방법 테스트 유 니 버 설 방안 의 상세 한 내용 입 니 다.Spring 류 사유 방법 에 관 한 자 료 는 다른 관련 글 에 주목 하 시기 바 랍 니 다!

좋은 웹페이지 즐겨찾기