在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 {
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() {
@Override
public HttpResult execute(GetUserByAccountParam param) {
HttpResult 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 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 HttpResult successResult(T t) {
HttpResult result = new HttpResult();
result.setSuccess(true);
result.setModel(t);
return result;
}
public static HttpResult failedResult(String msgCode, String msgInfo) {
HttpResult result = new HttpResult();
result.setSuccess(false);
result.setMsgCode(msgCode);
result.setMsgInfo(msgInfo);
return result;
}
public static HttpResult failedResult(ErrorEnum errorEnum) {
HttpResult result = new HttpResult();
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);
}
}
觉得文章不错的话,别忘了关注我哦!
关于更将详细的代码,请参见笔者的github。点击打开链接
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?
提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/93219220424868249