您好,登錄后才能下訂單哦!
本篇內容介紹了“Spring實現類私有方法是什么意思”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
現實的業務場景中,可能需要對Spring的實現類的私有方法進行測試。
比如XXXService里有 兩個函數a、函數b。
而實現類XXXServiceImpl中實現了函數a、函數b,還包含私有方法函數c和函數d。
要寫一個XXXTestController來調用XXXServiceImpl的函數c。
1、如果注入接口,則無法調用實現類的私有類。
2、如果注入實現類,則需要將實現類里的私有方法改為公有的,而且需要設置@EnableAspectJAutoProxy(proxyTargetClass = true)使用CGLIB代理方式
如果單純為了測試而接口中定義實現類的私有方法或者為了測試而將私有方法臨時改為公有方法,顯然不太合適。
可以通過CGLIB注入實現類的子類,如果是Gradle項目也可以使用Aspect插件,將切面代碼在編譯器織入實現類中注入的類型則為實現類,然后通過反射設置為可訪問來調用私有方法。
反射調用代碼:
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。
如果需要返回值,可以獲取該調用方法的返回值。
測試類
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實現類私有方法是什么意思”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。