獲得spring的指定目標物件,執行指定方法(JDK動態代理,cglib動態代理,Dubbo-Javassist代理)
阿新 • • 發佈:2019-01-31
在spring的配置檔案中配置的bean,spring會進行依賴注入和初始化物件。
根據配置不同,spring會選擇不同的代理方式。對於JDK動態代理、cglib動態代理,spring會找到目標介面的實現類並初始化一個物件,對於Dubbo的consumer,預設會使用Dubbo自己寫的動態代理實現方式(除非明確配置為使用JDK等代理),使用Javassist生成目標介面的代理類,並初始化該類的物件,這種是一種工廠方式。
在spring的上下文中,儲存的實際上是bean的代理,而不是直接儲存目標物件。
以下程式碼可以指定一個在spring中注入過的類名,指定方法名,然後從spring上下文中得到這個對應的代理,並執行對應的方法。
/** * @param service 在spring配置檔案中註冊的id * @param method 方法名 * @param data 方法傳入引數 * @throws Exception */ public void testDubboService(String service,String method,String data) throws Exception{ System.out.println("service---"+service); System.out.println("method---"+method); System.out.println("data---"+data); Object obj=ApplicationContextUtil.getBean(service); if(AopUtils.isJdkDynamicProxy(obj)) { //jdk代理 Field h = obj.getClass().getDeclaredField("CGLIB$CALLBACK_0"); h.setAccessible(true); Object dynamicAdvisedInterceptor = h.get(obj); Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object newObj = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); Map<String, BeanDefinition> map=ApplicationContextUtil.getBeanDefinitions(); BeanDefinition definition=map.get(service); String className=definition.getBeanClassName(); Class beanClass=Class.forName(className); Method oneMethod=beanClass.getMethod(method, String.class); oneMethod.invoke(newObj, data);//執行 } else if(AopUtils.isCglibProxy(obj)){ //cglib代理 Field h = obj.getClass().getSuperclass().getDeclaredField("h"); h.setAccessible(true); AopProxy aopProxy = (AopProxy) h.get(obj); Field advised = aopProxy.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object newObj = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); Map<String, BeanDefinition> map=ApplicationContextUtil.getBeanDefinitions(); BeanDefinition definition=map.get(service); String className=definition.getBeanClassName(); Class beanClass=Class.forName(className); Method oneMethod=beanClass.getMethod(method, String.class); oneMethod.invoke(newObj, data);//執行 } else if(obj.getClass().getName().startsWith("com.alibaba.dubbo.common.bytecode.proxy")){//dubbo代理 Field handlerField=obj.getClass().getDeclaredField("handler"); handlerField.setAccessible(true); com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler invokerInvocationHandler = (com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler)handlerField.get(obj); Field invokerField=invokerInvocationHandler.getClass().getDeclaredField("invoker"); invokerField.setAccessible(true); com.alibaba.dubbo.rpc.Invoker invoker=(com.alibaba.dubbo.rpc.Invoker)invokerField.get(invokerInvocationHandler); Field failoverClusterInvokerField=invoker.getClass().getDeclaredField("invoker"); failoverClusterInvokerField.setAccessible(true); FailoverClusterInvoker failoverClusterInvoker =(FailoverClusterInvoker)failoverClusterInvokerField.get(invoker); Class failoverClusterInvokerInterfaceClass=failoverClusterInvoker.getInterface(); Method oneMethod=failoverClusterInvokerInterfaceClass.getMethod(method, String.class); Object result=oneMethod.invoke(obj, data);//執行 Class resultClass=result.getClass(); } }
bean是從上下文中獲取的,Method是從spring記錄的BeanDefinitions來獲取的,按照bean不同的代理方式選擇不同的獲取方式。
上面程式碼只寫了引數是一個String的情況,實際上傳入引數和返回值可能會更復雜。
更新的分割線====================================================================================================
更新了一下,在dubbo代理的程式碼裡,原來支援只有一個引數而且必須是String型別的方法,現在加上了支援有包裝類引數的方法,引數是多個也可以
多個引數的話,各個引數之間用換行符隔開,包裝類的引數使用JSON格式,使用了com.alibaba.fastjson.JSON
JDK代理和CGLib代理的使用方式其實是一樣的
程式碼如下:
/**
* @param service 在spring配置檔案中註冊的id
* @param method 方法名
* @param data 方法傳入引數
* @throws Exception
*/
@RequestMapping("testDubboService")
public void testDubboService(String service,String method,String data) throws Exception{
System.out.println("service---"+service);
System.out.println("method---"+method);
System.out.println("data---"+data);
Object obj=ApplicationContextUtil.getBean(service);
if(AopUtils.isJdkDynamicProxy(obj)) { //jdk代理
Field h = obj.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(obj);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object newObj = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
Map<String, BeanDefinition> map=ApplicationContextUtil.getBeanDefinitions();
BeanDefinition definition=map.get(service);
String className=definition.getBeanClassName();
Class beanClass=Class.forName(className);
Method oneMethod=beanClass.getMethod(method, String.class);
oneMethod.invoke(newObj, data);//執行
} else if(AopUtils.isCglibProxy(obj)){ //cglib代理
Field h = obj.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(obj);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object newObj = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
Map<String, BeanDefinition> map=ApplicationContextUtil.getBeanDefinitions();
BeanDefinition definition=map.get(service);
String className=definition.getBeanClassName();
Class beanClass=Class.forName(className);
Method oneMethod=beanClass.getMethod(method, String.class);
oneMethod.invoke(newObj, data);//執行
} else if(obj.getClass().getName().startsWith("com.alibaba.dubbo.common.bytecode.proxy")){//dubbo代理
Field handlerField=obj.getClass().getDeclaredField("handler");
handlerField.setAccessible(true);
com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler invokerInvocationHandler = (com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler)handlerField.get(obj);
Field invokerField=invokerInvocationHandler.getClass().getDeclaredField("invoker");
invokerField.setAccessible(true);
com.alibaba.dubbo.rpc.Invoker invoker=(com.alibaba.dubbo.rpc.Invoker)invokerField.get(invokerInvocationHandler);
Field failoverClusterInvokerField=invoker.getClass().getDeclaredField("invoker");
failoverClusterInvokerField.setAccessible(true);
FailoverClusterInvoker failoverClusterInvoker =(FailoverClusterInvoker)failoverClusterInvokerField.get(invoker);
Class failoverClusterInvokerInterfaceClass=failoverClusterInvoker.getInterface();
//Method oneMethod=failoverClusterInvokerInterfaceClass.getMethod(method, String.class);
//Object result=oneMethod.invoke(obj, data);//執行
Object[] resultArr=disposeParameter(failoverClusterInvokerInterfaceClass,method,data);
Method oneMethod=(Method)resultArr[0];
Object[] parameterArr=(Object[])resultArr[1];
Object result=oneMethod.invoke(obj, parameterArr);//執行
if(result!=null){
Class resultClass=result.getClass();
System.out.println(resultClass.getName());
}
}
}
/**
*
* @param targetClass 目標類
* @param method 方法名
* @param data 傳入引數
* @return 長度為2的陣列,第一個元素是要執行的Method例項,第二個元素是要方法執行的引數陣列
*/
public Object[] disposeParameter(Class targetClass, String method,String data){
Object[] resultArr=new Object[2];
String[] dataArr=data.split("\n");
Method[] methodArr=targetClass.getMethods();
for(int i=0;i<methodArr.length;i++){
Method m=methodArr[i];
if(m.getName().equals(method)&&m.getParameterTypes().length==dataArr.length){//命中方法,只校驗了名字和引數數量
Class[] classArr=m.getParameterTypes();//目標方法的引數列表
resultArr[0]=m;
Object[] parameterArr=new Object[classArr.length];//需要賦值的引數列表
for(int j=0;j<classArr.length;j++){//迴圈目標方法的引數列表,賦值用
String oneData=dataArr[j]; //輸入的第j行資訊
Class parameterClass=classArr[j];//目標方法的第j個引數
try {
if(parameterClass.equals(String.class)){//引數是String型別
parameterArr[j]=oneData;
}else{//引數是包裝型別,還沒有考慮int,Integer,陣列等型別
Object newInstance=parameterClass.newInstance();//新建目標方法第j個引數的例項
JSONObject oneJsonObject=(JSONObject)JSON.parse(oneData);
Iterator<Entry<String, Object>> iter=oneJsonObject.entrySet().iterator();
while(iter.hasNext()){//把輸入第j行的資訊存給第j個引數
Entry<String, Object> entry=iter.next();
String key=entry.getKey();
Object value=entry.getValue();
try {
Field classField=parameterClass.getDeclaredField(key);
classField.setAccessible(true);
classField.set(newInstance, value);
} catch (NoSuchFieldException e) {//找不到這個屬性,再從父類中找這個屬性,沒有繼續往上迭代,只找了上面一級
System.out.println("沒有這個屬性"+key);
Class superClass=parameterClass.getSuperclass();
try {
Field classField=superClass.getDeclaredField(key);
classField.setAccessible(true);
classField.set(newInstance, value);
} catch (NoSuchFieldException e1) {
System.out.println("父類也沒有這個屬性"+key);
} catch (SecurityException e1) {
e1.printStackTrace();
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
parameterArr[j]=newInstance;
}
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
resultArr[1]=parameterArr;
break;
}
}
return resultArr;
}