统一异常处理,与 响应链

说明文档更新:https://gitee.com/heHouHui/jfinal_validated/wikis/Home


提供异常统一扑捉实现IErrorRequestResponseAdvice类即可:

/**
 * @Auther: hehh
 * @Date: 2018/12/27 12:40
 * @Description: 注意异常向下兼容
 */
public interface IErrorRequestResponseAdvice<T extends Throwable,E extends Render>{

    /**验证支持*/
    boolean supports(T throwable);

    /**异常处理*/
    E laterBodyWrite(T throwable,RenderManager renderManager,String[] urlPara,Action action,int code);

}


提供响应处理链实现IResponseAdvice即可:

/**
 * @Auther: hehh
 * @Date: 2018/12/26 18:00
 * @Description: 响应切面
 */
public interface  IResponseAdvice<T extends Render> {

     /**
      *  排序
      * @return
      */
     default int sort(){ return Integer.MAX_VALUE;}

     /**
      *  是否支持
      * @param clazz 响应的render calss
      * @param t render
      * @return
      */
     boolean supports(Class<? extends Render> clazz, Render t);

     /**
      *  处理响应
      * @param rqquestBody 响应render
      * @param renderManager render工厂
      * @param urlPara 请求url
      * @param action 请求action
      * @param controller 请求controller
      * @return
      */
     T beforeBodyWrite(Render rqquestBody, RenderManager renderManager,String[] urlPara,Action action, Controller controller);

}


 实现方案替换actionHander在不干扰源代码的情况下,插入自定义功能

/**
 * @Auther: hehh
 * @Date: 2018/12/26 17:41
 * @Description:
 */
public class ActionHandlerAdivce extends ActionHandler {

    private static final Log log = Log.getLog(ActionHandlerAdivce.class);

    /** 响应处理类 */
    public static Map<Class<? extends Render>,List<? extends IResponseAdvice<? extends Render>>> responseAdviceMap = new ConcurrentHashMap();

    /**异常处理器*/
    public static Map<Class<? extends Throwable>,IErrorRequestResponseAdvice> errorAdviceMap = new ConcurrentHashMap<>();

    /***
     *  获取异常处理器
     * @param aClass
     * @return
     */
    private List<IErrorRequestResponseAdvice> getErrorAdvice(Class<? extends Throwable> aClass){
        if(aClass == null){
             return null;
        }

        List<Class<? extends Throwable>> temp = new ArrayList<>();
        errorAdviceMap.keySet().forEach(new Consumer<Class<? extends Throwable>>() {
            @Override
            public void accept(Class<? extends Throwable> bClass) {
                if(bClass.isAssignableFrom(aClass) || bClass.equals(aClass)){
                    temp.add(bClass);
                }
            }
        });
        if(CollUtil.isEmpty(temp)){return  null;}

        List<Class<? extends Throwable>> sort = CollUtil.sort(temp, new Comparator<Class<? extends Throwable>>() {
            @Override
            public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
                return o1.isAssignableFrom(o2) ? -1 : 1;
            }
        });
        if(CollUtil.isEmpty(sort)){return  null;}
        List<IErrorRequestRequestAdvice> advice = new ArrayList<>();

        sort.forEach(new Consumer<Class<? extends Throwable>>() {
            @Override
            public void accept(Class<? extends Throwable> cClass) {
                advice.add(errorAdviceMap.get(cClass));
            }
        });

