利用java反射机制,可以让我们在知道类名的情况下获取该类的各类信息,并且能进行实例化,完成对对象的反射调用。本例是基于一个springboot的整合了dubbo的分布式项目,我们在controller做一些改动,跳过controller直接对service进行反射调用
1.创建一个service.json保存服务列表及调用方法
[ { "serviceId":200, "serviceName":"top.cwk.dubbo.api.customer.ICustomerService", "method":"getCustomerById", "version":"0.1", "group":"test" } ]
2.定义一个servletContextListener监听器,服务启动的时候加载service.json并保存为key-map格式,存放到servletContext
/** * @ClassName ServiceJsonBeanListener * @Description servlet监听器,初始化将service.json读取到ServletContext * @Author 维康 * @Date 2018/7/26 16:27 * @Version 1.0 **/ @Configuration public class ServiceJsonBeanListener implements ServletContextListener { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private static Map<Long,ServiceJsonBean> SERVICE_MAP = new HashMap<>(); @Override public void contextInitialized(ServletContextEvent servletContextEvent) { File file = null; try { file = file = ResourceUtils.getFile("classpath:service.json"); String jsonStr = BaseUtils.readFileContent(file); JSONArray array = JSONArray.fromObject(jsonStr); ServiceJsonBean[] arrayBean = (ServiceJsonBean[]) JSONArray.toArray(array,ServiceJsonBean.class); List<ServiceJsonBean> list = CollectionUtils.arrayToList(arrayBean); try{ //将list转换为以serviceId 为主键的Map SERVICE_MAP = list.parallelStream().collect( Collectors.toMap(ServiceJsonBean::getServiceId, bean -> bean) ); }catch (IllegalStateException e){ logger.error("service.json 配置出错,serviceId不得重复!",e); } servletContextEvent.getServletContext().setAttribute("SERVICE_MAP" , SERVICE_MAP); logger.debug("读取 service.json : "+SERVICE_MAP.toString()); } catch (FileNotFoundException e2) { logger.error(" service.json 配置出错,找不到文件!",e2); } } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }
3. 在controller添加一个拦截器,指定这个路径的请求全部走我们自定的处理流程(跳过controller)
/** * @ClassName RouterRequestInterceptor * @Description RouterRequestInterceptor * @Author 维康 * @Date 2018/7/26 16:08 * @Version 1.0 **/ public class RouterRequestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { return RouterToService.routerService(request,response); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception { } }
3.1 RouterToService根据serviceId匹配服务,然后进行反射调用服务
/** * @ClassName RouterToService * @Description RouterToService 匹配服务,反射调用 * @Author 维康 * @Date 2018/7/28 19:59 * @Version 1.0 **/ public class RouterToService { private static Map<Long,ServiceJsonBean> SERVICE_MAP = new HashMap<>(); static public final boolean routerService(HttpServletRequest request, HttpServletResponse response) throws IOException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { SERVICE_MAP = (Map<Long, ServiceJsonBean>) request.getServletContext().getAttribute("SERVICE_MAP"); String serviceIdStr = request.getParameter("serviceId"); ServiceJsonBean serviceJsonBean = null; response.setCharacterEncoding("UTF-8"); PrintWriter printWriter = response.getWriter(); if(serviceIdStr!=null){ Long serviceId = Long.valueOf(serviceIdStr); serviceJsonBean = SERVICE_MAP.get(serviceId); if(serviceJsonBean==null){ printWriter.write("ERROR_101 : No interface was found based on serviceId="+serviceId+"!"); return false; } }else{ printWriter.write("ERROR_100 : The parameter [serviceId] should not be empty!"); return false; } System.out.println(serviceJsonBean.toString()); //反射获取请求的接口 Class clz = Class.forName(serviceJsonBean.getServiceName()); //根据class从spring容器获取对象 Object obj = SpringContextUtil.getBean(clz); //反射获取该对象的所有方法 Method[] methods = obj.getClass().getDeclaredMethods(); String methodName = serviceJsonBean.getMethod(); boolean findMethod = false; //遍历方法找到要执行的方法 for (int i=0 ; i<methods.length ;i++){ if(methodName.equals(methods[i].getName())){ Method method = methods[i]; Class[] parameterTypes = method.getParameterTypes(); Object object = ""; if(parameterTypes.length == 0){ //该方法为无参方法 object = method.invoke(obj); }else{ if(parameterTypes.length == 1){ Class parameterType = parameterTypes[0]; //参数类型的名称 String simpleName = parameterType.getSimpleName(); //当参数为String/long/Integer/Date之类的时候 我们默认取参数名称为data if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(simpleName)){ //这里默认一个参数的时候,默认字段为data 只能填 serviceId=100&data=xxx //因为虚拟机编译后的字节码,参数名称会变成arg0..arg1之类的 String paramVal = request.getParameter("data"); if(paramVal!=null){ object = invokeSimpleParam(obj,method,simpleName,paramVal); }else{ object = "ERROR_003 : The parameter [data] should not be empty!"; } }else{ //服务当参数为一个自定义的对象时,反射获取对象,并将request的参数映射到对象的里的字段 Object arg = FieldUtil.setFieldValue(parameterType.getTypeName(),paramMapSet(request,parameterType.getTypeName())); object = method.invoke(obj ,arg); } }else{ System.err.println("暂不支持接口方法多参数调用,如果有多个参数,请封装为BO!"); object = "ERROR_002 : Method parameter too much! Only one parameter is allowed !"; } } printResult(object , printWriter); findMethod = true; break; } } if(!findMethod){ String result = "ERROR_001 :No matched method ["+serviceJsonBean.getMethod()+"] from service "+serviceJsonBean.getServiceName()+"!"; printWriter.write(result); } return false; } /** * 参数为简单类型的时候 */ static private final Object invokeSimpleParam(Object invObject , Method method , String paramType , String paramValue) { Object object = null; try { if(FieldTypeConstants.TYPE_STRING.equals(paramType)){ object = method.invoke(invObject , paramValue); } if(FieldTypeConstants.TYPE_LONG.equals(paramType)){ object = method.invoke(invObject , Long.parseLong(paramValue)); } if(FieldTypeConstants.TYPE_INT.equals(paramType) || FieldTypeConstants.TYPE_INTEGER.equals(paramType) ){ object = method.invoke(invObject , Integer.parseInt(paramValue)); } if(FieldTypeConstants.TYPE_BOOLEAN.equals(paramType)){ object = method.invoke(invObject , Boolean.parseBoolean(paramValue)); } if(FieldTypeConstants.TYPE_DATE.equals(paramType)){ object = method.invoke(invObject , FieldUtil.parseDate(paramValue)); } if(FieldTypeConstants.TYPE_DOUBLE.equals(paramType)){ object = method.invoke(invObject , Double.parseDouble(paramValue)); } if(FieldTypeConstants.TYPE_FLOAT.equals(paramType)){ object = method.invoke(invObject , Float.parseFloat(paramValue)); } } catch (IllegalAccessException e) { System.err.println("IllegalAccessException:"+e.getMessage()); object = "ERROR_000 : IllegalAccessException:"+e.getMessage(); } catch (InvocationTargetException e) { System.err.println("InvocationTargetException:"+e.getMessage()); object = "ERROR_000 : InvocationTargetException:"+e.getMessage(); } return object; } /** * 将request请求的参数封装为Map * @param request * @param paramTypeName * @return * @throws ClassNotFoundException */ static private final Map<String,String> paramMapSet(HttpServletRequest request, String paramTypeName) throws ClassNotFoundException { Map<String,String> map = new HashMap<>(); Field[] fields = Class.forName(paramTypeName).getDeclaredFields(); for (int i=0 ; i<fields.length ;i++){ String filedName = fields[i].getName(); map.put(filedName,request.getParameter(filedName)); } return map; } /** * 返回json格式的结果 * @param object * @param printWriter */ static private final void printResult(Object object , PrintWriter printWriter){ String returnTypeName = object.getClass().getSimpleName(); if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(returnTypeName)){ String resStr = ""; if(FieldTypeConstants.TYPE_DATE.equals(returnTypeName)){ //时间格式需要特殊转换 resStr = FieldUtil.fmtDate((Date)object); }else { resStr = object.toString(); } printWriter.write(resStr); }else{ JSONObject jsonObject = JSONObject.fromObject(object); printWriter.write(jsonObject.toString(4)); } } }
3.2 SpringContextUtil 从容器获取bean
/** * @ClassName SpringContextUtil * @Description SpringContextUtil * @Author 维康 * @Date 2018/7/26 18:33 * @Version 1.0 **/ @Configuration public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getCtx() { return SpringContextUtil.applicationContext; } public static T getBean(Class t) { return SpringContextUtil.applicationContext.getBean(t); } }
3.3 FieldUtil工具类,给入参赋值
/** * @ClassName FieldUtil * @Description 参数赋值 * @Author 维康 * @Date 2018/7/27 17:38 * @Version 1.0 **/ public class FieldUtil { /** * set属性的值到Bean * @param beanTypeName * @param valMap */ public static Object setFieldValue(String beanTypeName, Map<String, String> valMap) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class<?> cls = Class.forName(beanTypeName); Object bean = cls.newInstance(); // 取出bean里的所有方法 Method[] methods = cls.getDeclaredMethods(); Field[] fields = cls.getDeclaredFields(); for (Field field : fields) { try { String fieldSetName = parSetName(field.getName()); if (!checkSetMet(methods, fieldSetName)) { continue; } Method fieldSetMet = cls.getMethod(fieldSetName, field.getType()); String value = valMap.get(field.getName()); if (null != value && !"".equals(value)) { String fieldType = field.getType().getSimpleName(); if (FieldTypeConstants.TYPE_STRING.equals(fieldType)) { fieldSetMet.invoke(bean, value); } else if (FieldTypeConstants.TYPE_DATE.equals(fieldType)) { Date temp = parseDate(value); fieldSetMet.invoke(bean, temp); } else if (FieldTypeConstants.TYPE_INTEGER.equals(fieldType) || FieldTypeConstants.TYPE_INT.equals(fieldType)) { Integer intval = Integer.parseInt(value); fieldSetMet.invoke(bean, intval); } else if (FieldTypeConstants.TYPE_LONG.equalsIgnoreCase(fieldType)) { Long temp = Long.parseLong(value); fieldSetMet.invoke(bean, temp); } else if (FieldTypeConstants.TYPE_DOUBLE.equalsIgnoreCase(fieldType)) { Double temp = Double.parseDouble(value); fieldSetMet.invoke(bean, temp); } else if (FieldTypeConstants.TYPE_BOOLEAN.equalsIgnoreCase(fieldType)) { Boolean temp = Boolean.parseBoolean(value); fieldSetMet.invoke(bean, temp); } else if (FieldTypeConstants.TYPE_FLOAT.equalsIgnoreCase(fieldType)) { Float temp = Float.parseFloat(value); fieldSetMet.invoke(bean, temp); } else { System.err.println("not supper type" + fieldType); } } } catch (Exception e) { continue; } } return bean; } /** * 判断是否存在某属性的 set方法 * @param methods * @param fieldSetMet * @return boolean */ private static boolean checkSetMet(Method[] methods, String fieldSetMet) { for (Method met : methods) { if (fieldSetMet.equals(met.getName())) { return true; } } return false; } /** * 判断是否存在某属性的 get方法 * @param methods * @param fieldGetMet * @return boolean */ private static boolean checkGetMet(Method[] methods, String fieldGetMet) { for (Method met : methods) { if (fieldGetMet.equals(met.getName())) { return true; } } return false; } /** * 拼接某属性的 get方法 * @param fieldName * @return String */ private static String parGetName(String fieldName) { if (null == fieldName || "".equals(fieldName)) { return null; } return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); } /** * 拼接在某属性的 set方法 * @param fieldName * @return String */ private static String parSetName(String fieldName) { if (null == fieldName || "".equals(fieldName)) { return null; } return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); } /** * 格式化string为Date * @param datestr * @return date */ public static Date parseDate(String datestr) { if (null == datestr || "".equals(datestr)) { return null; } String fmtstr = null; if (datestr.indexOf(':') > 0) { fmtstr = "yyyy-MM-dd HH:mm:ss"; } else { fmtstr = "yyyy-MM-dd"; } SimpleDateFormat sdf = new SimpleDateFormat(fmtstr, Locale.UK); try { return sdf.parse(datestr); } catch (ParseException e) { return null; } } /** * 日期转化为String * @param date * @return date string */ public static String fmtDate(Date date) { if (null == date) { return null; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE); return sdf.format(date); } }
3.4 最后写一个通用的controller
/** * @ClassName RouterController * @Description RouterController * @Author 维康 * @Date 2018/7/27 14:28 * @Version 1.0 **/ @RequestMapping("/router") @Controller public class RouterController { @RequestMapping("/service") public String service(HttpServletRequest request){ return "index"; } }
4. 效果展示
此时拦截器拦截的路径为/router/service,那么我们请求这个服务就会按我们的流程处理。
这个时候只要写好了服务,然后在service.json配置好serviceName以及method,然后在请求的时候携带上servceId以及参数就行,不用再去写一个Controller了!
注意 : serviceId是全局唯一的