此次搭建的WebApi平臺思路如下踊淳;
1.對攔截到的Action請求統(tǒng)一構(gòu)建context對象,用于組裝請求所攜帶的參數(shù)。
2.構(gòu)建統(tǒng)一的響應(yīng)對象reponse
3.調(diào)用checkSecurity檢查模塊檢查請求的安全性和可用性诚纸,如有錯誤,則退出處理并寫入response錯誤信息
4.對通過check的context對象進行反射調(diào)用陈惰,并根據(jù)相應(yīng)結(jié)果組裝response對象
5.response對象通過Marshaller模塊返回指定的響應(yīng)報文
注:其中的3和5過程都需要提供可繼承的接口方便用戶自定義策略
在編碼過程中遇到的較大難題主要是反射調(diào)用這一塊畦徘,也是我覺得有價值的地方,特記錄如下抬闯。
先復(fù)習(xí)一下spring.beans包的主要內(nèi)容
總結(jié)如下:
1.Loading XML bean definitions
2.實例化BeanFactoryPostProcessor井辆,調(diào)用工廠方法
3.實例化BeanPostProcessor構(gòu)造器
4.實例化InstantiationAwareBeanPostProcessorAdapter
5.調(diào)用postProcessBeforeInstantiation方法,構(gòu)造目標bean實例溶握。再調(diào)用postProcessPropertyValues方法杯缺,為目標bean注入屬性
6.BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!
7.【init-method】調(diào)用的init-method屬性指定的初始化方法
8.BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改睡榆!
9.InstantiationAwareBeanPostProcessor調(diào)用postProcessAfterInitialization方法
10.容器初始化成功
其中可以看出萍肆,beanPostProcessor接口對spring接管的每個實體類都會調(diào)用postProcessBeforeInitialization,postProcessAfterInitialization這兩個方法胀屿。
為了實現(xiàn)讓spring反射知道調(diào)用的是哪個方法塘揣,我需要在緩存中記住每個帶有
@BopServiceMethod(method = "sayHelloTest", args = {"webctx","name","data"}, message = "哼哈嘿")
注解的service方法,并讀取其中指定的method,args,message信息進行緩存宿崭。
為此亲铡,我們只要實現(xiàn)BeanPostProcessor接口,在postProcessAfterInitialization中判斷①該方法所在的類是否是service ②該類中的方法是否帶有@BopServoceMethod注解就行了
AnnotationUtils.findAnnotation(proClass, org.springframework.stereotype.Service.class);
使用findAnnotation方法判斷一個類上是否有@service注解葡兑。proClass表示目標類的class
Method[] methods = ReflectionUtils.getAllDeclaredMethods(proClass);
若是service類奴愉,則使用getAllDeclaredMethods方法獲得該類的所有方法組成數(shù)組
BopServiceMethod bopService = AnnotationUtils.findAnnotation(method, BopServiceMethod.class);
遍歷所有方法,獲得注解類铁孵,當注解類bopService不為空時,即說明該方法上標注了該注解房资。此時蜕劝,再用過method對象和bopService對象獲取需要的信息就易如反掌了。
在Spring中,凡是標注了@Transactional(事務(wù))注解的方法都會使用spring事務(wù)進行處理岖沛,spring框架使用過jdkDynamicProxy動態(tài)代理進行處理的暑始。或者是自己篇日志Aop動態(tài)代理的的方法婴削,在通過BeanPostProcessor接口時獲取的bean對象統(tǒng)統(tǒng)都是代理對象廊镜,這時直接使用bean.class作為參數(shù)拿到的Service類一定是Null
于是在此做如下處理:
Class proClass;
if (AopUtils.isJdkDynamicProxy(bean)) {
proClass = AopUtils.getTargetClass(bean);
} else {
proClass = bean.getClass();
}
該段代碼的含義是,通過AopUtils工具判斷是何種動態(tài)代理方式(這里我只判斷是否是jdk動態(tài)代理)唉俗,如果是嗤朴,則使用getTargetClass獲取其代理目標類的class。若不是虫溜,則直接獲得bean.class
除去使用這種方式以外雹姊,還可以通過以下這種方式獲得代理類的class
DirectFieldAccessor ads = new DirectFieldAccessor(Proxy.getInvocationHandler(bean));
AdvisedSupport advised = (AdvisedSupport) ads.getPropertyValue("advised");
clazz = advised.getTargetClass();
獲取invocationHandler對象,再構(gòu)建DirectFieldAccessor實例,通過屬性方法獲得advised的實例衡楞,再從advised對象中獲取目標類的class吱雏;
接下來講一講具體反射的使用思路
首先是緩存中拿到我們需要的各種屬性
類名、方法名和參數(shù)類型數(shù)組
Method mh = ReflectionUtils.findMethod(SpringContextUtil.getBean(className).getClass(), methodName, argsType);
使用ReflectionUtils.findMethod獲取反射的方法對象
然后瘾境,就是重點【參數(shù)轉(zhuǎn)換】
Object[] param = new Object[mh.getParameterCount()];
Class[] mhTypes = mh.getParameterTypes();
String[] argsName = (String[]) m.get("argsName");
① 首先歧杏,要通過mh.getParameterCount()取得參數(shù)的數(shù)量,并以此新建參數(shù)數(shù)組object[]
②通過mh.getParameterTypes()方法獲得所有的參數(shù)類型數(shù)組迷守,class<?>[]
③從緩存中取出我們之前緩存的參數(shù)名數(shù)組String[]
④提供參數(shù)轉(zhuǎn)換策略犬绒。遍歷對象進行替換!
由于我使用的context對象將所有參數(shù)都封裝成了json字符串盒犹,所以提供的轉(zhuǎn)換策略里需要JsonUtil工具類去轉(zhuǎn)換成各種需要的參數(shù)對象懂更。
最后,反射調(diào)用:
Object result = ReflectionUtils.invokeMethod(mh, SpringContextUtil.getBean(className), param);
完事急膀。