SSM框架中的先後端分離

認識先後端分離

在傳統的web應用開發中,大多數的程序員會將瀏覽器做爲先後端的分界線。將瀏覽器中爲用戶進行頁面展現的部分稱之爲前端,而將運行在服務器,爲前端提供業務邏輯和數據準備的全部代碼統稱爲後端。javascript

因爲先後端分離這個概念相對來講剛出現不久,不少人都是隻聞其聲,不見其形,因此可能會對它產生一些誤解,誤覺得先後端分離只是一種web應用開發模式,只要在web應用的開發期進行了先後端開發工做的分工就是先後端分離。html

其實先後端分離並不僅是開發模式,而是web應用的一種架構模式。在開發階段,先後端工程師約定好數據交互接口,實現並行開發和測試;在運行階段先後端分離模式須要對web應用進行分離部署,先後端以前使用HTTP或者其餘協議進行交互請求。然而做爲一種架構模式,咱們在實施的過程當中主要對如下四個方面來進行比較和從新認識。前端

先後端分離大概能夠從四個方面來理解:java

  1. 交互形式
  2. 代碼組織方式
  3. 開發模式
  4. 數據接口規範流程

1、交互形式

在先後端分離架構中,後端只須要負責按照約定的數據格式向前端提供可調用的API服務便可。先後端之間經過HTTP請求進行交互,前端獲取到數據後,進行頁面的組裝和渲染,最終返回給瀏覽器。程序員

2、代碼組織方式

在傳統架構模式中,先後端代碼存放於同一個代碼庫中,甚至是同一工程目錄下。頁面中還夾雜着後端代碼。先後端工程師進行開發時,都必須把整個項目導入到開發工具中。web

而先後端分離模式在代碼組織形式上有如下兩種:json

  • 半分離
    先後端共用一個代碼庫,可是代碼分別存放在兩個工程中。後端不關心或不多 關心前端元素的輸出狀況,前端不能獨立進行開發和測試,項目中缺少先後端 交互的測試用例。
  • 分離
    先後端代碼庫分離,前端代碼中有能夠進行Mock測試(經過構造虛擬測試對 象以簡化測試環境的方法)的僞後端,能支持前端的獨立開發和測試。然後端 代碼中除了功能實現外,還有着詳細的測試用例,以保證API的可用性,下降 集成風險。

3、開發模式

咱們以前的架構屬於傳統的MVC架構,總體沒有進行先後端分離,在項目的開發階段,前端工程師負責編寫HTML,完成前端的頁面設計並套頁面,而後再使用模板技術將寫好的前端代碼轉換爲Smarty腳本,同時內嵌一些後端提供的模板變量和一些邏輯操做。應用運行期,將所有代碼進行打包,和後端代碼部署到同一服務器上,同時會進行簡單的動靜態分離部署。segmentfault

此時,應用的開發流程以下圖所示。後端

而在實現先後端分離架構以後,前端工程師只須要編寫HTML、js、CSS等前端資源,而後通 過HTTP請求調用後端提供的服務便可。除了開發期的分離,在運行期先後端資源也 會進行分離部署。瀏覽器

先後端分離以後,開發流程將以下圖所示。

經過上面的兩幅流程圖,不難發現,在開發模式上,先後段分離不只僅只是工程師的分工開發,更重要的意義在於實現了先後端的並行開發,簡化了開發流程

4、數據接口規範流程

在開發期間先後端共同商定好數據接口的交互形式和數據格式。而後實現先後端的並行開發,其中前端工程師再開發完成以後能夠獨自進行mock測試,然後端也可使用接口測試平臺進行接口自測,而後先後端一塊兒進行功能聯調並校驗格式,最終進行自動化測試。

分離的四個好處

先後端分離模式和傳統的web應用架構相比有很大的不一樣,到底分仍是不分,這還真是個問題。

從目前應用軟件開發的發展趨勢來看,主要有兩方面須要注意:

  1. 愈來愈注重用戶體驗,隨着互聯網的發展,開始多終端化。
  2. 大型應用架構模式正在向雲化、微服務化發展。

咱們主要經過先後端分離架構,爲咱們帶來如下四個方面的提高:

  • 爲優質產品打造精益團隊
    經過將開發團隊先後端分離化,讓先後端工程師只須要專一於前端或後端的開發工做,是的先後端工程師實現自治,培養其獨特的技術特性,而後構建出一個全棧式的精益開發團隊。
  • 提高開發效率
    先後端分離之後,能夠實現先後端代碼的解耦,只要先後端溝通約定好應用所需接口以及接口參數,即可以開始並行開發,無需等待對方的開發工做結束。與此同時,即便需求發生變動,只要接口與數據格式不變,後端開發人員就不須要修改代碼,只要前端進行變更便可。如此一來整個應用的開發效率必然會有質的提高。
  • 完美應對複雜多變的前端需求
    若是開發團隊能完成先後端分離的轉型,打造優秀的先後端團隊,開發獨立化,讓開發人員作到專一專精,開發能力必然會有所提高,可以完美應對各類複雜多變的前端需求。
  • 加強代碼可維護性
    先後端分離後,應用的代碼再也不是先後端混合,只有在運行期纔會有調用依賴關係。

