请求路由到业务方法设计(2)扯淡:
看到首页又来一个C#和java争论,我也是蛮醉的,玩C#好的同学多得是,一个技术能全通所有技术是不可能,术业本来就是有专攻,即使能借鉴那也是比较有高度的概念借鉴而已。
而语言这种东西要分高低,我觉得是件很愚蠢的事。如果你真的想讨论,建议你写个万字级别的文章来论述,我想这样的话等你写完你也一定成长了不少。总之一个告诫,别做无益处无乐趣却太浪费时间的事情。
我自己玩java,主要也是喜欢 开源 这两个字。看得到更多别人想法的实现细节对于我这种个性的开发是一件蛮兴奋的事情。而我想喜欢C#的同学自有自己的乐趣所在。
主题:
上一篇文章地址:请求路由到业务方法设计(1)
希望实现的功能在上一篇文章描述过了,参考这个ROP框架,是因为它也要解决这个问题,而且是java实现,可以参考。
我这边是自己参考后实现了一个,实际ROP框架实现参考源码,不过git上已经很久没有维护了。
另外:个人觉得ROP是java web开发可以详细阅读的小框架,花不了太多时间,了解结构,用到的模式,都是很好的学习素材,因为不是很庞大,所以可以有一个全局观。读大的框架,经验不够的时候,容易迷失,而且东西学的反而少。
思路是这样:核心我们要在应用启动后,产生出一个路由Map,这个map维护请求中的某个值对应执行哪一个类的哪个方法,也就是对应着那一段业务逻辑。如此就可以把输入组装好,放进统一如有接口中,然后就会执行相应逻辑。
这里使用注解的方式,来标识出那些业务逻辑,也就是给实现的业务逻辑打上标签,用于启动时组装出路由Map。
第一个注解标识类:
@Target({ElementType. TYPE})
@Retention(RetentionPolicy.RUNTIME )
@Documented
@Servicepublic@interfaceOpenServiceBean {
}
第二个注解标识方法,那么这个路由map就是指定到方法的,这里可以注意到这个标签就是method字段,那么前端传过来的值中有这个字段:
@Target({ElementType. TYPE})
@Retention(RetentionPolicy.RUNTIME )
@Documentedpublic@interfaceOpenServiceMethod {/*** 服务的方法名,即由method参数指定的服务方法名
**/String method()default"";/*** 服务的中文名称
**/String title()default"";
}
接下来就是启动的时候解析,那些写着这些注解的类和方法了,然后缓存到map中。
publicinterfaceOpenServiceRouter {//统一入口接口publicJSON doService(JSONObject json);
}
下面是实现类,这个类是启动类:
//实现,利用sPRing,实例化bean后,执行afterPropertiesSet()方法。//而所有使用了注解的bean,都会被spring初始化好,也就是说,我们建立路由Map的时候,spring的容器已经准备好了,//那么我们就可以去容器里拿这些bean来用了,所以此时我们必须先拿到applicationContext对象。所以使用了ApplicationContextAware。@ServicepublicclassOpenServiceRouterImplimplementsInitializingBean, OpenServiceRouter, ApplicationContextAware{privateMap<String, ServiceMethodHandler>routerMap;privateOpenServiceContext context;privateAbstractBaseServiceAdapter serviceAdapter;privateApplicationContext applicationContext;
@OverridepublicvoidafterPropertiesSet()throwsException {//执行器this.serviceAdapter =newAbstractBaseServiceAdapter();//产生路由Map,具体看OpenServiceContextthis.context =newOpenServiceContext(applicationContext);this.routerMap =this.context.getHandlerMap();
}
@OverridepublicJSON doService(JSONObject json) {//根据method拿出指定的handlerServiceMethodHandler handler = routerMap.get(json.getString("method"));if(handler ==null){returnnewJSONObject();//TODO}//拿到执行handler,执行returnserviceAdapter.execute(json, handler);
}
@OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throwsBeansException {this.applicationContext =applicationContext;
}
}
ServiceMethodHandler 用来存放处理器,也就是那些业务逻辑类和方法,那个Map的value也就是这个:
publicclassServiceMethodHandler {publicClass<?extendsOpenRequest>getRequestType() {returnrequestType;
}publicvoidsetRequestType(Class<?extendsOpenRequest>requestType) {this. requestType =requestType;
}privateObject handler;privateMethod handlerMethod;//处理方法的请求对象类privateClass<?extendsOpenRequest> requestType = OpenRequest.class;publicObject getHandler() {returnhandler;
}publicvoidsetHandler(Object handler) {this. handler =handler;
}publicMethod getHandlerMethod() {returnhandlerMethod;
}publicvoidsetHandlerMethod(Method handlerMethod) {this. handlerMethod =handlerMethod;
}
}
OpenServiceContext用来实际启动时将那些注解的类全部解析成路由Map,也算核心代码,上面提到的ROP框架也是一样的做法,值得借鉴,以后自己想写个小框架可以用用。
publicclassOpenServiceContext{privateMap<String, ServiceMethodHandler> handlerMap =newHashMap<String, ServiceMethodHandler>();privateSet<String> methodNameSet =newHashSet<String>();privateApplicationContext applicationContext;publicOpenServiceContext(){
initContext(this. applicationContext);
}publicOpenServiceContext(finalApplicationContext context){
initContext(context);
}publicvoidaddServiceMethodHandler(String methodName, ServiceMethodHandler serviceMethodHandler){
methodNameSet.add(methodName);
handlerMap.put(methodName, serviceMethodHandler);
}publicbooleanisValidMethod(String methodName){returnmethodNameSet.contains(methodName);
}publicMap<String, ServiceMethodHandler>getHandlerMap(){returnhandlerMap;
}//执行方法前需要将ApplicationContext准备好publicvoidinitContext(finalApplicationContext context){
String[] beanNames= context.getBeanNamesForType(Object.class);if(beanNames ==null){return;
}for(finalString beanName : beanNames) {
Class<?> handlerType =context.getType(beanName);//本方法是最后一个参数根据注解直接过滤出来的method,放入MapReflectionUtils. doWithMethods(handlerType,newReflectionUtils.MethodCallback() {publicvoiddoWith(Method method)throwsIllegalArgumentException, IllegalaccessException {
ReflectionUtils.makeAccessible(method);
OpenServiceMethod serviceMethod= method.getAnnotation(OpenServiceMethod .class);
ServiceMethodHandler serviceMethodHandler=newServiceMethodHandler();//1.set handlerserviceMethodHandler.setHandler(context.getBean(beanName));//handlerserviceMethodHandler.setHandlerMethod(method);//handler'methodif(!ClassUtils. isAssignable(OpenResponse.class, method.getReturnType())){thrownewOpenServiceException(method.getDeclaringClass().getName() + "." +method.getName()+ "的返回参数必须是" + OpenResponse.class.getName());
}if(method.getParameterTypes().length > 1) {//handler method's parameterthrownewOpenServiceException(method.getDeclaringClass().getName() + "." +method.getName()+ "的入参只能是" + OpenRequest.class.getName() + "或无入参。");
}elseif(method.getParameterTypes().length == 1) {
Class<?> paramType = method.getParameterTypes()[0];if(!ClassUtils.isAssignable(OpenRequest.class, paramType)) {thrownewOpenServiceException(method.getDeclaringClass().getName() + "." +method.getName()+ "的入参必须是" + OpenRequest.class.getName());
}
serviceMethodHandler.setRequestType((Class<?extendsOpenRequest>)paramType);
}else{thrownewOpenServiceException(method.getDeclaringClass().getName() + "." + method.getName() + "无入参");
}
addServiceMethodHandler(serviceMethod.method(), serviceMethodHandler);
}
},newReflectionUtils.MethodFilter() {
@Overridepublicbooleanmatches(Method method) {returnAnnotationUtils.findAnnotation(method, OpenServiceMethod.class) !=null;
}
}
);
}
}
}
接下来就是执行器来执行指定逻辑代码了,因为Map中放的是Method,执行需要进行反射:
publicclassAbstractBaseServiceAdapter {/*** json ==> OpenRequest
*@paramjson
*@paramhandler
*@return*/publicfinalOpenRequest decode(JSON json, ServiceMethodHandler handler){
Class<?extendsOpenRequest> requestClass =handler.getRequestType();
ObjectMapper mapper=newObjectMapper();
OpenRequest request=null;try{
request=mapper.readValue(json.toJSONString(), requestClass);
}catch(Exception e) {thrownewOpenServiceException( "open decode had a exp json==>"+json , e);
}returnrequest;
}/*** OpenResponse ==> json
*@paramresponse
*@return*/publicfinalJSON encode(OpenResponse response){
ObjectMapper mapper=newObjectMapper();//Convert object to JSON stringJSON json =null;try{
String j=mapper.writeValueAsString(response);
json=JSON. parSEObject(j);
}catch(Exception e) {thrownewOpenServiceException( "open encode had a exp response==>"+response.getClass() , e);
}returnjson;
}publicfinalJSON execute(JSON json, ServiceMethodHandler handler) {
OpenRequest request=this.decode(json, handler);
handler.getHandler();
handler.getHandlerMethod();
OpenResponse response=null;try{//执行器执行对应方法response =(OpenResponse) handler.getHandlerMethod().invoke(
handler.getHandler(), request);
}catch(Exception e) {thrownewOpenServiceException( "open invoke had a exp json"+json , e);
}
JSON retrunJson=this.encode(response);returnretrunJson;
}
}
至此结构代码已经理清,具体还有method的输入输出,需要统一接口实现,以保证执行器代码的统一。
总结:
1,这个方案的核心就是利用注解来组装好这个路由Map,以及用反射的方式执行对应的方法。
2,另外Spring的确十分强大,很多封装代码可以被使用。学习java的应该深入进去挖宝。
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。