最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring實(shí)現(xiàn)類私有方法的幾個(gè)問題(親測通用解決方案)

 更新時(shí)間:2021年06月16日 17:17:10   作者:satire  
現(xiàn)實(shí)的業(yè)務(wù)場景中,可能需要對(duì)Spring的實(shí)現(xiàn)類的私有方法進(jìn)行測試。本文給大家分享Spring實(shí)現(xiàn)類私有方法面臨的幾個(gè)問題及解決方案,感興趣的朋友跟隨小編一起看看吧

現(xiàn)實(shí)的業(yè)務(wù)場景中,可能需要對(duì)Spring的實(shí)現(xiàn)類的私有方法進(jìn)行測試。

場景描述:

比如XXXService里有 兩個(gè)函數(shù)a、函數(shù)b。

而實(shí)現(xiàn)類XXXServiceImpl中實(shí)現(xiàn)了函數(shù)a、函數(shù)b,還包含私有方法函數(shù)c和函數(shù)d。

要寫一個(gè)XXXTestController來調(diào)用XXXServiceImpl的函數(shù)c。

面臨幾個(gè)問題:

1、如果注入接口,則無法調(diào)用實(shí)現(xiàn)類的私有類。

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

如果單純?yōu)榱藴y試而接口中定義實(shí)現(xiàn)類的私有方法或者為了測試而將私有方法臨時(shí)改為公有方法,顯然不太合適。

解決方案:

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

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

反射調(diào)用代碼:

BeanInvokeUtil

public class BeanInvokeUtil {
 
  public class InvokeParams {
 
// 方法名稱(私有)
    private String methodName;
 
// 參數(shù)列表類型數(shù)組
    private Class<?>[] paramTypes;
// 調(diào)用的對(duì)象
    private Object object;
 
// 參數(shù)列表數(shù)組(如果不為null,需要和paramTypes對(duì)應(yīng))
    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 {
    // 參數(shù)檢查
    checkParams(invokeParams);
    // 調(diào)用
    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");
    }
 
    // 參數(shù)類型數(shù)組和參數(shù)數(shù)組要對(duì)應(yīng)
    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");
    }
  }
}

使用方式:

使用時(shí)通過CGLIB方式注入實(shí)現(xiàn)類或者將切面代碼編譯器織入實(shí)現(xiàn)類的方式,然后注入Bean。

@Autowired private XXXService xxxService;

然后填入調(diào)用的對(duì)象,待調(diào)用的私有方法,參數(shù)類型數(shù)組和參數(shù)數(shù)組。

BeanInvokeUtil.invokePrivateMethod(new BeanInvokeUtil()
            .new InvokeParams(xxxService, "somePrivateMethod", null, null));

注意這時(shí)注入的xxxService的類型為 xxxServiceImpl。

如果需要返回值,可以獲取該調(diào)用方法的返回值。

方案二:使用jdk和cglib工具獲取真實(shí)對(duì)象

測試類

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,因?yàn)樗呀?jīng)被spring管理,
  // 變成serviceImpl真實(shí)實(shí)例的代理類,而代理類中并沒有私有方法,所以需要先獲取它的真實(shí)實(shí)例
  method.invoke(target);
  }
}

獲取spring 代理對(duì)象的真實(shí)實(shí)例的工具類,適用于兩種動(dòng)態(tài)代理情況:jdk和cglib

public class ReflectionUtil {
  /**
   * 獲取spring 代理對(duì)象的真實(shí)實(shí)例
   * @param proxy 代理對(duì)象
   * @return
   * @throws Exception
   */
  public static Object getTarget(Object proxy) throws Exception {
    if(!AopUtils.isAopProxy(proxy)) {
      return proxy;//不是代理對(duì)象
    }

    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;
  }
}

另外還有一個(gè)更好的開源工具 PowerMock https://github.com/powermock/powermock,感興趣的同學(xué)可以研究一下

