回调模式——让你的controller不再繁琐

    xiaoxiao2021-12-14  19

    在Java后台编程中,大家一般会使用MVC设计模式,即便使用的具体框架不尽相同。今天,我们来说说MVC中的这个C,也就是Controller。Controller是web程序中最先接触到用户request的地方,当然,前提是该request经过了身份认证和权限检查等重重考验,这一部分建议在框架的Interceptor中进行。详细内容请看笔者之前的博客玩转Spring!从拒绝Filter开始。好了,话不多说,进入正题。

    一般来说,Controller的作用是为每个URI提供一个处理方法,比如有一组User的增删改查操作,我们会写一个UserController,然后其中有四个方法,对应User的增删改查操作。好了,这部分没什么不同。我们来看看这四个方法的内部逻辑,比如增加用户addUser方法,我们需要接受一些关于User的元信息,然后检查这些元信息是否非空,是否合法等等,接着调用对应的service方法,然后返回一个执行正确或者执行错误的结果。再比如updateUser方法,我们仍然需要接受一个User的元信息,然后检查这些元信息是否非空,是否合法等等,接着调用对应的service方法,然后返回一个执行正确或者执行错误的结果。

    根据Donnot repeat yourself原则,我们自然想到:

    1. 可否做一个抽象,将所有的参数合法性检查放到一个地方来做,业务代码仅仅需要指定传入的参数类,框架会帮助检查参数的合法性。如果不合法,直接返回异常。

    2. 可否做一个抽象,将所有的返回值放到一个地方来做,业务代码可以将任意的结果类返回,而无需关心该结果类和JsonString的转化。

    针对第一个问题,我们设计一种模式,让业务developer每次实现一个URI,就新增一个对应该URI的Param类,然后框架会帮助检查这个Param类。

    针对第二个问题,我们设计一个包含泛型结果的类,让业务developer每次仅仅需要将某一个处理结果类放入即可。

    至于重用代码的抽象,我们使用回调模式,来看代码:

    public interface CommonExecute<T extends BaseParam> { HttpResult execute(T param); } public class CommonExecutor { public static String execute(HttpServletRequest request, HttpServletResponse response, Class<? extends BaseParam> paramClass, CommonExecute commonExecute) { //根据paramClass的原信息检查参数的是否存在以及各式是否正确 BaseParam baseParam = null; try { baseParam = paramClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new SystemException(ErrorEnum.RUNTIME_EXCEPTION); } //inject the values into the baseParam new ServletRequestDataBinder(baseParam).bind(request); HttpResult result = commonExecute.execute(baseParam); return JSON.toJSONString(result); } }

    先来看第一个问题,有了上面的这两个类以后,以后的Controller是这样的:

    @Resource(name = "userService") IUserService userService; @RequestMapping(value = CommonUrl.UrlConstant.GET_USER_BY_ACCOUNT, produces = CommonUtils.CONTENT_TYPE) @ResponseBody public String getUserByAccount(HttpServletRequest request, HttpServletResponse response) { return CommonExecutor.execute(request, response, CommonUrl.GET_USER_BY_ACCOUNT.getParamClass(), new CommonExecute<GetUserByAccountParam>() { @Override public HttpResult execute(GetUserByAccountParam param) { HttpResult<UserDTO> result = userService.getUserByAccount(param.getAccount()); return result; } }); } public class GetUserByAccountParam extends BaseParam { private String account; public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } }

    我们来看上面的代码,当我需要写一个getUserByAccount的API时,仅仅需要在对应的controller中添加一个如下的方法

    public String getUserByAccount(HttpServletRequest request, HttpServletResponse response) {}

    当我想添加比如名为xxx方法的时候,仅仅需要添加同样的方法:

    public String xxx(HttpServletRequest request, HttpServletResponse response) {}

    在方法体的内部,直接调用CommonExecutor的静态execute方法,该方法有四个参数,前两个参数是request和response参数,传入皆可,无需感知。第三个方法是该方法对应的URI对应的参数类,可以看到,他是一个继承自BaseParam子类的元类。说白了,就是你新添加的方法,需要传入那些参数,把这些参数写成一个类。

    Class<? extends BaseParam>

    第四个参数是一个实现了CommonExecute接口的匿名类,在这里进行具体的业务处理。读者可以看到,该类的一个传入的参数就是你自定义的传入参数类,只要用户在发送请求的时候传入和该参数类的字段域同名的参数,框架就会自动帮你注入到该传入参数类中。这样业务developer无需感知这个过程,在写CommonExecute接口的匿名类时,直接使用自定义的参数类即可。

    对于第二个问题,框架定义了一个HttpResult类:

    public class HttpResult<T> implements Serializable { private static final long serialVersionUID = -3404886040638951329L; protected boolean success; protected T model; protected String msgCode; protected String msgInfo; public HttpResult() { } public HttpResult(boolean success, String msgCode, String msgInfo) { this.success = success; this.msgCode = msgCode; this.msgInfo = msgInfo; } public static <T> HttpResult<T> successResult(T t) { HttpResult<T> result = new HttpResult<T>(); result.setSuccess(true); result.setModel(t); return result; } public static <T> HttpResult<T> failedResult(String msgCode, String msgInfo) { HttpResult<T> result = new HttpResult<T>(); result.setSuccess(false); result.setMsgCode(msgCode); result.setMsgInfo(msgInfo); return result; } public static <T> HttpResult<T> failedResult(ErrorEnum errorEnum) { HttpResult<T> result = new HttpResult<T>(); result.setSuccess(false); if (errorEnum != null) { result.setMsgCode(errorEnum.getErrCode()); result.setMsgInfo(errorEnum.getErrDesc()); } return result; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getModel() { return model; } public void setModel(T model) { this.model = model; } public String getMsgCode() { return msgCode; } public void setMsgCode(String msgCode) { this.msgCode = msgCode; } public String getMsgInfo() { return msgInfo; } public void setMsgInfo(String msgInfo) { this.msgInfo = msgInfo; } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } } 可以看见,HttpResult类是框架定义好的返回格式,在业务Developer实现CommonExecute接口的匿名类时,仅仅需要返回一个HttpResult类,框架会帮助把该HttpResult类格式化成为Json返回给用户,十分方便。

    觉得文章不错的话,别忘了关注我哦!

    关于更将详细的代码,请参见笔者的github。点击打开链接

    笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?

    提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/93219220424868249

    转载请注明原文地址: https://ju.6miu.com/read-972112.html

    最新回复(0)