應用代碼將會變得整潔清晰,不管是代碼閱讀仍是代碼維護都會比之前輕鬆。


以上轉自先後端分離實踐(一)


利用JSON串來實現先後端分離

在本身最近作的項目中,使用的是利用SSM框架中的controller層來傳出JSON串,再經過jQuery中的.getJSON()來進行解析,再將數據傳到前端頁面。

controller層

部分代碼省略,放出關鍵代碼:

ShopManagementController

@Controller
@RequestMapping("/shopadmin")
public class ShopManagementController {
    @Autowired
    private ShopService shopService;
    @Autowired
    private ShopCategoryService shopCategoryService;
    @Autowired
    private AreaService areaService;
    @RequestMapping(value = "/getshopmanagementinfo",method = RequestMethod.GET)
    @ResponseBody
    private Map<String,Object> getShopManagementInfo(HttpServletRequest request) {
        Map<String,Object> modelMap = new HashMap<String, Object>();
        long shopId = HttpServletRequestUtil.getLong(request,"shopId");
        if(shopId <= 0){
            Object currentShopObj = request.getSession().getAttribute("shopId");
            if(currentShopObj == null){
                modelMap.put("redirect",true);
                modelMap.put("url","/shopadmin/shoplist");
            }else {
                Shop currentShop = (Shop) currentShopObj;
                modelMap.put("redirect",false);
                modelMap.put("shopId",currentShop.getShopId());
            }
        }else{
            Shop currentShop = new Shop();
            currentShop.setShopId(shopId);
            request.getSession().setAttribute("currentShop",currentShop);
            modelMap.put("redirect",false);
        }
        return modelMap;
    }

    @RequestMapping(value = "/getshoplist",method = RequestMethod.GET)
    @ResponseBody
    private Map<String,Object> getShopList(HttpServletRequest request){
        Map<String,Object> modelMap = new HashMap<String, Object>();
        PersonInfo user = new PersonInfo();
        user.setUserId(1L);
        user.setName("test");
        request.getSession().setAttribute("user",user);
        user = (PersonInfo) request.getSession().getAttribute("user");
        try{
            Shop shopCondition;
            shopCondition = new Shop();
            shopCondition.setOwner(user);
            ShopExecution se = shopService.getShopList(shopCondition,0,100);
            modelMap.put("shopList",se.getShopList());
            modelMap.put("user",user);
            modelMap.put("success",true);
        }catch (Exception e){
            modelMap.put("success",false);
            modelMap.put("errMsg",e.getMessage());
        }
        return modelMap;
    }

    @RequestMapping(value = "/getshopbyid",method = RequestMethod.GET)
    @ResponseBody
    private Map<String,Object> getShopById(HttpServletRequest request){
        Map<String,Object> modelMap = new HashMap<String,Object>();
        Long shopId = HttpServletRequestUtil.getLong(request,"shopId");
        if(shopId > -1){
            try {
                Shop shop = shopService.getByShopId(shopId);
                List<Area> areaList = areaService.getAreaList();
                modelMap.put("shop",shop);
                modelMap.put("areaList",areaList);
                modelMap.put("success",true);
            }catch (Exception e){
                modelMap.put("success",false);
                modelMap.put("errMsg",e.getMessage());
            }
        }else {
            modelMap.put("success",false);
            modelMap.put("errMsg","empty shopId");
        }
        return modelMap;
    }

    @RequestMapping(value = "/getshopinitinfo",method = RequestMethod.GET)
    @ResponseBody
    public Map<String,Object> getShopInitInfo(){
        Map<String,Object> modelMap = new HashMap<String, Object>();
        List<ShopCategory> shopCategoryList = new ArrayList<ShopCategory>();
        List<Area> areaList = new ArrayList<Area>();
        try {
            shopCategoryList = shopCategoryService.getShopCategoryList(new ShopCategory());
            areaList = areaService.getAreaList();
            modelMap.put("shopCategoryList",shopCategoryList);
            modelMap.put("areaList",areaList);
            modelMap.put("success",true);
        }catch (Exception e){
            modelMap.put("success",false);
            modelMap.put("errMsg",e.getMessage());
        }
        return modelMap;
    }

