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