SSH?通常指的是 Struts2 做前端控制器卿城,Spring 管理各層的組件,Hibernate 負(fù)責(zé)持久化層铅搓。
SSM?則指的是 SpringMVC 做前端控制器瑟押,Spring 管理各層的組件,MyBatis 負(fù)責(zé)持久化層星掰。
共同之處是都使用了Spring的依賴注入DI來管理各層的組件多望,使用了面向切面編程AOP來實現(xiàn)日志管理,權(quán)限認(rèn)證氢烘,事務(wù)等通用功能的切入怀偷。
不同之處是 Struts2 和 SpringMVC 做前端控制器的區(qū)別,以及 Hibernate 和 MyBatis 做持久化時的區(qū)別播玖。但是椎工,Struts2 也可以和 MyBatis 搭配使用,SpringMVC 也可以和 Hibernate 搭配使用蜀踏。本文為了簡化對比维蒙,指定 Struts2 要和 Hibernate 搭配,SpringMVC 要和 MyBatis 搭配脓斩。
1.1. SSH 和 SSM 的實現(xiàn)原理區(qū)別
所在分層SSHSSM頁面層(View)JSPJSP控制器層(Controller)Struts2SpringMVC業(yè)務(wù)層(Service)JavaJava持久層(DAO)HibernateMyBatis數(shù)據(jù)庫層(DB)MySQL/OracleMySQL/Oracle組件管理(Bean)SpringSpring
(1) Struts2 的原理
一個請求在Struts2框架中的處理大概分為以下幾個步驟:
1木西、客戶端初始化一個指向Servlet容器(例如Tomcat)的請求
2畴栖、這個請求經(jīng)過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器随静,這個過濾器對于Struts2和其他框架的集成很有幫助,例如:SiteMesh Plugin)
3、接著FilterDispatcher被調(diào)用燎猛,F(xiàn)ilterDispatcher詢問ActionMapper來決定這個請求是否需要調(diào)用某個Action
FilterDispatcher是控制器的核心恋捆,就是mvc中c控制層的核心。下面粗略的分析下FilterDispatcher工作流程和原理:FilterDispatcher進行初始化并啟用核心doFilter重绷。
publicvoiddoFilter(ServletRequest req, ServletResponse res, FilterChain chain)throwsIOException, ServletException{? ? ? ? showDeprecatedWarning();? ? ? ? HttpServletRequest request = (HttpServletRequest) req;? ? ? ? HttpServletResponse response = (HttpServletResponse) res;? ? ? ? ServletContext servletContext = getServletContext();? ? ? ? String timerKey ="FilterDispatcher_doFilter: ";try{//FIXME:this should be refactored better to not duplicate work with the action invocationValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();? ? ? ? ? ? ActionContext ctx =newActionContext(stack.getContext());? ? ? ? ? ? ActionContext.setContext(ctx);? ? ? ? ? ? UtilTimerStack.push(timerKey);? ? ? ? ? ? request = prepareDispatcherAndWrapRequest(request, response);? ? ? ? ? ? ActionMapping mapping;try{//在這里找到Action的映射器mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());? ? ? ? ? ? }catch(Exception ex) {? ? ? ? ? ? ? ? log.error("error getting ActionMapping", ex);? ? ? ? ? ? ? ? dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);return;? ? ? ? ? ? }//沒有此Action的話,就去查找靜態(tài)資源if(mapping ==null) {// there is no action in this request, should we look for a static resource?String resourcePath = RequestUtils.getServletPath(request);if("".equals(resourcePath) &&null!= request.getPathInfo()) {? ? ? ? ? ? ? ? ? ? resourcePath = request.getPathInfo();? ? ? ? ? ? ? ? }if(staticResourceLoader.canHandle(resourcePath)) {? ? ? ? ? ? ? ? ? ? staticResourceLoader.findStaticResource(resourcePath, request, response);? ? ? ? ? ? ? ? }else{// this is a normal request, let it pass throughchain.doFilter(request, response);? ? ? ? ? ? ? ? }// The framework did its job herereturn;? ? ? ? ? ? }//有此Action的話則把控制權(quán)交給ActionProxydispatcher.serviceAction(request, response, servletContext, mapping);? ? ? ? }finally{? ? ? ? ? ? dispatcher.cleanUpRequest(request);try{? ? ? ? ? ? ? ? ActionContextCleanUp.cleanUp(req);? ? ? ? ? ? }finally{? ? ? ? ? ? ? ? UtilTimerStack.pop(timerKey);? ? ? ? ? ? }? ? ? ? ? ? devModeOverride.remove();? ? ? ? }? }
4沸停、如果ActionMapper決定需要調(diào)用某個Action,F(xiàn)ilterDispatcher把請求的處理交給ActionProxy
publicvoidserviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ActionMapping mapping) throws ServletException {? ? ? ? Map extraContext = createContextMap(request, response, mapping, context);// If there was a previous value stack, then create a new copy and pass it in to be used by the new ActionValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);booleannullStack = stack ==null;if(nullStack) {? ? ? ? ? ? ActionContext ctx = ActionContext.getContext();if(ctx !=null) {? ? ? ? ? ? ? ? stack = ctx.getValueStack();? ? ? ? ? ? }? ? ? ? }if(stack !=null) {? ? ? ? ? ? extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));? ? ? ? }StringtimerKey ="Handling request from Dispatcher";try{? ? ? ? ? ? UtilTimerStack.push(timerKey);Stringnamespace= mapping.getNamespace();Stringname = mapping.getName();Stringmethod = mapping.getMethod();//獲取配置文件Configuration config = configurationManager.getConfiguration();//根據(jù)配置文件找到此Action并生成ActionProxyActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext,true,false);? ? ? ? ? ? request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());// if the ActionMapping says to go straight to a result, do it!if(mapping.getResult() !=null) {? ? ? ? ? ? ? ? Result result = mapping.getResult();//ActionProxy創(chuàng)建一個ActionInvocation的實例result.execute(proxy.getInvocation());? ? ? ? ? ? }else{? ? ? ? ? ? ? ? proxy.execute();? ? ? ? ? ? }// If there was a previous value stack then set it back onto the requestif(!nullStack) {? ? ? ? ? ? ? ? request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);? ? ? ? ? ? }? ? ? ? }catch(ConfigurationException e) {? ? ? ? ? ? logConfigurationException(request, e);? ? ? ? ? ? sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);? ? ? ? }catch(Exception e) {if(handleException || devMode) {? ? ? ? ? ? ? ? sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);? ? ? ? ? ? }else{thrownewServletException(e);? ? ? ? ? ? }? ? ? ? }finally{? ? ? ? ? ? UtilTimerStack.pop(timerKey);? ? ? ? }? ? }
5昭卓、ActionProxy通過Configuration Manager詢問框架的配置文件愤钾,找到需要調(diào)用的Action類
6、ActionProxy創(chuàng)建一個ActionInvocation的實例候醒。
7能颁、ActionInvocation實例使用命名模式來調(diào)用,在調(diào)用Action的過程前后倒淫,涉及到相關(guān)攔截器(Intercepter)的調(diào)用伙菊。
8、一旦Action執(zhí)行完畢敌土,ActionInvocation負(fù)責(zé)根據(jù)struts.xml中的配置找到對應(yīng)的返回結(jié)果镜硕。返回結(jié)果通常是(但不總是,也可 能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版返干。
9兴枯、將處理結(jié)果返回給客戶端
(2) SpringMVC 的原理
執(zhí)行步驟:
第一步:發(fā)起請求到前端控制器(DispatcherServlet)
第二步:前端控制器請求HandlerMapping查找 Handler
可以根據(jù)xml配置、注解進行查找
第三步:處理器映射器HandlerMapping向前端控制器返回Handler
第四步:前端控制器調(diào)用處理器適配器去執(zhí)行Handler
第五步:處理器適配器去執(zhí)行Handler
第六步:Handler執(zhí)行完成給適配器返回ModelAndView
第七步:處理器適配器向前端控制器返回ModelAndView
ModelAndView是SpringMVC框架的一個底層對象矩欠,包括 Model和view
第八步:前端控制器請求視圖解析器去進行視圖解析
根據(jù)邏輯視圖名解析成真正的視圖(jsp)
第九步:視圖解析器向前端控制器返回View
第十步:前端控制器進行視圖渲染
視圖渲染將模型數(shù)據(jù)(在ModelAndView對象中)填充到request域
第十一步:前端控制器向用戶響應(yīng)結(jié)果
(3) Hibernate 的原理
1.通過Configuration().configure();讀取并解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>讀取并解析映射信息
3.通過config.buildSessionFactory();//創(chuàng)建SessionFactory
4.sessionFactory.openSession();//打開Sesssion
5.session.beginTransaction();//創(chuàng)建事務(wù)Transation
6.persistent operate持久化操作
7.session.getTransaction().commit();//提交事務(wù)
8.關(guān)閉Session
9.關(guān)閉SesstionFactory
(4) MyBatis原理
MyBatis框架執(zhí)行過程:
1念恍、配置MyBatis的配置文件,SqlMapConfig.xml(名稱不固定)
2晚顷、通過配置文件峰伙,加載MyBatis運行環(huán)境,創(chuàng)建SqlSessionFactory會話工廠
SqlSessionFactory 在實際使用時按單例方式该默。
3瞳氓、通過SqlSessionFactory創(chuàng)建SqlSession
SqlSession 是一個面向用戶接口(提供操作數(shù)據(jù)庫方法),實現(xiàn)對象是線程不安全的栓袖,建議sqlSession應(yīng)用場合在方法體內(nèi)匣摘。
4、調(diào)用 sqlSession 的方法去操作數(shù)據(jù)裹刮。
如果需要提交事務(wù)音榜,需要執(zhí)行 SqlSession 的 commit() 方法。
5捧弃、釋放資源赠叼,關(guān)閉SqlSession
1.2. Struts2 和 SpringMVC 在 web.xml 中配置的不同
(1) Struts2
struts2org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilterConfigclasspath:struts2/struts.xmlstruts2/*
Struts2使用Filter嵌入自己的框架擦囊。配置文件加載順序為:default.properties -> struts-default.xml -> struts-plugins.xml -> struts.xml -> struts.locale。
加載順序可以參考這篇文章的源碼分析了解更多嘴办。https://my.oschina.net/gschen/blog/121433
(2) SpringMVC
<!-- springmvc前端控制器瞬场,rest配置 -->springmvc_restorg.springframework.web.servlet.DispatcherServlet<!-- contextConfigLocation配置springmvc加載的配置文件(配置處理器映射器、適配器等等) 如果不配置contextConfigLocation涧郊,默認(rèn)加載的是/WEB-INF/servlet名稱-serlvet.xml(springmvc-servlet.xml) -->contextConfigLocationclasspath:spring/springmvc.xmlspringmvc_rest/
SpringMVC使用Servlet嵌入自己的框架贯被。
(3)web.xml不同之處
SpringMVC的入口是Servlet,而Struts2是Filter(這里要指出妆艘,F(xiàn)ilter和Servlet是不同的彤灶。以前認(rèn)為filter是servlet的一種特殊),這就導(dǎo)致了二者的機制不同批旺,這里就牽涉到Servlet和Filter的區(qū)別了枢希。但是這只是接管用戶請求的兩種不同方式而已,控制權(quán)被Struts2和SpringMVC掌握之后朱沃,想做什么事都是可以做到的苞轿。
Servlet
servlet是一種運行服務(wù)器端的java應(yīng)用程序,具有獨立于平臺和協(xié)議的特性逗物,并且可以動態(tài)的生成web頁面搬卒,它工作在客戶端請求與服務(wù)器響應(yīng)的中間層。最早支持 Servlet 技術(shù)的是 JavaSoft 的 Java Web Server翎卓。此后契邀,一些其它的基于 Java 的 Web Server 開始支持標(biāo)準(zhǔn)的 Servlet API。Servlet 的主要功能在于交互式地瀏覽和修改數(shù)據(jù)失暴,生成動態(tài) Web 內(nèi)容坯门。這個過程為:
1)客戶端發(fā)送請求至服務(wù)器端;2)服務(wù)器將請求信息發(fā)送至Servlet逗扒;3)Servlet生成響應(yīng)內(nèi)容并將其傳給服務(wù)器古戴。響應(yīng)內(nèi)容動態(tài)生成,通常取決于客戶端的請求矩肩;4)服務(wù)器將響應(yīng)返回給客戶端现恼。在Web應(yīng)用程序中,一個Servlet在一個時刻可能被多個用戶同時訪問黍檩。這時Web容器將為每個用戶創(chuàng)建一個線程來執(zhí)行Servlet叉袍。如果Servlet不涉及共享資源的問題,不必關(guān)心多線程問題刽酱。但如果Servlet需要共享資源喳逛,需要保證Servlet是線程安全的。為了簡化開發(fā)流程棵里,Servlet3.0引入了注解(annotation)润文,這使得web部署描述符web.xml不再是必須的選擇
Filter:Filter是一個可以復(fù)用的代碼片段姐呐,可以用來轉(zhuǎn)換HTTP請求、響應(yīng)和頭信息转唉。Filter不像Servlet皮钠,它不能產(chǎn)生一個請求或者響應(yīng)稳捆,它只是修改對某一資源的請求赠法,或者修改從某一的響應(yīng)。Servlet中的過濾器Filter是實現(xiàn)了javax.servlet.Filter接口的服務(wù)器端程序乔夯,主要的用途是過濾字符編碼砖织、做一些業(yè)務(wù)邏輯判斷等。其工作原理是末荐,只要你在web.xml文件配置好要攔截的客戶端請求侧纯,它都會幫你攔截到請求,此時你就可以對請求或響應(yīng)(Request甲脏、Response)統(tǒng)一設(shè)置編碼眶熬,簡化操作;同時還可進行邏輯判斷块请,如用戶是否已經(jīng)登陸娜氏、有沒有權(quán)限訪問該頁面等等工作。它是隨你的web應(yīng)用啟動而啟動的墩新,只初始化一次贸弥,以后就可以攔截相關(guān)請求,只有當(dāng)你的web應(yīng)用停止或重新部署的時候才銷毀海渊。Filter可認(rèn)為是Servlet的一種“變種”绵疲,它主要用于對用戶請求進行預(yù)處理,也可以對HttpServletResponse進行后處理臣疑,是個典型的處理鏈盔憨。它與Servlet的區(qū)別在于:它不能直接向用戶生成響應(yīng)。完整的流程是:Filter對用戶請求進行預(yù)處理讯沈,接著將請求交給Servlet進行處理并生成響應(yīng)般渡,最后Filter再對服務(wù)器響應(yīng)進行后處理。
1.3. Struts2 和 SpringMVC 處理用戶請求的不同
Struts2和SpringMVC的核心都是接管用戶的請求芙盘,解決傳統(tǒng)Servlet開發(fā)過于繁瑣驯用,重用性不高的問題。
Struts2和SpringMVC都有注解和配置文件兩種匹配用戶請求URL的方式儒老。
Struts2注解方式匹配URL
首先需要將架包(struts2-convention-plugin-xxx.jar)導(dǎo)入工程中
示例
packagecom.example.actions;importcom.opensymphony.xwork2.ActionSupport;importorg.apache.struts2.convention.annotation.Action;importorg.apache.struts2.convention.annotation.Actions;importorg.apache.struts2.convention.annotation.Result;importorg.apache.struts2.convention.annotation.Results;@Results({@Result(name="failure", location="fail.jsp")? })? public class HelloWorld extends ActionSupport {@Action(value="/different/url",? ? ? results={@Result(name="success", location="http://struts.apache.org", type="redirect")}? ? )? ? public String execute()? {returnSUCCESS;? ? }@Action("/another/url")? ? public String doSomething()? {returnSUCCESS;? ? }? }
Struts2配置方式匹配URL
/fail.jsphttp://struts.apache.org/fail.jsp
SpringMVC注解方式匹配URL
packagecom.jpkc.controller;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.servlet.ModelAndView;@RequestMapping("/admin")@ControllerpublicclassLoginController{@RequestMapping("/admin_home")publicString admin_home() throws Exception? ? {return"forward:/shop/index.jsp";? ? }@RequestMapping("/exit")publicString logout(ModelAndView model, HttpSession session) throws Exception? ? {? ? ? ? session.invalidate();return"redirect:/manager/login.jsp";? ? }}
SpringMVC配置方式匹配URL
publicclassItemsController1implementsController{@OverridepublicModelAndViewhandleRequest(HttpServletRequest request,
? ? ? ? ? ? HttpServletResponse response)throwsException{//調(diào)用Service查找 數(shù)據(jù)庫蝴乔,查詢商品列表,這里使用靜態(tài)數(shù)據(jù)模擬List itemsList =newArrayList();//向list中填充靜態(tài)數(shù)據(jù)Items items_1 =newItems();? ? ? ? items_1.setName("聯(lián)想筆記本");? ? ? ? items_1.setPrice(6000f);? ? ? ? items_1.setDetail("ThinkPad T430 聯(lián)想筆記本電腦驮樊!");? ? ? ? Items items_2 =newItems();? ? ? ? items_2.setName("蘋果手機");? ? ? ? items_2.setPrice(5000f);? ? ? ? items_2.setDetail("iphone6蘋果手機薇正!");? ? ? ? itemsList.add(items_1);? ? ? ? itemsList.add(items_2);//返回ModelAndViewModelAndView modelAndView =newModelAndView();//相當(dāng) 于request的setAttribut片酝,在jsp頁面中通過itemsList取數(shù)據(jù)modelAndView.addObject("itemsList", itemsList);//指定視圖modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");returnmodelAndView;? ? }}
<!-- 配置Handler -->
1、Struts2是類級別的攔截挖腰, 一個類對應(yīng)一個request上下文雕沿,SpringMVC是方法級別的攔截,一個方法對應(yīng)一個request上下文猴仑,而方法同時又跟一個url對應(yīng),所以說從架構(gòu)本身上SpringMVC就容易實現(xiàn)restful url,而struts2的架構(gòu)實現(xiàn)起來要費勁审轮,因為Struts2中Action的一個方法可以對應(yīng)一個url,而其類屬性卻被所有方法共享辽俗,這也就無法用注解或其他方式標(biāo)識其所屬方法了疾渣。
2、由上邊原因崖飘,SpringMVC的方法之間基本上獨立的榴捡,獨享request response數(shù)據(jù),請求數(shù)據(jù)通過參數(shù)獲取朱浴,處理結(jié)果通過ModelMap交回給框架吊圾,方法之間不共享變量,而Struts2搞的就比較亂翰蠢,雖然方法之間也是獨立的项乒,但其所有Action變量是共享的,這不會影響程序運行躏筏,卻給我們編碼 讀程序時帶來麻煩板丽,每次來了請求就創(chuàng)建一個Action,一個Action對象對應(yīng)一個request上下文趁尼。
3埃碱、由于Struts2需要針對每個request進行封裝,把request酥泞,session等servlet生命周期的變量封裝成一個一個Map砚殿,供給每個Action使用,并保證線程安全芝囤,所以在原則上似炎,是比較耗費內(nèi)存的。
1.4. Struts2 和 SpringMVC 實現(xiàn) RESTful 的不同
實現(xiàn)上面這個鏈接悯姊,其中l(wèi)ocalhost是域名羡藐,jpkc是項目名。
Struts2實現(xiàn)方式
/story/story_02.jsp
publicclassCourseActionextendsActionSupport{publicStringget_course_info(){? ? ? ? String actionName = ServletActionContext.getActionMapping().getName();? ? ? ? CourseInfo courseInfoFromDB = courseInfoDAO.findById(actionName);if(courseInfoFromDB ==null)? ? ? ? {return"404";? ? ? ? }? ? ? ? Course courseFromDB = courseDAO.findById(actionName);if(courseFromDB ==null)? ? ? ? {return"404";? ? ? ? }? ? ? ? setCourseInfo(courseInfoFromDB);? ? ? ? setCourse(courseFromDB);returnSUCCESS;? ? }}
SpringMVC實現(xiàn)方式
@ControllerpublicclassCourseController{@RequestMapping("/item/{id}")publicModelAndView get_course_info(ModelAndView model,@PathVariable("id")String id)? ? {if(CM.validIsEmptyWithTrim(id))? ? ? ? {? ? ? ? ? ? model.addObject("message","沒有找到此視頻頁面");? ? ? ? ? ? model.setViewName("/WEB-INF/jsp/error");returnmodel;? ? ? ? }? ? ? ? CourseInfo courseInfoFromDB=null;try{? ? ? ? ? ? courseInfoFromDB = courseInfoService.selectByPrimaryKey(id);? ? ? ? }catch(Exception e1)? ? ? ? {? ? ? ? ? ? System.out.println("沒有找到課程信息");? ? ? ? }if(courseInfoFromDB ==null)? ? ? ? {? ? ? ? ? ? model.addObject("message","沒有找到此視頻頁面");? ? ? ? ? ? model.setViewName("/WEB-INF/jsp/error");returnmodel;? ? ? ? }? ? ? ? Course courseFromDB =null;try{? ? ? ? ? ? courseFromDB = courseService.selectByPrimaryKey(id);? ? ? ? }catch(Exception e)? ? ? ? {? ? ? ? ? ? System.out.println("沒有查找到課程");? ? ? ? }if(courseFromDB ==null)? ? ? ? {? ? ? ? ? ? model.addObject("message","沒有找到此視頻頁面");? ? ? ? ? ? model.setViewName("/WEB-INF/jsp/error");returnmodel;? ? ? ? }? ? ? ? model.addObject("courseInfo", courseInfoFromDB);? ? ? ? model.addObject("course", courseFromDB);? ? ? ? model.setViewName("/story/story_02");returnmodel;? ? }}
對于類似于http://localhost/jpkc/item/id1這種鏈接悯许,Struts2實現(xiàn)RESTful風(fēng)格需要在代碼中調(diào)用ServletActionContext.getActionMapping().getName()獲取ActionName仆嗦。SpringMVC直接將鏈接映射到方法參數(shù)里去了。
如果類似于http://localhost/jpkc/id2/id1這種鏈接先壕,Struts2要進一步分析鏈接得到id1和id2瘩扼。SpringMVC依然可以將id2映射到方法參數(shù)上谆甜。從調(diào)用的角度來看SpringMVC要方便一些。但是如果將Struts2獲取方式封裝一下集绰,也可以得到同樣的效果规辱。
1.5. Struts2 和 SpringMVC 獲取 request 參數(shù)的不同
前臺頁面有一個表單需要提交。
Struts2 接收 request 參數(shù)
登錄系統(tǒng)請輸入用戶名和密碼用戶名密碼登錄
packagecom.jpkc.pojo;importjava.io.Serializable;publicclassAccountimplementsSerializable{privateString id;privateString password;privateString name;publicAccount(){super();// TODO Auto-generated constructor stub}publicStringgetId(){returnid;? ? }publicvoidsetId(String id){this.id = id;? ? }publicStringgetPassword(){returnpassword;? ? }publicvoidsetPassword(String password){this.password = password;? ? }publicStringgetName(){returnname;? ? }publicvoidsetName(String name){this.name = name;? ? }}
packagecom.jpkc.action;importjava.util.HashMap;importjava.util.Map;importcom.jpkc.common.CM;importcom.jpkc.pojo.Account;publicclassAccountActionextendsBaseAction{privateAccount account;publicStringlogin_do(){? ? ? ? String method = getRequest().getMethod();if(method.toUpperCase().equals("GET"))? ? ? ? {return"404";? ? ? ? }if(account ==null|| CM.validIsEmpty(account.getId()) || CM.validIsEmpty(account.getPassword()))? ? ? ? {returnERROR;? ? ? ? }? ? ? ? getSession().setAttribute("accountSession", account);returnSUCCESS;? ? }publicAccountgetAccount(){returnaccount;? ? }publicvoidsetAccount(Account account){this.account = account;? ? }}
SpringMVC 接收 request 參數(shù)
登錄系統(tǒng)請輸入用戶名和密碼用戶名密碼登錄
packagecom.jpkc.pojo;importjava.io.Serializable;publicclassAccountimplementsSerializable{privateString id;privateString password;privateString name;publicAccount(){super();// TODO Auto-generated constructor stub}publicStringgetId(){returnid;? ? }publicvoidsetId(String id){this.id = id;? ? }publicStringgetPassword(){returnpassword;? ? }publicvoidsetPassword(String password){this.password = password;? ? }publicStringgetName(){returnname;? ? }publicvoidsetName(String name){this.name = name;? ? }}
package com.jpkc.controller;importjava.util.HashMap;importjava.util.Map;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importorg.codehaus.jackson.map.ObjectMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.servlet.ModelAndView;importcom.jpkc.common.CM;importcom.jpkc.exception.CustomException;importcom.jpkc.mapper.CourseInfoMapper;importcom.jpkc.pojo.Account;importcom.jpkc.pojo.CourseInfo;importcom.jpkc.service.LoginService;@RequestMapping("/admin")@ControllerpublicclassLoginController{? ? @AutowiredLoginServiceloginService;? ? @RequestMapping(value ="/login_do", method = {RequestMethod.POST})publicvoid login_do(ModelAndViewmodel,HttpServletRequestrequest,HttpServletResponseresponse,HttpSessionsession,Accountaccount)throwsException{? ? ? ? response.setCharacterEncoding("utf-8");? ? ? ? response.setContentType("application/json;charset=utf-8");Map json = newHashMap();Stringinfo;if(account == null ||CM.validIsEmpty(account.getId()) ||CM.validIsEmpty(account.getPassword()))? ? ? ? {? ? ? ? ? ? info ="用戶名栽燕、密碼都是必填項罕袋。";? ? ? ? ? ? json.put("success",false);? ? ? ? ? ? json.put("info", info);? ? ? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? ? ? }? ? ? ? session.setAttribute("accountSession", account);? ? ? ? json.put("success",true);? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? }}
Struts2單個方法可以處理一個request,接收參數(shù)Account需要定義一個成員變量纫谅,Struts2會自動將對應(yīng)的參數(shù)調(diào)用成員變量的set方法設(shè)置進去炫贤。處理方法可以在方法內(nèi)獲取到溅固。用完還存在request級別Map中付秕。
SpringMVC的單個方法也對應(yīng)于一個request,接收參數(shù)Account需要定義一個方法參數(shù)侍郭,SpringMVC會自動將對應(yīng)的參數(shù)設(shè)置到方法參數(shù)中去询吴。處理方法可以在方法內(nèi)獲取到。用完即銷毀亮元。
可以看出兩種框架都可以實現(xiàn)參數(shù)的自動轉(zhuǎn)換猛计。Struts2定義一個成員變量,其他方法都是可以共享的爆捞,不用重新定義奉瘤。SpringMVC每個方法都是獨立的,方法參數(shù)是每一個方法獨享的煮甥。
各有利弊盗温。
成員變量共享可以避免重復(fù)定義,但是方法一多成肘,用到的成員變量原來越多卖局,整個Action類會慘不忍睹,因為你不知道其中一個方法具體會用到哪幾個成員變量双霍。而且用不到的成員變量也被存儲到request級別Map中了砚偶。造成內(nèi)存的浪費。
方法參數(shù)是方法獨享的洒闸。則不能復(fù)用到其他方法染坯,但是對于當(dāng)前方法來說有哪些參數(shù)足夠明確,而且不用和其他方法攪合丘逸,干脆利落单鹿。
從JVM角度來說,Struts2成員變量會被分配到堆中鸣个。SpringMVC方法參數(shù)則會存在于方法棧中羞反,一般認(rèn)為棧比堆更輕量一些布朦,方法結(jié)束,用完參數(shù)即回收昼窗。堆需要垃圾回收觸發(fā)時才能統(tǒng)一回收是趴。
1.6. Struts2 和 SpringMVC 限制訪問方式GET和POST的不同
在上例中,表單提交有密碼澄惊,需要指定只接受POST提交方式唆途。
Struts2指定POST方式
publicStringlogin_do(){? ? ? ? String method = getRequest().getMethod();if(method.toUpperCase().equals("GET"))? ? ? ? {return"404";? ? ? ? }? ? }
SpringMVC指定POST方式
@RequestMapping(value ="/login_do", method = { RequestMethod.POST })publicvoidlogin_do(ModelAndView model, HttpServletRequest request, HttpServletResponse response, HttpSession session, Account account) throws Exception? ? {? ? ? ? response.setCharacterEncoding("utf-8");? ? ? ? response.setContentType("application/json;charset=utf-8");? ? ? ? Map json =newHashMap();Stringinfo;if(account ==null|| CM.validIsEmpty(account.getId()) || CM.validIsEmpty(account.getPassword()))? ? ? ? {? ? ? ? ? ? info ="用戶名、密碼都是必填項掸驱。";? ? ? ? ? ? json.put("success",false);? ? ? ? ? ? json.put("info", info);? ? ? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? ? ? }? ? ? ? session.setAttribute("accountSession", account);? ? ? ? json.put("success",true);? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? }
Struts2限制只能通過POST方式訪問肛搬,是通過調(diào)用request的getMethod方法來得到當(dāng)前訪問方式。然后手工的去判斷毕贼。
SpringMVC也可以調(diào)用request的getMethod方法來判斷温赔,但是框架本身提供了方便的內(nèi)置判斷。使用注解即可鬼癣。
Struts2通過攔截器設(shè)置好訪問方式的代碼后陶贼,也可以通過注解的方式指定攔截器得到同樣的效果。本身不是太難的事情待秃,兩個框架都可以實現(xiàn)拜秧,Struts2需要手工實現(xiàn),SpringMVC默認(rèn)提供了章郁。即使SpringMVC不提供枉氮,調(diào)用SpringMVC的攔截器也能和Struts2的攔截器的效果一樣。在GET和POST訪問限制方面暖庄,并沒有誰優(yōu)誰劣聊替,都可以實現(xiàn)。只是SpringMVC愿意往前多走一小步雄驹。
1.7. Struts2 和 SpringMVC 攔截器的不同
后臺頁面需要登錄佃牛,我們可以使用攔截器限制未登錄的用戶訪問。
Struts2實現(xiàn)攔截器的方式
publicclassManagerLoginInterceptorextendsAbstractInterceptor{? ? @OverridepublicStringintercept(ActionInvocation invocation) throws Exception{? ? ? ? String actionName = ServletActionContext.getActionMapping().getName();// 如果是登錄医舆、注冊俘侠、退出的話就不要攔截了if(actionName.equals("exit") || actionName.equals("login") || actionName.equals("login_do") || actionName.equals("regist")? ? ? ? ? ? ? ? || actionName.equals("regist_do"))? ? ? ? {returninvocation.invoke();? ? ? ? }// 如果不是管理員就不能進入Manager managerTemp = (Manager) ServletActionContext.getRequest().getSession().getAttribute("managerSession");if(managerTemp ==null)? ? ? ? {return"manager_login";? ? ? ? }//驗證成功,放行蔬将。returninvocation.invoke();? ? }}
<!-- 自定義攔截器棧-攔截未登錄的管理員- -->json
Struts2還提供了很多默認(rèn)的攔截器供用戶調(diào)用爷速。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
SpringMVC實現(xiàn)攔截器的方式
publicclassLoginInterceptorimplementsHandlerInterceptor{// 進入 Handler方法之前執(zhí)行// 用于身份認(rèn)證、身份授權(quán)// 比如身份認(rèn)證霞怀,如果認(rèn)證通過表示當(dāng)前用戶沒有登陸惫东,需要此方法攔截不再向下執(zhí)行@OverridepublicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException{// 獲取請求的urlString url = request.getRequestURI();// 判斷url是否是公開 地址(實際使用時將公開 地址配置配置文件中)// 這里公開地址是登陸提交的地址if(url.indexOf("login") >=0|| url.indexOf("exit") >=0)? ? ? ? {// 如果進行登陸提交,放行returntrue;? ? ? ? }// 判斷sessionHttpSession session = request.getSession();// 從session中取出用戶身份信息Account account = (Account) session.getAttribute("accountSession");if(account !=null)? ? ? ? {// 身份存在,放行returntrue;? ? ? ? }// 執(zhí)行這里表示用戶身份需要認(rèn)證廉沮,跳轉(zhuǎn)登陸頁面request.getRequestDispatcher("/manager/login.jsp").forward(request, response);// return false表示攔截颓遏,不向下執(zhí)行// return true表示放行returnfalse;? ? }// 進入Handler方法之后,返回modelAndView之前執(zhí)行// 應(yīng)用場景從modelAndView出發(fā):將公用的模型數(shù)據(jù)(比如菜單導(dǎo)航)在這里傳到視圖滞时,也可以在這里統(tǒng)一指定視圖@OverridepublicvoidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throwsException{? ? ? ? System.out.println("HandlerInterceptor1...postHandle");? ? }// 執(zhí)行Handler完成執(zhí)行此方法// 應(yīng)用場景:統(tǒng)一異常處理叁幢,統(tǒng)一日志處理@OverridepublicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throwsException{? ? ? ? System.out.println("HandlerInterceptor1...afterCompletion");? ? }}
<!--攔截器 --><!--多個攔截器,順序執(zhí)行 --><!-- 登錄認(rèn)證攔截器 -->
攔截器實現(xiàn)機制上,Struts2有自己的interceptor機制坪稽,SpringMVC用的是獨立的AOP方式曼玩。都可以實現(xiàn)在前后進行攔截。
1.8. Struts2 和 SpringMVC 支持 JSON 的不同
有時我們界面的一些操作窒百,是通過 Ajax 調(diào)用后臺的服務(wù)黍判,獲取服務(wù)器返回的 json 數(shù)據(jù),進行后續(xù)的操作篙梢。
Struts2 實現(xiàn)JSON數(shù)據(jù)返回的方式
<!-- 這里指定將被Struts2序列化的屬性顷帖,該屬性在action中必須有對應(yīng)的getter方法 -->json
publicclassAccountActionextendsBaseAction{// 常用變量privateMap json;// 返回到前臺的map對象privateAccount account;publicAccountAction()? ? {? ? ? ? json =newHashMap();? ? }publicStringlogin_do()? ? {if(account ==null|| CM.validIsEmpty(account.getId()) || CM.validIsEmpty(account.getPassword()))? ? ? ? {? ? ? ? ? ? info ="用戶名、密碼都是必填項庭猩。";? ? ? ? ? ? json.put("success",false);? ? ? ? ? ? json.put("info", info);returnSUCCESS;? ? ? ? }? ? ? ? getSession().setAttribute("accountSession", account);? ? ? ? json.put("success",true);returnSUCCESS;? ? }}
$.post("login_do", $(".login-form").serialize(),function(json){if(json.success ==true)? ? {window.location.href="shop/index.jsp";? ? }else{? ? ? ? alert("操作失斂咚:"+ json.info);? ? }},"json");
SpringMVC 實現(xiàn)JSON數(shù)據(jù)返回的方式
<!--注解適配器 -->
$.post("login_do", $(".login-form").serialize(),function(json){if(json.success ==true)? ? {window.location.href="shop/index.jsp";? ? }else{? ? ? ? alert("操作失敵轮ⅰ:"+ json.info);? ? }},"json");
SpringMVC在控制器中返回json有兩種方式蔼水。
一種是使用response返回json。
@RequestMapping(value ="/login_do", method = { RequestMethod.POST })publicvoidlogin_do(ModelAndView model, HttpServletRequest request, HttpServletResponse response, HttpSession session, Account account) throws Exception? ? {? ? ? ? response.setCharacterEncoding("utf-8");? ? ? ? response.setContentType("application/json;charset=utf-8");? ? ? ? Map json =newHashMap();Stringinfo;if(account ==null|| CM.validIsEmpty(account.getId()) || CM.validIsEmpty(account.getPassword()))? ? ? ? {? ? ? ? ? ? info ="用戶名录肯、密碼都是必填項趴腋。";? ? ? ? ? ? json.put("success",false);? ? ? ? ? ? json.put("info", info);? ? ? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? ? ? }? ? ? ? session.setAttribute("accountSession", account);? ? ? ? json.put("success",true);? ? ? ? response.getWriter().write(newObjectMapper().writeValueAsString(json));return;? ? }
另一種是使用@ResponseBody注解方式。
@RequestMapping(value ="/login_do", method = { RequestMethod.POST })public@ResponseBodyMap login_do(ModelAndView model, HttpServletRequest request, HttpServletResponse response, HttpSession session, Account account) throws Exception? ? {? ? ? ? Map json =newHashMap();Stringinfo;if(account ==null|| CM.validIsEmpty(account.getId()) || CM.validIsEmpty(account.getPassword()))? ? ? ? {? ? ? ? ? ? info ="用戶名论咏、密碼都是必填項优炬。";? ? ? ? ? ? json.put("success",false);? ? ? ? ? ? json.put("info", info);returnjson;? ? ? ? }? ? ? ? session.setAttribute("accountSession", account);? ? ? ? json.put("success",true);returnjson;? ? }
可以看出,Struts2 和 SpringMVC 都可以實現(xiàn) Ajax 請求返回 JSON厅贪。實現(xiàn)方式上蠢护,Struts2在配置文件配置返回類型為JSON。SpringMVC在方法上加一個@ResponseBody注解即可返回對應(yīng)類型轉(zhuǎn)成的JSON字符串养涮。都是對返回數(shù)據(jù)轉(zhuǎn)成JSON葵硕,但是不得不說SpringMVC的寫法方便太多了。
1.9. Hibernate 和 MyBatis 在 ORM 側(cè)重點的不同
Hibernate對數(shù)據(jù)庫結(jié)構(gòu)提供了較為完整的封裝贯吓,Hibernate的O/R Mapping實現(xiàn)了POJO 和數(shù)據(jù)庫表之間的映射懈凹,以及SQL 的自動生成和執(zhí)行。程序員往往只需定義好了POJO 到數(shù)據(jù)庫表的映射關(guān)系悄谐,即可通過Hibernate 提供的方法完成持久層操作介评。程序員甚至不需要對SQL 的熟練掌握,?Hibernate/OJB 會根據(jù)指定的存儲邏輯爬舰,自動生成對應(yīng)的SQL?并調(diào)用JDBC 接口加以執(zhí)行们陆。
MyBatis 的著力點寒瓦,則在于POJO 與SQL之間的映射關(guān)系。然后通過映射配置文件坪仇,將SQL所需的參數(shù)孵构,以及返回的結(jié)果字段映射到指定POJO。 相對Hibernate“O/R”而言烟很,MyBatis 是一種“Sql Mapping”的ORM實現(xiàn)颈墅。
SQL語句支持:Hibernate可以完全不用手寫SQL語句,MyBatis手動維護SQL語句雾袱。Hibernate修改優(yōu)化SQL語句困難恤筛,MyBatis由于SQL語句自己控制,優(yōu)化非常方便芹橡。
開發(fā)速度:Hibernate的真正掌握要比Mybatis來得難些毒坛。Mybatis框架相對簡單很容易上手,但也相對簡陋些林说。
開發(fā)社區(qū):Hibernate 與Mybatis都是流行的持久層開發(fā)框架煎殷,但Hibernate開發(fā)社區(qū)相對多熱鬧些,支持的工具也多腿箩,更新也快豪直。而Mybatis相對平靜,工具較少珠移。
開發(fā)工作量:Hibernate和MyBatis都有相應(yīng)的代碼生成工具弓乙。可以生成簡單基本的DAO層方法钧惧。
針對高級查詢暇韧,Mybatis需要手動編寫SQL語句,以及ResultMap浓瞪。而Hibernate有良好的映射機制懈玻,開發(fā)者無需關(guān)心SQL的生成與結(jié)果映射,可以更專注于業(yè)務(wù)流程乾颁。
1.10. Hibernate 和 MyBatis 在調(diào)優(yōu)方面的不同
制定合理的緩存策略涂乌;
盡量使用延遲加載特性;
采用合理的Session管理機制钮孵;
SQL優(yōu)化方面
Hibernate的查詢會將表中的所有字段查詢出來骂倘,這一點會有性能消耗。Hibernate也可以自己寫SQL來指定需要查詢的字段巴席,但這樣就破壞了Hibernate開發(fā)的簡潔性历涝。而Mybatis的SQL是手動編寫的,所以可以按需求指定查詢的字段。
Hibernate HQL語句的調(diào)優(yōu)需要將SQL打印出來荧库,而Hibernate的SQL被很多人嫌棄因為太丑了堰塌。MyBatis的SQL是自己手動寫的所以調(diào)整方便。但Hibernate具有自己的日志統(tǒng)計分衫。Mybatis本身不帶日志統(tǒng)計场刑,使用Log4j進行日志記錄。
擴展性方面
Hibernate與具體數(shù)據(jù)庫的關(guān)聯(lián)只需在XML文件中配置即可蚪战,所有的HQL語句與具體使用的數(shù)據(jù)庫無關(guān)牵现,移植性很好。MyBatis項目中所有的SQL語句都是依賴所用的數(shù)據(jù)庫的邀桑,所以不同數(shù)據(jù)庫類型的支持不好瞎疼。
1.11. Hibernate 和 MyBatis 在對象管理與抓取策略的不同
對象管理
Hibernate 是完整的對象/關(guān)系映射解決方案,它提供了對象狀態(tài)管理(state management)的功能壁畸,使開發(fā)者不再需要理會底層數(shù)據(jù)庫系統(tǒng)的細(xì)節(jié)贼急。也就是說,相對于常見的 JDBC/SQL 持久層方案中需要管理 SQL 語句,Hibernate采用了更自然的面向?qū)ο蟮囊暯莵沓志没?Java 應(yīng)用中的數(shù)據(jù)。
換句話說绢记,使用 Hibernate 的開發(fā)者應(yīng)該總是關(guān)注對象的狀態(tài)(state),不必考慮 SQL 語句的執(zhí)行走敌。這部分細(xì)節(jié)已經(jīng)由 Hibernate 掌管妥當(dāng),只有開發(fā)者在進行系統(tǒng)性能調(diào)優(yōu)的時候才需要進行了解这揣。
而MyBatis在這一塊沒有文檔說明悔常,用戶需要對對象自己進行詳細(xì)的管理。當(dāng)調(diào)用sqlSession.commit()方法時才會進行真正的提交给赞。
抓取策略
Hibernate對實體關(guān)聯(lián)對象的抓取有著良好的機制。對于每一個關(guān)聯(lián)關(guān)系都可以詳細(xì)地設(shè)置是否延遲加載矫户,并且提供關(guān)聯(lián)抓取片迅、查詢抓取、子查詢抓取皆辽、批量抓取四種模式柑蛇。 它是詳細(xì)配置和處理的。
而Mybatis的延遲加載是全局配置的驱闷,在resultMap中使用association中的select指定延遲加載去執(zhí)行的statement的id耻台。
<!-- 延遲加載的resultMap --><!--對訂單信息進行映射配置? --><!-- 實現(xiàn)對用戶信息進行延遲加載 -->
1.12. Hibernate 和 MyBatis 在緩存機制的不同
Hibernate緩存
Hibernate一級緩存是Session緩存,利用好一級緩存就需要對Session的生命周期進行管理好空另。建議在一個Action操作中使用一個Session盆耽。一級緩存需要對Session進行嚴(yán)格管理。
Hibernate二級緩存是SessionFactory級的緩存。 SessionFactory的緩存分為內(nèi)置緩存和外置緩存摄杂。內(nèi)置緩存中存放的是SessionFactory對象的一些集合屬性包含的數(shù)據(jù)(映射元素?fù)?jù)及預(yù)定SQL語句等),對于應(yīng)用程序來說,它是只讀的坝咐。外置緩存中存放的是數(shù)據(jù)庫數(shù)據(jù)的副本,其作用和一級緩存類似.二級緩存除了以內(nèi)存作為存儲介質(zhì)外,還可以選用硬盤等外部存儲設(shè)備。二級緩存稱為進程級緩存或SessionFactory級緩存析恢,它可以被所有session共享墨坚,它的生命周期伴隨著SessionFactory的生命周期存在和消亡。
MyBatis緩存
MyBatis 包含一個非常強大的查詢緩存特性,它可以非常方便地配置和定制映挂。MyBatis 3 中的緩存實現(xiàn)的很多改進都已經(jīng)實現(xiàn)了,使得它更加強大而且易于配置泽篮。
一級緩存是SqlSession級別的緩存,二級緩存是mapper(命名空間)級別的緩存柑船,默認(rèn)情況下是沒有開啟二級緩存的咪辱。
要開啟二級緩存,你需要在你的 SQL 映射文件中添加一行: <cache/>
字面上看就是這樣。這個簡單語句的效果如下:
映射語句文件中的所有 select 語句將會被緩存椎组。
映射語句文件中的所有 insert,update 和 delete 語句會刷新緩存油狂。
緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
根據(jù)時間表(比如 no Flush Interval,沒有刷新間隔), 緩存不會以任何時間順序 來刷新寸癌。
緩存會存儲列表集合或?qū)ο?無論查詢方法返回什么)的 1024 個引用专筷。
緩存會被視為是 read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而 且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改。
所有的這些屬性都可以通過緩存元素的屬性來修改蒸苇。
比如: <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
這個更高級的配置創(chuàng)建了一個 FIFO 緩存,并每隔 60 秒刷新,存數(shù)結(jié)果對象或列表的 512 個引用,而且返回的對象被認(rèn)為是只讀的,因此在不同線程中的調(diào)用者之間修改它們會 導(dǎo)致沖突磷蛹。可用的收回策略有, 默認(rèn)的是 LRU:
LRU – 最近最少使用的:移除最長時間不被使用的對象溪烤。
FIFO – 先進先出:按對象進入緩存的順序來移除它們味咳。
SOFT – 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
WEAK – 弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象檬嘀。
flushInterval(刷新間隔)可以被設(shè)置為任意的正整數(shù),而且它們代表一個合理的毫秒 形式的時間段槽驶。默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅調(diào)用語句時刷新。
size(引用數(shù)目)可以被設(shè)置為任意正整數(shù),要記住你緩存的對象數(shù)目和你運行環(huán)境的 可用內(nèi)存資源數(shù)目鸳兽。默認(rèn)值是1024掂铐。
readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會給所有調(diào)用者返回緩 存對象的相同實例揍异。因此這些對象不能被修改全陨。這提供了很重要的性能優(yōu)勢≈灾溃可讀寫的緩存 會返回緩存對象的拷貝(通過序列化) 辱姨。這會慢一些,但是安全,因此默認(rèn)是 false。
相同點
Hibernate和Mybatis的二級緩存除了采用系統(tǒng)默認(rèn)的緩存機制外戚嗅,都可以通過實現(xiàn)你自己的緩存或為其他第三方緩存方案雨涛,創(chuàng)建適配器來完全覆蓋緩存行為枢舶。
不同點
Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細(xì)配置,然后再在具體的表-對象映射中配置是那種緩存镜悉。
MyBatis的二級緩存配置都是在每個具體的表-對象映射中進行詳細(xì)配置祟辟,這樣針對不同的表可以自定義不同的緩存機制。并且Mybatis可以在命名空間中共享相同的緩存配置和實例侣肄,通過Cache-ref來實現(xiàn)旧困。
兩者比較
因為Hibernate對查詢對象有著良好的管理機制,用戶無需關(guān)心SQL稼锅。所以在使用二級緩存時如果出現(xiàn)臟數(shù)據(jù)吼具,系統(tǒng)會報出錯誤并提示。
而MyBatis在這一方面矩距,使用二級緩存時需要特別小心拗盒。如果不能完全確定數(shù)據(jù)更新操作的波及范圍,避免Cache的盲目使用锥债。否則陡蝇,臟數(shù)據(jù)的出現(xiàn)會給系統(tǒng)的正常運行帶來很大的隱患。
1.13. Hibernate 和 MyBatis 對比總結(jié)
兩者相同點
Hibernate與MyBatis都可以是通過SessionFactoryBuider由XML配置文件生成SessionFactory哮肚,然后由SessionFactory 生成Session登夫,最后由Session來開啟執(zhí)行事務(wù)和SQL語句。其中SessionFactoryBuider允趟,SessionFactory恼策,Session的生命周期都是差不多的。
Hibernate和MyBatis都支持JDBC和JTA事務(wù)處理潮剪。
Mybatis優(yōu)勢
MyBatis可以進行更為細(xì)致的SQL優(yōu)化涣楷,可以減少查詢字段。
MyBatis容易掌握抗碰,而Hibernate門檻較高狮斗。
Hibernate優(yōu)勢
Hibernate的DAO層開發(fā)比MyBatis簡單,Mybatis需要維護SQL和結(jié)果映射改含。
Hibernate對對象的維護和緩存要比MyBatis好情龄,對增刪改查的對象的維護要方便。
Hibernate數(shù)據(jù)庫移植性很好捍壤,MyBatis的數(shù)據(jù)庫移植性不好,不同的數(shù)據(jù)庫需要寫不同SQL鞍爱。
Hibernate有更好的二級緩存機制鹃觉,可以使用第三方緩存。MyBatis本身提供的緩存機制不佳睹逃,更新操作不能指定刷新指定記錄盗扇,會清空整個表祷肯,但是也可以使用第三方緩存。
Hibernate 封裝性好疗隶,屏蔽了數(shù)據(jù)庫差異佑笋,自動生成SQL語句,應(yīng)對數(shù)據(jù)庫變化能力較弱斑鼻,SQL語句優(yōu)化困難蒋纬。
MyBatis僅實現(xiàn)了SQL語句和對象的映射,需要針對具體的數(shù)據(jù)庫寫SQL語句坚弱,應(yīng)對數(shù)據(jù)庫變化能力較強蜀备,SQL語句優(yōu)化較為方便。
1.14. SSH 和 SSM 對比總結(jié)
SSH 和 SSM 的技術(shù)框架的不同只需要比較Struts2和SpringMVC的不同荒叶,以及Hibernate和MyBatis的不同碾阁。
對于不同的功能,兩大技術(shù)陣營均有對應(yīng)的解決方案些楣。SSH將配置文件開發(fā)用到極致脂凶。SSM將注解開發(fā)用到極致。
企業(yè)進行技術(shù)選型愁茁,以低成本高回報作為技術(shù)選型的原則蚕钦,根據(jù)項目組的技術(shù)力量來進行選擇。
小弟水平有限埋市,只能總結(jié)到這里冠桃。更進一步的底層代碼級別的對比,才是本質(zhì)的區(qū)別道宅。用法上的區(qū)別只是表象而已食听,但是對于廣大開發(fā)者來說,誰的開發(fā)者用戶體驗好污茵,顯然更能贏得開發(fā)者的青睞樱报。