    @RequestMapping(value = "/modifyshop",method = {RequestMethod.POST})
    @ResponseBody
    public Map<String,Object> modifyShop(HttpServletRequest request) throws IOException {
        Map<String,Object> modelMap = new HashMap<String, Object>();
        //判斷驗證碼是否正確
        if(!CodeUtil.checkVerifyCode(request)){
            modelMap.put("success",false);
            modelMap.put("errMsg","輸入了錯誤的驗證碼");
            return modelMap;
        }
        //1.接收並轉化相應的參數,包括店鋪信息以及圖片信息
        String shopStr = HttpServletRequestUtil.getString(request,"shopStr");
        ObjectMapper mapper = new ObjectMapper();
        Shop shop = null;
        try {
            shop = mapper.readValue(shopStr,Shop.class);
        }catch (Exception e){
            modelMap.put("success",false);
            modelMap.put("errMsg",e.getMessage());
            return modelMap;
        }
        CommonsMultipartFile shopImg = null;
        CommonsMultipartResolver commonsMultipartResolver =new CommonsMultipartResolver(
                request.getSession().getServletContext());
        // 檢測文件是否有上傳文件流
        if (commonsMultipartResolver.isMultipart(request)){
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            shopImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("shopImg");
        }
        //2.修改店鋪信息
        if(shop != null && shop.getShopId() != null){
            PersonInfo owner =(PersonInfo) request.getSession().getAttribute("user");
            // session
            owner.setUserId(1L);
            shop.setOwner(owner);
            ShopExecution se;
            try {
                if (shopImg == null){
                    se = shopService.modifyShop(shop, new ImageHolder(null, null));
                }else {
                    se = shopService.modifyShop(shop,
                            new ImageHolder(shopImg.getInputStream(), shopImg.getOriginalFilename()));
                }
                if(se.getState()==ShopStateEnum.SUCCESS.getState()){
                    modelMap.put("success",true);
                }else {
                    modelMap.put("success",false);
                    modelMap.put("errMsg",se.getStateInfo());
                }
            }catch (ShopOperationException e){
                modelMap.put("success",false);
                modelMap.put("errMsg",e.getMessage());
            } catch (IOException e){
                modelMap.put("success",false);
                modelMap.put("errMsg",e.getMessage());
            }
            return modelMap;
        }else {
            modelMap.put("success",false);
            modelMap.put("errMsg","請輸入店鋪Id");
            return  modelMap;
        }
    }
}

getShopById作分析

//返回/getshopbyid的url,方法爲get
    @RequestMapping(value = "/getshopbyid",method = RequestMethod.GET)
    //返回JSON串
    @ResponseBody
    private Map<String,Object> getShopById(HttpServletRequest request){
        //初始化要返回的JSON串
        Map<String,Object> modelMap = new HashMap<String,Object>();
        //從Http請求中得到shopId值
        Long shopId = HttpServletRequestUtil.getLong(request,"shopId");
        if(shopId > -1){
            try {
                /**
                * 下面代碼作service層查詢,得到商店Id爲shopId的商店信息
                */
                Shop shop = shopService.getByShopId(shopId);
                List<Area> areaList = areaService.getAreaList();
                modelMap.put("shop",shop);
                modelMap.put("areaList",areaList);
                modelMap.put("success",true);
            }catch (Exception e){
                modelMap.put("success",false);
                modelMap.put("errMsg",e.getMessage());
            }
        }else {
            modelMap.put("success",false);
            modelMap.put("errMsg","empty shopId");
        }
        return modelMap;
    }

再來看看前端頁面是如何處理的。

(function() {
    var shopId = getQueryString('shopId');
    var isEdit = shopId?true:false;
    var initUrl = '/shopadmin/getshopinitinfo';
    var registerShopUrl = '/shopadmin/registershop';
    //訪問/shopadmin/getshopbyid這個url
    var shopInfoUrl = "/shopadmin/getshopbyid?shopId=" + shopId;
//從/shopadmin/getshopbyid?shopId這個url中得到controller層返回的JSON串,經過$.getJSON函數進行解析。
//獲取相關的鍵值對,而後插入前端HTML頁面。
function getShopInfoUrl(shopId) {
        $.getJSON(shopInfoUrl,function (data) {
            if(data.success){
                var shop = data.shop;
                $('#shop-name').val(shop.shopName);
                $('#shop-addr').val(shop.shopAddr);
                $('#shop-phone').val(shop.phone);
                $('#shop-desc').val(shop.shopDesc);
                var shopCategory = '<option data-id="'
                    + shop.shopCategory.shopCategoryId +'"selected>'
                    + shop.shopCategory.shopCategoryName +'</option>';
                var tempAreaHtml = '';
                data.areaList.map(function (item,index) {
                    tempAreaHtml += '<option data-id="' + item.areaId+'"selected>'
                        + item.areaName+'</option>';
                });
                $('#shop-category').html(shopCategory);
                $('#shop-category').attr('disable','disable');
                $('#shop-area').html(tempAreaHtml);
                $("#shop-area option[data-id='"+shop.area.areaId+"']").attr("selected","selected");
            }
        });
    }

訪問url,便可得到商店信息

流程圖:

相關文章
相關標籤/搜索