        return advice;
    }


    /**
     * handle
     * 1: Action action = actionMapping.getAction(target)
     * 2: new Invocation(...).invoke()
     * 3: render(...)
     *
     * @param target
     * @param request
     * @param response
     * @param isHandled
     */
    @Override
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if (target.indexOf('.') != -1) {
            return ;
        }

        isHandled[0] = true;
        String[] urlPara = {null};
        Action action = actionMapping.getAction(target, urlPara);

        if (action == null) {
            String qs = request.getQueryString();
            String error = "404 Action Not Found: " + (qs == null ? target : target + "?" + qs);
            if (log.isWarnEnabled()) {
                log.warn(error);
            }

            List<IErrorRequestResponseAdvice> errorAdvice = getErrorAdvice(RenderException.class);
            Render render = renderManager.getRenderFactory().getErrorRender(404).setContext(request, response);
            if(CollUtil.isEmpty(errorAdvice)){
                render.render();
                return;
            }else{
                throw  new RenderException(error);
            }
        }

        /**跨域在前*/
        if(!RequestBodyUtil.configurationCrossOrigin(action,request,response)){
            renderManager.getRenderFactory().getJsonRender(new ErrorResult<>(Code.REST_ERROR,"请求错误")).setContext(request,response).render();
            return;
        }
        RequestBodyUtil.resolutionBody(action,request);//解析body

        Controller controller = null;
        try {
            controller = controllerFactory.getController(action.getControllerClass());
            if (injectDependency) {com.jfinal.aop.Aop.inject(controller);}
            CPI._init_(controller,action,request,response,urlPara[0]);

            //TODO 进入拦截器前
            if (devMode) {
                if (ActionReporter.isReportAfterInvocation(request)) {
                    new Invocation(action, controller).invoke();
                    ActionReporter.report(target, controller, action);
                } else {
                    ActionReporter.report(target, controller, action);
                    new Invocation(action, controller).invoke();
                }
            }
            else {
                new Invocation(action, controller).invoke();
            }

            Render render = controller.getRender();
            if (render instanceof ForwardActionRender) {
                String actionUrl = ((ForwardActionRender)render).getActionUrl();
                if (target.equals(actionUrl)) {
                    throw new RuntimeException("The forward action url is the same as before.");
                } else {
                    handle(actionUrl, request, response, isHandled);
                }
                return ;
            }

            if (render == null) {
                render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName());
            }


            //TODO 响应前 执行响应处理链
            if(MapUtil.isNotEmpty(responseAdviceMap)){
                List<? extends IResponseAdvice<? extends Render>> iResponseAdvices = responseAdviceMap.get(render.getClass());
                if(CollUtil.isNotEmpty(iResponseAdvices)){
                    for (IResponseAdvice<? extends Render> iResponseAdvice : iResponseAdvices) {
                        if(iResponseAdvice.supports(render.getClass(),render)){
                            Render beforeRender = iResponseAdvice.beforeBodyWrite(render, renderManager, urlPara, action, controller);
                            if(beforeRender == null){
                                throw new ResponseAdviceExceptor("响应处理异常:"+iResponseAdvice.getClass()+" beforeBodyWrite 方法不能返回 null.");
                            }
                            render = beforeRender;
                        }
                    }
                }
            }
            render.setContext(request, response, action.getViewPath()).render();
        } catch (Exception e) {

            /**异常处理*/
            List<IErrorRequestResponseAdvice> errorAdvice = getErrorAdvice(e.getClass());
            if(CollUtil.isNotEmpty(errorAdvice)){
                int code = e instanceof RenderException?404:(e instanceof ActionException?((ActionException)e).getErrorCode():500);
                errorAdvice.forEach(new Consumer<IErrorRequestRequestAdvice>() {
                    @Override
                    public void accept(IErrorRequestRequestAdvice iErrorRequestRequestAdvice) {
                        if(iErrorRequestRequestAdvice.supports(e)){
                            Render render1 = iErrorRequestRequestAdvice.laterBodyWrite(e, renderManager, urlPara, action, code);
                            if(render1 != null){
                                 render1.setContext(request,response).render();
                                 return;
                            }
                        }
                    }
                });
            }else{
                //TODO 异常响应不处理
                if(e instanceof RenderException){
                    if (log.isErrorEnabled()) {
                        String qs = request.getQueryString();
                        log.error(qs == null ? target : target + "?" + qs, e);
                    }
                }else if(e instanceof ActionException){
                    handleActionException(target, request, response, action, (ActionException)e);
                }else{
                    if (log.isErrorEnabled()) {
                        String qs = request.getQueryString();
                        log.error(qs == null ? target : target + "?" + qs, e);
                    }
                    renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render();
                }
            }

        } finally {
            if (controller != null) {
                CPI._clear_(controller);
            }
        }
    }


    /**
     * 抽取出该方法是为了缩短 handle 方法中的代码量,确保获得 JIT 优化,
     * 方法长度超过 8000 个字节码时,将不会被 JIT 编译成二进制码
     *
     * 通过开启 java 的 -XX:+PrintCompilation 启动参数得知,handle(...)
     * 方法(73 行代码)已被 JIT 优化,优化后的字节码长度为 593 个字节,相当于
     * 每行代码产生 8.123 个字节
     */
    private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) {
        int errorCode = e.getErrorCode();
        String msg = null;
        if (errorCode == 404) {
            msg = "404 Not Found: ";
        } else if (errorCode == 400) {
            msg = "400 Bad Request: ";
        } else if (errorCode == 401) {
            msg = "401 Unauthorized: ";
        } else if (errorCode == 403) {
            msg = "403 Forbidden: ";
        }

        if (msg != null) {
            if (log.isWarnEnabled()) {
                String qs = request.getQueryString();
                msg = msg + (qs == null ? target : target + "?" + qs);
                if (e.getMessage() != null) {
                    msg = msg + "\n" + e.getMessage();
                }
                log.warn(msg);
            }
        } else {
            if (log.isErrorEnabled()) {
                String qs = request.getQueryString();
                log.error(errorCode + " Error: " + (qs == null ? target : target + "?" + qs), e);
            }
        }

        e.getErrorRender().setContext(request, response, action.getViewPath()).render();
    }
}


