若對外暴露Dubbo接口,咱們能夠經過invoke直接調用。java
若是未對外暴露Dubbo接口,內部的方法如倉儲層、應用層(項目採用DDD分層架構)的某個方法,有辦法直接調用嗎?spring
ps:爲了保護公司的代碼,參考代碼中的一些包名都是我臨時改的,若有不便,見諒。api
咱們組有一個通用工具包,在這裏面定義一個接口,接口類名稱叫DebugService,方法名稱叫invoke,以下:緩存
package com.xuming.pay.commons.core.test; /** * @author xuming.chen Date: 2021/7/26 Time: 3:52 下午 */ public interface DebugService { /** * 在線dubbo調試方法,主要適用於不方便對外提供dubbo api的方法,用來刷數據或者測試,修數據等場景使用 * * @param beanName spring bean name * @param method bean class下的方法名 * @param params 調用的參數列表 * @return 方法執行結果 */ public Object invoke(String beanName, String method, Object... params); }
一共三個參數,註釋寫得很清晰了,params是Java可變參數。服務器
接着來看invoke方法的內部實現:架構
package com.xuming.pay.commons.impl; import com.fasterxml.jackson.core.type.TypeReference; import com.xuming.pay.commons.core.base.JsonUtils; import com.xuming.pay.commons.core.test.DebugService; import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.util.ReflectionUtils; import javax.annotation.Resource; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * 在線調試利器。須要在基礎設施裏面對外配置xml暴露出該service * * @author xuming.chen Date: 2021/7/23 Time: 2:52 下午 */ public class DebugServiceImpl implements DebugService, ApplicationContextAware { @Resource private ApplicationContext applicationContext; public Object invoke(String beanName, String method, Object... params) { Object objBean = applicationContext.getBean(beanName); Class cls = AopUtils.getTargetClass(objBean); List<Method> m = Arrays.stream(cls.getDeclaredMethods()) .filter(me -> me.getName().equals(method) && me.getParameterTypes().length == params.length) .collect(Collectors.toList()); Method targetMethod = m.get(0); // 取消Java語言訪問檢查,容許經過反射調用私有方法 targetMethod.setAccessible(true); Type[] types = targetMethod.getGenericParameterTypes(); Object[] ps = new Object[params.length]; for (int i = 0; i < types.length; i++) { ps[i] = parseObject(String.valueOf(params[i]), types[i]); } return ReflectionUtils.invokeMethod(targetMethod, objBean, ps); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } private Object parseObject(String value, Type type) { if (value == null) { return null; } if (Long.class.getName().equals(type.getTypeName())) { return Long.parseLong(value); } if (Integer.class.getName().equals(type.getTypeName())) { return Integer.parseInt(value); } if (Short.class.getName().equals(type.getTypeName())) { return Short.parseShort(value); } if (Byte.class.getName().equals(type.getTypeName())) { return Byte.parseByte(value); } if (Boolean.class.getName().equals(type.getTypeName())) { return Boolean.parseBoolean(value); } if (Character.class.getName().equals(type.getTypeName())) { return value.charAt(0); } if (Float.class.getName().equals(type.getTypeName())) { return Float.parseFloat(value); } if (Double.class.getName().equals(type.getTypeName())) { return Double.parseDouble(value); } if (String.class.getName().equals(type.getTypeName())) { return value; } return JsonUtils.decode(value, new TypeReference<Object>() { @Override public Type getType() { return type; } }); } }
具體使用時,在項目中引入該通用工具包的maven依賴,而後聲明該接口對外暴露,以下圖:app
接下來就能夠愉快地玩耍啦~~maven
咱們在服務器上invoke調用看看:ide
從上圖能夠看到,返回值是true,符合預期。工具
好比咱們用Redis緩存,倉儲層有個方法用於清除緩存,經過DebugService這個小後門,咱們就能夠在須要時將緩存清除。
調試/線上排查問題時,可用。
不過若要在生產環境使用,仍是要慎之又慎。線上刷數據/修數據都是有嚴格規範的,要遵照,要提早報備。最好不要用這種方式來刷數據/修數據!