Controller层相当于MVC中的C,也是安卓或者前端请求的接口。
首先说Controller为什么需要写的更加简化?
第一、Controller是不能复用的;
第二、即便是将Controller分类,如果Controller代码过于庞大,不利于维护;
第三、Controller中的CRUD之类的基本都是重复的,要么是返回数据,要么是返回状态码(通常定义一个ResultBean即可搞定);
我在第一个项目和第二个项目以及现在的项目犯了很多错误。
关于第一个项目和第二个项目的错误,今天暂不说。
第三个项目也就是现在的项目,Controller写的是过于庞大,参数表单校验都写在里面。
正常情况下,像比如参数非空校验之类的很常见,完全可以封装成一个ValidateUtils来校验,犯不着重复写了很多,虽然Hutool这个开源项目封装的StrUtils里面有对应的判断方法,但是感觉写了很多遍,着实不爽。觉得不经意间又犯了很多错误。重复写,显得代码冗余。
下面以人人开源的代码生成器为例,人人开源的代码生成器生成的Controller是这样的,代码如下:
@RestController @RequestMapping("/sys/menu") public class SysMenuController extends AbstractController { @Autowired private SysMenuService sysMenuService; /** * 导航菜单 */ @RequestMapping("/nav") public R nav(){ List<SysMenuEntity> menuList = sysMenuService.getUserMenuList(getUserId()); return R.ok().put("menuList", menuList); } /** * 所有菜单列表 */ @RequestMapping("/list") @RequiresPermissions("sys:menu:list") public List<SysMenuEntity> list(){ List<SysMenuEntity> menuList = sysMenuService.selectList(null); for(SysMenuEntity sysMenuEntity : menuList){ SysMenuEntity parentMenuEntity = sysMenuService.selectById(sysMenuEntity.getParentId()); if(parentMenuEntity != null){ sysMenuEntity.setParentName(parentMenuEntity.getName()); } } return menuList; } /** * 选择菜单(添加、修改菜单) */ @RequestMapping("/select") @RequiresPermissions("sys:menu:select") public R select(){ //查询列表数据 List<SysMenuEntity> menuList = sysMenuService.queryNotButtonList(); //添加顶级菜单 SysMenuEntity root = new SysMenuEntity(); root.setMenuId(0L); root.setName("一级菜单"); root.setParentId(-1L); root.setOpen(true); menuList.add(root); return R.ok().put("menuList", menuList); } /** * 菜单信息 */ @RequestMapping("/info/{menuId}") @RequiresPermissions("sys:menu:info") public R info(@PathVariable("menuId") Long menuId){ SysMenuEntity menu = sysMenuService.selectById(menuId); return R.ok().put("menu", menu); } /** * 保存 */ @SysLog("保存菜单") @RequestMapping("/save") @RequiresPermissions("sys:menu:save") public R save(@RequestBody SysMenuEntity menu){ //数据校验 verifyForm(menu); sysMenuService.insert(menu); return R.ok(); } /** * 修改 */ @SysLog("修改菜单") @RequestMapping("/update") @RequiresPermissions("sys:menu:update") public R update(@RequestBody SysMenuEntity menu){ //数据校验 verifyForm(menu); sysMenuService.updateById(menu); return R.ok(); } /** * 删除 */ @SysLog("删除菜单") @RequestMapping("/delete") @RequiresPermissions("sys:menu:delete") public R delete(long menuId){ if(menuId <= 31){ return R.error("系统菜单,不能删除"); } //判断是否有子菜单或按钮 List<SysMenuEntity> menuList = sysMenuService.queryListParentId(menuId); if(menuList.size() > 0){ return R.error("请先删除子菜单或按钮"); } sysMenuService.delete(menuId); return R.ok(); } /** * 验证参数是否正确 */ private void verifyForm(SysMenuEntity menu){ if(StringUtils.isBlank(menu.getName())){ throw new RRException("菜单名称不能为空"); } if(menu.getParentId() == null){ throw new RRException("上级菜单不能为空"); } //菜单 if(menu.getType() == Constant.MenuType.MENU.getValue()){ if(StringUtils.isBlank(menu.getUrl())){ throw new RRException("菜单URL不能为空"); } } //上级菜单类型 int parentType = Constant.MenuType.CATALOG.getValue(); if(menu.getParentId() != 0){ SysMenuEntity parentMenu = sysMenuService.selectById(menu.getParentId()); parentType = parentMenu.getType(); } //目录、菜单 if(menu.getType() == Constant.MenuType.CATALOG.getValue() || menu.getType() == Constant.MenuType.MENU.getValue()){ if(parentType != Constant.MenuType.CATALOG.getValue()){ throw new RRException("上级菜单只能为目录类型"); } return ; } //按钮 if(menu.getType() == Constant.MenuType.BUTTON.getValue()){ if(parentType != Constant.MenuType.MENU.getValue()){ throw new RRException("上级菜单只能为菜单类型"); } return ; } } }
再看看我的Controller吧,代码示例如下:
@RestController @RequiresAuthentication @RequestMapping("/sysMenu") public class SysMenuController { @Autowired private SysModuleService sysModuleService; @Autowired private SysMenuService sysMenuService; @Autowired private SysUserService sysUserServicec; /** * 菜单分页查询 * @param request * @return */ @GetMapping(value="/menuPagingQuery",produces="application/json;charset=utf-8") @ApiOperation(value="菜单分页查询",httpMethod="GET",notes="菜单分页查询") public JSONObject modulePageQuery(HttpServletRequest request) { JSONObject json = new JSONObject(); //公司编码 String companyCode = request.getParameter("companyCode"); //获取前台当前页 String currentPage = request.getParameter("pageno"); //获取前台每页显示数据 String pageSize = request.getParameter("pagesize"); //将前台通过request获取的currentPage参数转为Integer类型 Integer pageno = Integer.parseInt(currentPage.trim()); //将前台通过request获取的pageSize参数转为Integer类型 Integer pagesize = Integer.parseInt(pageSize.trim()); //将条件放入Map中 Map<String,Object> conditionMap = new HashMap<String,Object>(); conditionMap.put("companyCode", companyCode); conditionMap.put("start", (pageno-1)*pagesize); conditionMap.put("size", pagesize); //调用查询集合数据方法 List<SysMenu> list = sysMenuService.queryMenuPagingListInfo(conditionMap); int count =sysMenuService.queryMenuPagingTotalCount(conditionMap); //总页数计算 初始化为0 int totalPageCount = 0; if ( count % pagesize == 0 ) { totalPageCount = count / pagesize; } else { totalPageCount = count / pagesize + 1; } //判断集合数据是否为空 if(!list.isEmpty()) { Page<SysMenu> page = new Page<SysMenu>(); page.setDatas(list); page.setTotalno(totalPageCount); page.setTotalsize(count); json.put(CommonEnum.RETURN_MSG, "获得菜单信息"); json.put("page", page); json.put(CommonEnum.RETURN_CODE, "000000"); }else { json.put(CommonEnum.RETURN_MSG, "暂无数据"); json.put(CommonEnum.RETURN_CODE, "111111"); } return json; } /** * 根据模块编码获得对应的菜单信息 * @param moduleCodes * @return */ @GetMapping(value = "/getMenuListInfo",produces="application/json;charset=utf-8") @ApiOperation(value="根据模块编码获得对应的菜单信息",httpMethod="GET",notes="根据模块编码获得对应的菜单信息") public JSONObject getMenuListInfo(String moduleCodes) { JSONObject json = new JSONObject(); try { if(!StrUtil.isEmpty(moduleCodes)) { EntityWrapper<SysMenu> wrapper = new EntityWrapper<SysMenu>(); wrapper.eq("module_codes", moduleCodes); List<SysMenu> sysMenuList = sysMenuService.selectList(wrapper); if(!sysMenuList.isEmpty()) { json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "获得该模块下的所有菜单信息"); json.put("sysMenuList", sysMenuList); json.put("menuSize", sysMenuList.size()); }else { json.put(CommonEnum.RETURN_CODE, "111111"); json.put(CommonEnum.RETURN_MSG, "没有数据"); } }else { json.put(CommonEnum.RETURN_CODE, "333333"); json.put(CommonEnum.RETURN_MSG, "参数异常:参数不能为空"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "444444"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } /** * 新增菜单 * @return */ @PostMapping(value = "/addMenu",produces="application/json;charset=utf-8") @ApiOperation(value="新增菜单",httpMethod="POST",notes="新增菜单") public JSONObject addMenu(@RequestBody MenuDto menuDto) { JSONObject json = new JSONObject(); try { //参数非空校验 if(!StrUtil.isEmptyIfStr(menuDto)) { //根据用户编号获取操作用户信息 EntityWrapper<SysUser> wrapper =new EntityWrapper<SysUser>(); wrapper.eq("user_code", menuDto.getUserCode()); SysUser user = sysUserServicec.selectOne(wrapper); //根据模块名字获取对应模块信息 EntityWrapper<SysModule> wrapper2 = new EntityWrapper<SysModule>(); wrapper2.eq("module_name", menuDto.getModuleName()); SysModule module = sysModuleService.selectOne(wrapper2); SysMenu menu = new SysMenu(); menu.setMenuCode(String.valueOf(RandomUtil.randomInt(666666))); menu.setMenuName(menuDto.getMenuName()); menu.setMenuHref(menuDto.getMenuHref()); menu.setMenuIcon(menuDto.getMenuIcon()); menu.setMenuType(menuDto.getMenuType()); menu.setCreateBy(user.getUserName()); menu.setIsShow(menuDto.getIsShow()); menu.setTreeSort(menuDto.getTreeSort()); menu.setSysCode(module.getModuleCode()); menu.setModuleCodes(module.getModuleCode()); menu.setPermission(menuDto.getPermission()); menu.setCreateDate(DateUtil.date().toString()); menu.setUpdateBy(user.getUserName()); menu.setUpdateDate(DateUtil.date().toString()); menu.setRemarks(menuDto.getRemarks()); //调用添加逻辑 boolean isAddMenu = sysMenuService.insert(menu); if(isAddMenu) { json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "添加菜单成功"); }else { json.put(CommonEnum.RETURN_CODE, "111111"); json.put(CommonEnum.RETURN_MSG, "添加菜单失败"); } }else { json.put(CommonEnum.RETURN_CODE, "222222"); json.put(CommonEnum.RETURN_MSG, "参数校验异常"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "333333"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } /** * 根据菜单编号获取对应的菜单信息 * @param menuCode * @return */ @GetMapping(value="/selectByMenuCodeInfo",produces="application/json;charset=utf-8") @ApiOperation(value="根据菜单编号获取对应的菜单信息",httpMethod="GET",notes="根据菜单编号获取对应的菜单信息") public JSONObject selectByMenuCodeInfo(String menuCode) { JSONObject json = new JSONObject(); try { if(!StrUtil.isEmpty(menuCode)) { EntityWrapper<SysMenu> wrapper = new EntityWrapper<SysMenu>(); wrapper.eq("menu_code", menuCode); SysMenu menu = sysMenuService.selectOne(wrapper); if(menu!=null) { json.put("menu", menu); json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "获取菜单信息成功"); }else { json.put(CommonEnum.RETURN_CODE, "1111111"); json.put(CommonEnum.RETURN_MSG, "获取菜单信息失败"); } }else { json.put(CommonEnum.RETURN_CODE, "222222"); json.put(CommonEnum.RETURN_MSG, "参数异常"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "333333"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } /** * 删除菜单 * @param menuCode * @return */ @PostMapping(value="/deleteMenuInfo",produces="application/json;charset=utf-8") @ApiOperation(value="删除菜单",httpMethod="POST",notes="删除菜单") public JSONObject deleteMenuInfo(String menuCode) { JSONObject json = new JSONObject(); try { if(!StrUtil.isEmpty(menuCode)) { boolean isDelete = sysMenuService.deleteById(menuCode); if(isDelete) { json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "删除成功"); }else { json.put(CommonEnum.RETURN_CODE, "111111"); json.put(CommonEnum.RETURN_MSG, "删除失败"); } }else { json.put(CommonEnum.RETURN_CODE, "222222"); json.put(CommonEnum.RETURN_MSG, "参数异常"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "333333"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } /** * 修改菜单 * @return */ @PostMapping(value = "/modifyMenu",produces="application/json;charset=utf-8") @ApiOperation(value="修改菜单",httpMethod="POST",notes="修改菜单") public JSONObject modifyMenu(@RequestBody MenuDto menuDto) { JSONObject json = new JSONObject(); try { //参数非空校验 if(!StrUtil.isEmptyIfStr(menuDto)) { //根据用户编号获取操作用户信息 EntityWrapper<SysUser> wrapper =new EntityWrapper<SysUser>(); wrapper.eq("user_code", menuDto.getUserCode()); SysUser user = sysUserServicec.selectOne(wrapper); //根据模块名字获取对应模块信息 EntityWrapper<SysModule> wrapper2 = new EntityWrapper<SysModule>(); wrapper2.eq("module_name", menuDto.getModuleName()); SysModule module = sysModuleService.selectOne(wrapper2); SysMenu menu = new SysMenu(); menu.setMenuCode(menuDto.getMenuCode()); menu.setMenuName(menuDto.getMenuName()); menu.setMenuHref(menuDto.getMenuHref()); menu.setMenuIcon(menuDto.getMenuIcon()); menu.setMenuType(menuDto.getMenuType()); menu.setIsShow(menuDto.getIsShow()); menu.setTreeSort(menuDto.getTreeSort()); menu.setSysCode(module.getModuleCode()); menu.setPermission(menuDto.getPermission()); menu.setModuleCodes(module.getModuleCode()); menu.setUpdateBy(user.getUserName()); menu.setUpdateDate(DateUtil.date().toString()); menu.setRemarks(menuDto.getRemarks()); //调用修改逻辑 boolean isAddMenu = sysMenuService.updateById(menu); if(isAddMenu) { json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "修改菜单成功"); }else { json.put(CommonEnum.RETURN_CODE, "111111"); json.put(CommonEnum.RETURN_MSG, "修改菜单失败"); } }else { json.put(CommonEnum.RETURN_CODE, "222222"); json.put(CommonEnum.RETURN_MSG, "参数校验异常"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "333333"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } /** * 图标存储(以Cookie的形式保存) * @param icon * @param response * @return */ @GetMapping(value="iconStorage",produces="application/json;charset=utf-8") @ApiOperation(value="图标存储",httpMethod="GET",notes="图标存储") public JSONObject iconStorage(String icon,HttpServletResponse response) { JSONObject json = new JSONObject(); try { if(!StrUtil.isEmpty(icon)) { CookieUtils.setCookie(response, "icon", icon, 3600); json.put(CommonEnum.RETURN_CODE, "000000"); json.put(CommonEnum.RETURN_MSG, "图标存储成功"); }else { json.put(CommonEnum.RETURN_CODE, "1111111"); json.put(CommonEnum.RETURN_MSG, "图标存储失败"); } } catch (Exception e) { json.put(CommonEnum.RETURN_CODE, "222222"); json.put(CommonEnum.RETURN_MSG, "其他异常"); } return json; } }
两者比较,前者对重复的代码和一些常见的参数校验和异常捕捉拦截做了很好的封装,后者则相反。俗话说:向好同学学习。
下面我将贴一下上面的封装类:
R.java
import java.util.HashMap; import java.util.Map; public class R extends HashMap<String, Object> { private static final long serialVersionUID = 1L; public R() { put("code", 0); put("msg", "success"); } public static R error() { return error(500, "未知异常,请联系管理员"); } public static R error(String msg) { return error(500, msg); } public static R error(int code, String msg) { R r = new R(); r.put("code", code); r.put("msg", msg); return r; } public static R ok(String msg) { R r = new R(); r.put("msg", msg); return r; } public static R ok(Map<String, Object> map) { R r = new R(); r.putAll(map); return r; } public static R ok() { return new R(); } @Override public R put(String key, Object value) { super.put(key, value); return this; } }
RRException.java
import io.renren.common.utils.R; import org.apache.shiro.authz.AuthorizationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 异常处理器 * * @author Mark sunlightcs@gmail.com * @since 1.0.0 2016-10-27 */ @RestControllerAdvice public class RRExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 处理自定义异常 */ @ExceptionHandler(RRException.class) public R handleRRException(RRException e){ R r = new R(); r.put("code", e.getCode()); r.put("msg", e.getMessage()); return r; } @ExceptionHandler(DuplicateKeyException.class) public R handleDuplicateKeyException(DuplicateKeyException e){ logger.error(e.getMessage(), e); return R.error("数据库中已存在该记录"); } @ExceptionHandler(AuthorizationException.class) public R handleAuthorizationException(AuthorizationException e){ logger.error(e.getMessage(), e); return R.error("没有权限,请联系管理员授权"); } @ExceptionHandler(Exception.class) public R handleException(Exception e){ logger.error(e.getMessage(), e); return R.error(); } }
小结:
本次讲解的是如何将Controller写的更加简洁,同时也不要忘记一点,前面说到Controller的代码不能过于庞大,那么service可以稍微庞大点,前提是在业务需要的情况下。当然了,最好还是那句话,代码能写短点就短点,利于维护,提高性能。当然了,凡事没有绝对,不一定代码写的长,执行性能就差。这就需要开发者们实际开发中多注意。同时关于代码规范,可以参考借鉴《阿里巴巴Java开发手册》,毕竟是咱们中国人自己开创的。另外最近再看一本新书叫《码出高效》,翻了几章,觉得挺不错的,在此推荐给大家。