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開發手冊》,畢竟是咱們中國人自己開創的。另外最近再看一本新書叫《碼出高效》,翻了幾章,覺得挺不錯的,在此推薦給大家。