一。SpringMVC概述及原理1。SpringMVC是什么 SpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的MVC架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),〔Struts2〕(https:baike。baidu。comitemStruts22187934)(一般老项目使用)等。 SpringMVC已经成为目前最主流的MVC框架之一,从Spring3。0的发布,就已全面超越Struts2,成为最优秀的MVC框架。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。2。MVC和三层架构 MVC模式(ModelViewController)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。 控制器(Controller):Servlet,控制器主要处理用户的请求 视图(View):HTML,JSP,前端框架 模型(Model):逻辑业务程序(后台的功能程序),Service,Dao,JavaBeanJavaWEB发展史Model1 所有的业务逻辑交给jsp单独处理完成,一个web项目只存在DB层和JSP层,所有的东西都耦合在一起,对后期的维护和扩展极为不利。 Model2第一代 JSPModel2有所改进,把业务逻辑的内容放到了JavaBean中,而JSP页面负责显示以及请求调度的工作。虽然第二代比第一代好了些,但JSP还是把view和controller的业务耦合在一起。依然很不理想。 Model2第二代(三层架构) Model2第二代就是现在大力推广的和使用的mvc,将一个项目划分为三个模块,各司其事互不干扰,既解决了jsp所形成的耦合性,又增加了逻辑性、业务性以及复用性和维护性 表示层(web层):包含JSP,Servlet等web相关的内容 业务逻辑层(Service):处理业务,不允许出现servlet中的request、response。 数据层(DataAccessObject):也叫持久层,封装了对数据库的访问细节。 其中web层相当于mvc中的viewcontroller,Service层和dao层相当于mvc中的model。 3。SpringMVC在三层架构的位置 MVC模式:(ModelViewController):为了解决页面代码和后台代码的分离。 二。入门示例1。配置流程基于XML的配置1。1。搭建普通Maven项目 使用插件将项目转换为web项目 转换成功: 查看是否生成webapp目录和maven项目打包方式是否变为war 添加SpringMVC依赖dependencygroupIdorg。springframeworkgroupIdspringwebmvcartifactIdversion5。0。6。RELEASEversiondependency查看关系依赖图 1。2。在web。xml配置核心控制器lt;?xmlversion1。0encodingUTF8?webappxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlnshttp:java。sun。comxmlnsjavaeexsi:schemaLocationhttp:java。sun。comxmlnsjavaeehttp:java。sun。comxmlnsjavaeewebapp25。xsdversion2。5!spring核心(前端控制器)servletservletnamedispatcherservletnameservletclassorg。springframework。web。servlet。DispatcherServletservletclassservletservletmapping!只有。form后缀的请求才会进入springmvcservletnamedispatcherservletnameurlpattern。formurlpatternservletmappingwebapp1。3。创建一个Spring的配置文件lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlns:contexthttp:www。springframework。orgschemacontextxmlns:mvchttp:www。springframework。orgschemamvcxsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdhttp:www。springframework。orgschemacontexthttp:www。springframework。orgschemacontextspringcontext。xsdhttp:www。springframework。orgschemamvchttp:www。springframework。orgschemamvcspringmvc。xsdcontext:componentscanbasepackagecom。dfbz!开启springmvc注解支持mvc:annotationdrivenbeansmvc:annotationdriven说明 在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。 在springmvc早期版本中需要我们自己加载springmvc的三大组件(现在我们使用的版本5。0。6会自动加载这三大组件) 处理器映射器:RequestMappingHandlerMapping 处理器适配器:RequestMappingHandlerAdapter 处理器解析器:ExceptionHandlerExceptionResolver 在早期的版本中使用自动加载这三大组件,但是高版本的不需要来加载 同时它还提供了:数据绑定支持,NumberFormatannotation支持,DateTimeFormat支持,Valid支持,读写XML的支持(JAXB,读写JSON的支持(Jackson)。我们处理响应ajax请求时,就使用到了对json的支持(配置之后,在加入了jackson的core和mapper包之后,不写配置文件也能自动转换成json)springmvc配置文件说明 注意:默认的Spring配置文件放在WEBINF下,名为{servletname}servlet。xml {servletname}指的是,核心控制器配置的名字 如:dispatcherServletservlet。xml 当请求被springmvc处理时,springmvc会去默认路径下加载xxxxservlet。xml核心配置文件 但是我们在开发中一般都是把配置文件写在classes下的,我们可以在web。xml中设置springmvc配置文件的路径!spring核心(前端控制器)servletservletnamedispatcherservletnameservletclassorg。springframework。web。servlet。DispatcherServletservletclass!配置springmvc配置文件的位置initparamparamnamecontextConfigLocationparamnameparamvalueclasspath:dispatcherservlet。xmlparamvalueinitparamservletservletmapping!只有。form后缀的请求才会进入springmvcservletnamedispatcherservletnameurlpattern。formurlpatternservletmapping1。4。创建一个业务控制器packagecom。dfbz。controller;importorg。springframework。stereotype。Controller;importorg。springframework。web。bind。annotation。RequestMapping;importorg。springframework。web。servlet。ModelAndView;ControllerpublicclassHelloController{代表此方法的访问路径为hello。formRequestMapping(hello。form)publicModelAndViewhello(){ModelAndViewmavnewModelAndView();mav。addObject(msg,小标);mav。setViewName(hello。jsp);returnmav;}}1。5。创建一个返回的视图页面lt;pagecontentTypetexthtml;charsetUTF8languagejavahtmlheadtitleTitletitleheadbody{msg},欢迎您!bodyhtml1。7。SpringMVC的工作流程 1、客户端发送请求给前端控制器(DispatcherServlet) 2、dispatcherServlet接收到请求调用HandlerMapping处理器映射器 3、处理器映射器根据请求的url找对应的处理器,生成处理器对象(handler)返回 4、dispatchServlet将handler传入处理器适配器执行 5、处理器适配器执行handler 6、执行完成最终封装一个ModelAndView 7、将ModelAndView返回给前端控制器 8、前端控制器将请求的路径交给视图解析器进行解析 9、最终封装一个View对象给dispatcherServlet,此View对象封装了响应参数 10、JSP页面渲染数据 11、响应客户端1。8SpringMVC源码分析 我们知道SpringMVC实质上是对servlet进行封装,让我们的开发更加简便1。准备工作 我们知道springmvc在工作开始之前会加载默认的处理器映射器、处理器适配器、处理器解析器等 可以在springwebmvc5。0。6。RELEASE。jar源码包下查看DispatcherServlet。properties文件看有哪些处理器是springmvc默认加载的 2。查看DispatcherServlet的继承体系: 我们发现DispatcherServlet最终还是继承与HttpServlet,那么我们就直接找service方法吧! 经打断点发现,最终会走向DispacherServlet的doDispacher方法! 此时请求进入DispatcherServlet,按照我们画图分析的结果应该是把请求交给处理器映射器HandlerMapping最终返回一个Handler3。查看HandlerMapping接口: 4。寻找HandlerMapping实现类: 接下来进入处理器适配器HandlerAdapter执行handler最终返回一个ModelAndView5。查看HandlerAdapter接口: 6。查看HandlerAdapter实现类: 然后请求交给视图解析器进行解析最终返回一个View对象7。查看View接口: 8。查看View实现类: 9。查看View信息: 1。9。核心控制器 SpringMVC自带了拦截器请求的核心控制器。所以就可以在请求过来的时候,直接启动Spring框架 默认情况下,Spring容器是在核心控制器DispatcherServlet获得请求后才启动的。 能不能网站启动的时候,Spring容器就立刻启动。servletservletnamedispatcherservletnameservletclassorg。springframework。web。servlet。DispatcherServletservletclassloadonstartup1loadonstartup!启动服务器的时候,Servlet就创建实例servlet三。Controller方法返回值返回值返回String 可以返回视图字符串,解析器会自动解析RequestMapping(demo31)publicStringdemo31(){returnsuccess。jsp;直接返回字符串}返回ModelAndView ModelAndView是SpringMVC帮我们提供的,即模型和视图RequestMapping(demo32)publicModelAndViewdemo32(){返回ModelAndViewModelAndViewmavnewModelAndView();mav。setViewName(success。jsp);mav。addObject(username,东方标准);returnmav;}返回void 一般用于使用原生的Servlet对象或者ajax请求RequestMapping(demo33)返回void(一般用于ajax)publicvoiddemo32(HttpServletResponseresponse)throwsIOException{response。setContentType(texthtml;charsetutf8);response。getWriter()。write(东方标准);}转发和重定向 SpringMVC提供了一个String类型返回值之后会自动解析forward、redirect等特殊字符串 视图解析器配置的前缀和后缀解析不支持forward、redirect :forward:代表转发request。getRequestDispatcher(url)。forward(request,response) :redirect:代表重定向response。sendRedirect(url)RequestMapping(demo34)publicStringdemo34(Modelmodel){转发System。out。println(执行啦!);model。addAttribute(username,东方标准);returnforward:success。jsp;}RequestMapping(demo35)publicStringdemo35(HttpSessionsession){重定向session。setAttribute(password,admin);System。out。println(执行啦!);returnredirect:success。jsp;}四。映射路径RequestMapping1。探究RequestMapping 注解式处理器映射器,对类中标记了ResquestMapping的方法进行映射。根据ResquestMapping定义的url匹配ResquestMapping标记的方法,匹配成功返回HandlerMethod对象给前端控制器。HandlerMethod对象中封装url对应的方法Method。Target({ElementType。METHOD,ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedMappingpublicinterfaceRequestMapping{}命名空间 按照我们传统的url写法不能很好的规定请求路径,即不能按照业务来区分请求 例如现在user需要定义一个findById,goods也需要定义一个findById,此时就会有冲突,针对这种现象我们可以在类上定义一个命名空间,即在类上使用RequestMapping注解,类似于一级目录,以后访问此类下的任意资源都需要加上此目录类上 请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以开头。它出现的目的是为了使我们的URL可以按照模块化管理: 例如: user模块: userregister userupdate userfindById goods模块: goodsadd goodsupdate goodsfindById 映射路径的有三种:标准的映射路径,带通配符的映射路径,带路径变量的映射路径方法上 请求URL的第二级访问目录。属性:value:用于指定请求的URL。它和path属性的作用是一样的。method:用于指定请求的方式。params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样。 params{userId},表示请求参数必须有userId,区分大小写 params{userId!20},表示请求参数中id不能是20。可以不携带userId参数,区分大小写 headers:用于指定限制请求消息头的条件。packagecom。dfbz。controller;importorg。springframework。stereotype。Controller;importorg。springframework。web。bind。annotation。RequestMapping;importorg。springframework。web。bind。annotation。RequestMethod;importorg。springframework。web。servlet。ModelAndView;ControllerpublicclassHelloController{publicHelloController(){System。out。println(Hello创建了);}代表此方法的访问路径为hello。form,如果携带userId参数那么参数必须不能等于1并且提交方式一定要为POSTRequestMapping(valuehello。form,paramsuserId!1,methodRequestMethod。POST)publicModelAndViewhello(){ModelAndViewmavnewModelAndView();mav。addObject(msg,小标);mav。setViewName(hello。jsp);returnmav;}} 小结:1。参数必须包括:params{username,password} 2。参数不能包括:params{!userid} 3参数值必须是指定的值:params{usernamezhangsan}) 4。参数值必须不是指定的值:params{userid!123})2。3。RESTFUL 所谓的路径变量,就是将参数放在路径里面,而不是放在?的后面 如:原get请求方法login。mvc?username’zhangsan’pwd’123456’ 路径变量写法: zhangsan123456login。form2。3。1什么是RESTFUL REST(英文:RepresentationalStateTransfer,简称REST)RESTful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。2。3。2RESTFUL示例: 请求方式有几种?7种 jsp、html,只支持get、post。基于restful风格的url:添加 http:localhost:8080SpringMVC01user 提交方式:post修改 http:localhost:8080SpringMVC01user 提交方式:put删除 http:localhost:8080SpringMVC01user101 提交方式:delete查询 http:localhost:8080SpringMVC01user102 提交方式:get五。数据绑定1。数据绑定是什么 SpringMVC里面,所谓的数据绑定就是将请求带过来的表单数据绑定到执行方法的参数变量中,或将服务器数据绑定到内置对象,传递到页面2。自动绑定的数据类型2。1自动绑定数据类型基本数据类型:基本数据类型String包装类包装数据类型(POJO):包装实体类数组和集合类型:List、Map、Set、数组等数据类型2。2内置绑定数据自动绑定:ServletAPI: HttpServletRequest HttpServletResponse HttpSessionSpringMVC内置对象 Model ModelMap ModelAndView Model和ModelMap默认都是存储了Request请求作用域的数据的对象 这个两个对象的作用是一样。就将数据返回到页面。测试页面:lt;pagecontentTypetexthtml;charsetUTF8languagejava!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleTitletitleheadbodyh3测试ServletAPI绑定h3ph3测试参数类型绑定h3p属性参数绑定h3测试对象参数绑定h3h3测试数组类型绑定h3h3测试Pojo对象数据绑定h3hrbodyhtml测试Controller:packagecom。dfbz。controller;importcom。dfbz。entity。Pojo;importcom。dfbz。entity。User;importorg。springframework。stereotype。Controller;importorg。springframework。web。bind。annotation。RequestMapping;importjavax。servlet。http。HttpServletRequest;importjavax。servlet。http。HttpServletResponse;importjavax。servlet。http。HttpSession;importjava。util。Arrays;importjava。util。List;ControllerpublicclassDemoController{RequestMapping(demo1)自定义属性绑定publicStringdemo1(HttpServletRequestrequest,HttpServletResponseresponse,HttpSessionsession){request。setAttribute(username,zhangsan);returnsuccess。jsp;}RequestMapping(demo2)属性参数绑定publicStringdemo2(Stringusername){System。out。println(username);returnsuccess。jsp;}RequestMapping(demo3)对象参数绑定publicStringdemo3(Useruser){System。out。println(user);returnsuccess。jsp;}RequestMapping(demo4)数组参数绑定publicStringdemo4(String〔〕ids){System。out。println(Arrays。toString(ids));returnsuccess。jsp;}RequestMapping(demo5)Pojo对象参数绑定publicStringdemo5(Pojopojo){System。out。println(pojo);returnsuccess。jsp;}}3。Post提交方式乱码解决 测试GETPOST提交中文数据页面:h3测试Post乱码h3controller:RequestMapping(demo7。form)Pojo对象参数绑定publicStringdemo7(Useruser){System。out。println(user);returnsuccess。jsp;} 发现POST乱码 解决post提交乱码我们可以配置spring提供的过滤器!解决Post提交中文乱码filterfilternamecharacterEncodingfilternamefilterclassorg。springframework。web。filter。CharacterEncodingFilterfilterclassinitparam!要使用的字符集,一般我们使用UTF8(保险起见UTF8最好)paramnameencodingparamnameparamvalueUTF8paramvalueinitparamfilterfiltermappingfilternamecharacterEncodingfiltername!代表所有请求都经过编码处理urlpatternurlpatternfiltermapping4。SpringMVC常用注解 SpringMVC有一些数据是不能自动绑定,需要我们使用它提供的注解强制绑定。 遇到需要强制绑定的几种情况 a。默认参数绑定的是表单数据,如果数据不是来自表单(如restful),那么必须需要强制绑定 b。数据是来自表单的,但是参数名不匹配,那么也需要强制绑定 c。数据是来自表单的,但是需要将数据绑定在Map对象里面,需要强制绑定4。1。PathVariable:绑定路径参数 这个注解是绑定路径参数的。http:localhost:808020zhangsandemo9。formparamidparamusernamereturnRequestMapping({id}{username}demo9)PathVariable绑定restful请求publicStringdemo9(PathVariable(id)Integerid,PathVariable(username)Stringusername){System。out。println(id);System。out。println(username);returnsuccess。jsp;}测试restful风个绑定4。2。通过RequestParam绑定表单数据 接收的参数的变量名与表单的name属性不一样http:localhost:8080demo8。form?namezhangsanparamusernamereturnRequestMapping(demo8)RequestParam强制数据类型绑定publicStringdemo8(RequestParam(name)Stringusername){System。out。println(username);returnsuccess。jsp;}测试强制类型绑定4。3。CookieValue获得Cookie值的注解获得JSP页面,JSESSIOINID这个Cookie值paramcookevalueRequestMapping(demo10)publicvoidgetcookie(CookieValue(valueJSESSIONID)Stringcookevalue){输出CookieSystem。out。println(cookevalue);}测试CookieValue注解4。4。RequestHeader获得指定请求头的值RequestMapping(demo11)获取请求头host的值封装到value中publicvoiddemo10(RequestHeader(host)Stringvalue){System。out。println(value);}测试RequestHeader注解4。5SessionAttributes注解 把Model和ModelMap中的指定的key或者指定的属性的值也存入一份进session域packagecom。dfbz。controller;importcom。dfbz。entity。User;importorg。springframework。stereotype。Controller;importorg。springframework。ui。Model;importorg。springframework。ui。ModelMap;importorg。springframework。web。bind。annotation。RequestMapping;importorg。springframework。web。bind。annotation。SessionAttribute;importorg。springframework。web。bind。annotation。SessionAttributes;importjavax。servlet。http。HttpServletRequest;Controllernames:代表此类中modelmodelMap的username属性将会添加到一份进入sessiontypes:此类中指定的类型会添加一份到session中SessionAttributes(names{username},types{User。class,String。class,Integer。class})publicclassDemo2Controller{RequestMapping(demo12。form)publicStringdemo12(ModelmodelMap){modelMap。addAttribute(username,zhangsan);modelMap。addAttribute(password,admin);modelMap。addAttribute(age,20);UserusernewUser();user。setUsername(xiaodong);modelMap。addAttribute(user,user);returnsuccess。jsp;}}测试SessionAttribute注解SessionAttribute注解: 从session中获取一个值封装到参数中SessionAttribute:从session中获取一个值paramusernameparamuserreturnRequestMapping(valuedemo6)publicStringdemo5(SessionAttribute(password)Stringusername,SessionAttribute(user)Useruser){System。out。println(username);returnsuccess。jsp;}5。格式化参数类型 SpringMVC之所以能够帮我们实现自动数据类型转换是因为SpringMVC提供了非常多的转换器(Converter) 例如: 发现他们都实现Converter接口,如果我们需要之定义转换器必须实现Converter接口案例: 实现日期的转换实体类:publicclassUser{privateIntegerid;privateStringusername;privateStringpassword;privateStringaddress;privateDatebirthday;}表单:lt;pagecontentTypetexthtml;charsetUTF8languagejavahtmlheadtitleTitletitleheadbodybodyhtmlcontroller:RequestMapping(testConverter。form)publicStringtestConverter(Useruser){System。out。println(user);returnsuccess。jsp;}转换器类:packagecom。dfbz。converter;importorg。springframework。core。convert。converter。Converter;importjava。text。ParseException;importjava。text。SimpleDateFormat;importjava。util。Date;Converter传入进来的类型,转换之后的类型publicclassMyConverterimplementsConverterString,Date{OverridepublicDateconvert(Stringstr){SimpleDateFormatsdfnewSimpleDateFormat(yyyyMMdd);try{Datedatesdf。parse(str);returndate;}catch(ParseExceptione){e。printStackTrace();}returnnull;}}dispatcherservlet。xml配置!在SpringMVC配置的转换器中添加我们自定义的转换器beanidmyConvertersclassorg。springframework。format。support。FormattingConversionServiceFactoryBean!配置多个转换器propertynameconvertersset!配置转换器(自定义转化器类)beanclasscom。dfbz。converter。MyConverterbeansetpropertybean!开启springmvc注解支持并重新指定转换器mvc:annotationdrivenconversionservicemyConverters六。Controller的生命周期 Spring框架默认创建的对象是单例。所以业务控制器是一个单例对象。 SpringMVC提供了,request,session,globalsession三个生命周期request:每次新的请求,创建一个新的实例。session:每次会话创建一个新的实例。就是同一个浏览器,就使用同一个实例globalsession:基于集群的session 每个Session创建一个实例packagecom。dfbz。controller;importorg。springframework。context。annotation。Scope;importorg。springframework。stereotype。Controller;importorg。springframework。web。bind。annotation。RequestMapping;ControllerScope(request)代表每次请求都会创建一个新的Demo4Controller对象Scope(session)代表每次创建session就会创建一个Demo4Controller对象publicclassDemo4Controller{publicDemo4Controller(){System。out。println(Demo4Controller创建啦!);}RequestMapping(demo41)publicStringdemo41(){returnsuccess。jsp;}} at。support。FormattingConversionServiceFactoryBean 六。Controller的生命周期 Spring框架默认创建的对象是单例。所以业务控制器是一个单例对象。 SpringMVC提供了,request,session,globalsession三个生命周期request:每次新的请求,创建一个新的实例。session:每次会话创建一个新的实例。就是同一个浏览器,就使用同一个实例globalsession:基于集群的session 每个Session创建一个实例packagecom。dfbz。controller;importorg。springframework。context。annotation。Scope;importorg。springframework。stereotype。Controller;importorg。springframework。web。bind。annotation。RequestMapping;ControllerScope(request)代表每次请求都会创建一个新的Demo4Controller对象Scope(session)代表每次创建session就会创建一个Demo4Controller对象publicclassDemo4Controller{publicDemo4Controller(){System。out。println(Demo4Controller创建啦!);}RequestMapping(demo41)publicStringdemo41(){returnsuccess。jsp;}}