以上就是Spring實(shí)現(xiàn)類私有方法測試通用方案的詳細(xì)內(nèi)容,更多關(guān)于Spring類私有方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Java多態(tài)對(duì)象的類型轉(zhuǎn)換與動(dòng)態(tài)綁定

    詳解Java多態(tài)對(duì)象的類型轉(zhuǎn)換與動(dòng)態(tài)綁定

    這篇文章主要介紹了詳解Java多態(tài)對(duì)象的類型轉(zhuǎn)換與動(dòng)態(tài)綁定,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • 基于SpringBoot和MongoDB實(shí)現(xiàn)實(shí)時(shí)分析和日志處理功能

    基于SpringBoot和MongoDB實(shí)現(xiàn)實(shí)時(shí)分析和日志處理功能

    實(shí)時(shí)分析和日志處理在現(xiàn)代應(yīng)用程序開發(fā)中扮演著重要的角色,MongoDB是一個(gè)非常流行的NoSQL數(shù)據(jù)庫,其高性能和靈活性使其成為實(shí)時(shí)分析和日志處理的理想選擇,本文將介紹如何使用?Spring?Boot?和?MongoDB?實(shí)現(xiàn)實(shí)時(shí)分析和日志處理的功能
    2023-06-06
  • java技巧:反射判斷field類型的操作

    java技巧:反射判斷field類型的操作

    這篇文章主要介紹了java技巧:反射判斷field類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • SpringBoot整合log4j日志與HashMap的底層原理解析

    SpringBoot整合log4j日志與HashMap的底層原理解析

    這篇文章主要介紹了SpringBoot整合log4j日志與HashMap的底層原理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • SpringBoot集成validation校驗(yàn)參數(shù)遇到的坑

    SpringBoot集成validation校驗(yàn)參數(shù)遇到的坑

    這篇文章主要介紹了SpringBoot集成validation校驗(yàn)參數(shù)遇到的坑,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 如何在IDEA啟動(dòng)多個(gè)Spring Boot工程實(shí)例(圖文)

    如何在IDEA啟動(dòng)多個(gè)Spring Boot工程實(shí)例(圖文)

    這篇文章主要介紹了如何在IDEA啟動(dòng)多個(gè)Spring Boot工程實(shí)例(圖文),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對(duì)比

    后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對(duì)比

    這篇文章主要給大家介紹了關(guān)于后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對(duì)比的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-06-06
  • 關(guān)于Spring的@Autowired依賴注入常見錯(cuò)誤的總結(jié)

    關(guān)于Spring的@Autowired依賴注入常見錯(cuò)誤的總結(jié)

    有時(shí)我們會(huì)使用@Autowired自動(dòng)注入,同時(shí)也存在注入到集合、數(shù)組等復(fù)雜類型的場景。這都是方便寫 bug 的場景,本篇文章帶你了解Spring @Autowired依賴注入的坑
    2021-09-09
  • Java如何基于DOM解析xml文件

    Java如何基于DOM解析xml文件

    這篇文章主要介紹了Java如何基于DOM解析xml文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Springboot使用maven打包指定mainClass問題

    Springboot使用maven打包指定mainClass問題

    這篇文章主要介紹了Springboot使用maven打包指定mainClass問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評(píng)論

桐柏县| 措美县| 柳州市| 大名县| 茌平县| 和田市| 津南区| 香格里拉县| 香港 | 慈利县| 乐平市| 桃园市| 新疆| 漳平市| 健康| 财经| 泽库县| 左云县| 南充市| 伊宁县| 玛曲县| 台前县| 遵义县| 阳江市| 高陵县| 哈尔滨市| 特克斯县| 遂川县| 招远市| 泸水县| 宁津县| 鲁甸县| 龙州县| 阿巴嘎旗| 炉霍县| 巴青县| 光泽县| 资源县| 奉新县| 赞皇县| 道真|