不积跬步,无以至千里;不积小流,无以成江海。

利用Java反射机制,直接调用Service服务,跳过Controller

JAVA 康康 800℃ 0评论

利用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是全局唯一的

5. 项目地址

springboot整合了dubbo

转载请注明:左手代码右手诗 » 利用Java反射机制,直接调用Service服务,跳过Controller

喜欢 (1)or分享 (0)
头像
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
隐藏