加载处理器的IPlugin

/**
 * @Auther: hehh
 * @Date: 2018/12/26 09:08
 * @Description: 处理响应
 */
public class RequestAdivcePlugin implements IPlugin {

    /**初始化 响应处理器*/
    private void initRequestAdivce(){

        ThreadUtil.excAsync(new Runnable() {
            Set<Class<?>> validates = ClassUtil.scanPackageBySuper("",IResponseAdvice.class);
            @Override
            public void run() {
                if(CollUtil.isNotEmpty(validates)){
                    Map<ResponseAdviceMapSort,IResponseAdvice> responseAdviceMap = new HashMap<>();
                    validates.forEach(new Consumer<Class<?>>() {
                        @Override
                        public void accept(Class<?> aClass) {
                            if(!ClassUtil.isAbstract(aClass) && !aClass.isInterface()){

                                Type t =aClass.getGenericInterfaces()[0];

                                //返回表示此类型实际类型参数的 Type 对象的数组
                                Type[]params = ((ParameterizedType) t).getActualTypeArguments();
                                Type param = params[0];
                                try {
                                    IResponseAdvice iResponseAdvice = (IResponseAdvice) aClass.newInstance();
                                    ResponseAdviceMapSort eesponseAdviceMapSort = new ResponseAdviceMapSort();
                                    eesponseAdviceMapSort.setAClass((Class<? extends Render>) TypeUtil.getClass(param));
                                    eesponseAdviceMapSort.setSort(iResponseAdvice.sort());
                                    responseAdviceMap.put(eesponseAdviceMapSort,iResponseAdvice);
                                } catch (InstantiationException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }


                            }
                        }
                    });

                    if(MapUtil.isNotEmpty(responseAdviceMap)){

                        for (ResponseAdviceMapSort responseAdviceMapSort : responseAdviceMap.keySet()) {
                            Class<? extends Render> aClass = responseAdviceMapSort.getAClass();
                            if(!ActionHandlerAdivce.responseAdviceMap.containsKey(aClass)) {
                                List<? extends IResponseAdvice<? extends Render>> responseAdvice = getResponseAdvice(aClass, responseAdviceMap);
                                ActionHandlerAdivce.responseAdviceMap.put(aClass, responseAdvice);
                            }
                        }

                    }
                }
            }
        },true);
    }

