91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring實現類私有方法是什么意思

發布時間:2021-06-17 09:21:06 來源:億速云 閱讀:170 作者:chen 欄目:開發技術

本篇內容介紹了“Spring實現類私有方法是什么意思”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

現實的業務場景中,可能需要對Spring的實現類的私有方法進行測試。

場景描述:

比如XXXService里有 兩個函數a、函數b。

而實現類XXXServiceImpl中實現了函數a、函數b,還包含私有方法函數c和函數d。

要寫一個XXXTestController來調用XXXServiceImpl的函數c。

面臨幾個問題:

1、如果注入接口,則無法調用實現類的私有類。

2、如果注入實現類,則需要將實現類里的私有方法改為公有的,而且需要設置@EnableAspectJAutoProxy(proxyTargetClass = true)使用CGLIB代理方式

如果單純為了測試而接口中定義實現類的私有方法或者為了測試而將私有方法臨時改為公有方法,顯然不太合適。

解決方案:

可以通過CGLIB注入實現類的子類,如果是Gradle項目也可以使用Aspect插件,將切面代碼在編譯器織入實現類中注入的類型則為實現類,然后通過反射設置為可訪問來調用私有方法。

方案一 使用BeanUtils.findDeclaredMethod反射方法

反射調用代碼:

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。

如果需要返回值,可以獲取該調用方法的返回值。

方案二:使用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實現類私有方法是什么意思”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

海原县| 固原市| 南川市| 新兴县| 三河市| 泌阳县| 东丽区| 南平市| 溆浦县| 湖北省| 正宁县| 祁东县| 苍南县| 华容县| 潜山县| 壶关县| 墨竹工卡县| 高唐县| 南昌市| 兰溪市| 中超| 马山县| 潜江市| 宁武县| 黔江区| 文登市| 屏东县| 南华县| 革吉县| 东港市| 界首市| 大理市| 中卫市| 灵寿县| 南平市| 莒南县| 藁城市| 古浪县| 金塔县| 丰台区| 拜城县|