此次搭建的WebApi平台思路如下;
1.对拦截到的Action请求统一构建context对象,用于组装请求所携带的参数。
2.构建统一的响应对象reponse
3.调用checkSecurity检查模块检查请求的安全性和可用性,如有错误,则退出处理并写入response错误信息
4.对通过check的context对象进行反射调用,并根据相应结果组装response对象
5.response对象通过Marshaller模块返回指定的响应报文
注:其中的3和5过程都需要提供可继承的接口方便用户自定义策略
在编码过程中遇到的较大难题主要是反射调用这一块,也是我觉得有价值的地方,特记录如下。
先复习一下spring.beans包的主要内容
总结如下:
1.Loading XML bean definitions
2.实例化BeanFactoryPostProcessor,调用工厂方法
3.实例化BeanPostProcessor构造器
4.实例化InstantiationAwareBeanPostProcessorAdapter
5.调用postProcessBeforeInstantiation方法,构造目标bean实例。再调用postProcessPropertyValues方法,为目标bean注入属性
6.BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!
7.【init-method】调用的init-method属性指定的初始化方法
8.BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!
9.InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法
10.容器初始化成功
其中可以看出,beanPostProcessor接口对spring接管的每个实体类都会调用postProcessBeforeInitialization,postProcessAfterInitialization这两个方法。
为了实现让spring反射知道调用的是哪个方法,我需要在缓存中记住每个带有
@BopServiceMethod(method = "sayHelloTest", args = {"webctx","name","data"}, message = "哼哈嘿")
注解的service方法,并读取其中指定的method,args,message信息进行缓存。
为此,我们只要实现BeanPostProcessor接口,在postProcessAfterInitialization中判断①该方法所在的类是否是service ②该类中的方法是否带有@BopServoceMethod注解就行了
AnnotationUtils.findAnnotation(proClass, org.springframework.stereotype.Service.class);
使用findAnnotation方法判断一个类上是否有@service注解。proClass表示目标类的class
Method[] methods = ReflectionUtils.getAllDeclaredMethods(proClass);
若是service类,则使用getAllDeclaredMethods方法获得该类的所有方法组成数组
BopServiceMethod bopService = AnnotationUtils.findAnnotation(method, BopServiceMethod.class);
遍历所有方法,获得注解类,当注解类bopService不为空时,即说明该方法上标注了该注解。此时,再用过method对象和bopService对象获取需要的信息就易如反掌了。
在Spring中,凡是标注了@Transactional(事务)注解的方法都会使用spring事务进行处理,spring框架使用过jdkDynamicProxy动态代理进行处理的。或者是自己篇日志Aop动态代理的的方法,在通过BeanPostProcessor接口时获取的bean对象统统都是代理对象,这时直接使用bean.class作为参数拿到的Service类一定是Null
于是在此做如下处理:
Class proClass;
if (AopUtils.isJdkDynamicProxy(bean)) {
proClass = AopUtils.getTargetClass(bean);
} else {
proClass = bean.getClass();
}
该段代码的含义是,通过AopUtils工具判断是何种动态代理方式(这里我只判断是否是jdk动态代理),如果是,则使用getTargetClass获取其代理目标类的class。若不是,则直接获得bean.class
除去使用这种方式以外,还可以通过以下这种方式获得代理类的class
DirectFieldAccessor ads = new DirectFieldAccessor(Proxy.getInvocationHandler(bean));
AdvisedSupport advised = (AdvisedSupport) ads.getPropertyValue("advised");
clazz = advised.getTargetClass();
获取invocationHandler对象,再构建DirectFieldAccessor实例,通过属性方法获得advised的实例,再从advised对象中获取目标类的class;
接下来讲一讲具体反射的使用思路
首先是缓存中拿到我们需要的各种属性
类名、方法名和参数类型数组
Method mh = ReflectionUtils.findMethod(SpringContextUtil.getBean(className).getClass(), methodName, argsType);
使用ReflectionUtils.findMethod获取反射的方法对象
然后,就是重点【参数转换】
Object[] param = new Object[mh.getParameterCount()];
Class[] mhTypes = mh.getParameterTypes();
String[] argsName = (String[]) m.get("argsName");
① 首先,要通过mh.getParameterCount()取得参数的数量,并以此新建参数数组object[]
②通过mh.getParameterTypes()方法获得所有的参数类型数组,class<?>[]
③从缓存中取出我们之前缓存的参数名数组String[]
④提供参数转换策略。遍历对象进行替换!
由于我使用的context对象将所有参数都封装成了json字符串,所以提供的转换策略里需要JsonUtil工具类去转换成各种需要的参数对象。
最后,反射调用:
Object result = ReflectionUtils.invokeMethod(mh, SpringContextUtil.getBean(className), param);
完事。