    /**
     *  初始化异常处理
     */
    private void initError(){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Set<Class<?>> validates = ClassUtil.
                scanPackageBySuper("",IErrorRequestResponseAdvice.class);
                if(CollUtil.isNotEmpty(validates)){
                    for (Class<?> aClass : validates) {
                        Type t =aClass.getGenericInterfaces()[0];

                        //返回表示此类型实际类型参数的 Type 对象的数组
                        Type[]params = ((ParameterizedType) t).getActualTypeArguments();
                        Type param = params[0];
                        try {
                            ActionHandlerAdivce.
                            errorAdviceMap.put((Class<? extends Throwable>) 
                            TypeUtil.getClass(param),
                            (IErrorRequestResponseAdvice)aClass.newInstance());
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        thread.start();
    }

    @Override
    public boolean start() {
        initRequestAdivce();
        initError();
        return true;
    }

    @Override
    public boolean stop() {
        return true;
    }




    /***
     *  得到响应处理器
     * @param clazz
     * @param <T>
     * @return
     */
    private  <T extends Render> 
    List<IResponseAdvice<T>> getResponseAdvice(Class<T> clazz,
    Map<ResponseAdviceMapSort,IResponseAdvice> responseAdviceMap){
        if(clazz == null || MapUtil.isEmpty(responseAdviceMap))
        { return null; }
        /***
         *  得到指定处理 T 的处理器
         */
        List<ResponseAdviceMapSort> filter = new ArrayList<>();
        responseAdviceMap.forEach(new BiConsumer<ResponseAdviceMapSort, 
        IResponseAdvice>() {
            @Override
            public void accept(ResponseAdviceMapSort responseAdviceMapSort,
             IResponseAdvice iResponseAdvice) {
                if (responseAdviceMapSort.getAClass().
                        isAssignableFrom(clazz)
                         || responseAdviceMapSort.getAClass().equals(clazz)) {
                    filter.add(responseAdviceMapSort);
                }
            }
        });

        if(CollUtil.isEmpty(filter)){
            return null;
        }

        /***
         *  排序
         */
        List<ResponseAdviceMapSort> sort = 
        CollUtil.sort(filter, new Comparator<ResponseAdviceMapSort>() {

            /**compare方法大于0,就把前一个数和后一个数交换,
            也就是把大的数放后面了,即所谓的升序了*/
            @Override
            public int compare(ResponseAdviceMapSort o1, 
            ResponseAdviceMapSort o2) {
                return o1.getSort() - o2.getSort();
            }
        });

        List<IResponseAdvice<T>> temp = new ArrayList<>();

        sort.forEach(new Consumer<ResponseAdviceMapSort>() {
            @Override
            public void accept(ResponseAdviceMapSort responseAdviceMapSort) {
                temp.add(responseAdviceMap.get(responseAdviceMapSort));
            }
        });
        return temp;
    }
}



在config中替换原有的actionHander即可

/**
     * 配置处理器 如在规模开发中  定制Handler来实现自定义的url映射
     */
    public void configHandler(Handlers me) {
        me.setActionHandler(new ActionHandlerAdivce());
    }



 异常效果

     异常(扑捉昨天的参数验证(action 与非action都可以))

**
 * @Auther: Administrator
 * @Date: 2018/10/8 16:40
 * @Description:
 */
@Validated
public class PoemsService extends BaseService<Poems> {

    public String aa(@Size(min = 5,msg = "名字长度不能小于5!") String aa){
        return "xxx";
    }
}

  

部分验证代码

 /**得到校验类*/
        ValidateAbstract iValidate = ValidateAbstract.validates.
                                get(parameterAnnotation.annotationType());
        /**校验是否符合规则*/
        if (iValidate.supports(method, parameter,
                                 parameterAnnotation.annotationType())) {
            /**校验*/
            Result verify = iValidate.verify(parameterAnnotation, 
                                    inv.getArg(i), method, parameter);
            /**code != 0 为失败 */
            if (verify.getCode() != 0) {
                    if (takeError != null && 
                            Result.class.isAssignableFrom(method.getReturnType())) {
                        inv.setReturnValue(verify);
                        return;
                    }
                    throw new ValidateException(verify.getMsg());
                    //TODO 不是action而又没接管就直接报错
                }
            }
        }

 

扑捉

**
 * @Auther: hehh
 * @Date: 2018/12/27 13:56
 * @Description:
 */
public class ValidateErrorAdvice implements 
IErrorRequestResponseAdvice<ValidateException,JsonRender>{
    /**
     * 验证支持
     *
     * @param throwable
     */
    @Override
    public boolean supports(ValidateException throwable) {
        return true;
    }

    /**
     * 异常处理
     *
     * @param throwable
     * @param renderManager
     * @param urlPara
     * @param action
     * @param code
     */
    @Override
    public JsonRender laterBodyWrite(ValidateException throwable,
     RenderManager renderManager, String[] urlPara, Action action, int code) {
        return (JsonRender)renderManager.getRenderFactory().
            getJsonRender(new ErrorResult<>
                (Code.PARAM_ERROR,throwable.getMessage()));
    }
}


 效果

image.png


响应效果


扑捉自定义的result

/**
 * @Auther: hehh
 * @Date: 2018/12/27 09:54
 * @Description: 处理json 有数据响应
 */
public class RequestJsonAdvice implements IResponseAdvice<JsonRender> {
    /**
     * 排序
     *
     * @return
     */
    @Override
    public int sort() {
        return 0;
    }

    /**
     * 是否支持
     *
     * @param clazz      响应的render calss
     * @param jsonRender render
     * @return
     */
    @Override
    public boolean supports(Class<? extends Render> clazz, Render jsonRender) {
        boolean b = jsonRender != null && jsonRender != null && jsonRender.getClass().equals(clazz);
        if(b){
            JsonRender jsonRender1 = (JsonRender) jsonRender;
            if(StrKit.notBlank(jsonRender1.getJsonText())){
                 JSONObject object = new JSONObject(jsonRender1.getJsonText());
                 if(null == object.getInt("code")){
                      b = false;
                 }
            }
        }
        return b;
    }

    /**
     * 处理响应
     *
     * @param rqquestBody    响应render
     * @param renderManager render工厂
     * @param urlPara       请求url
     * @param action        请求action
     * @param controller    请求controller
     * @return
     */
    @Override
    public JsonRender beforeBodyWrite(Render rqquestBody, RenderManager renderManager, String[] urlPara, Action action, Controller controller) {
        JsonRender rqquestBody1 = (JsonRender) rqquestBody;
        System.out.println("============处理响应:"+rqquestBody1.getJsonText());
        rqquestBody1 = new JsonRender("bbbbb");
        return rqquestBody1;
    }
}


 原响应

@Validated
    @RequestBody
    public void index(@NotBlank(msg = "不能为空啊!!!")  String name){
        renderJson(ps.aa(name));

    }


效果:

image.png


源码地址:https://gitee.com/heHouHui/jfinal_validated.git


有什么不对的地方,还请各位指正

评论区

JFinal

2018-12-27 16:38

说明文字要再多点就好了,感谢分享

静态代码块

2018-12-27 16:41

@JFinal 在gitHub上面已经准备写了,其实都很简单.

静态代码块

2018-12-27 18:32

@JFinal 文档已更新

热门分享

扫码入社