SpringMVC第一天
主要内容
1.MVC架构模式
2.SpringMVC的概念引入
3.SpringMVC框架搭建
4.SpringMVC框架完善
5.SrpingMVC参数注入
6.SpringMVC中常见注解
一.MVC结构模式
1.MVC是一种架构模式
1.1.M:Model
模型层:负责跟数据库进行交互的操作.pojo,service,dao(mapper)
1.2V:View
视图层:进行数据的表现.负责跟客户进行交互.jsp,html,css,js,...
1.3C:Controller
控制器:桥梁,负责M和V之间的交互
2.MVC发展阶段
2.1jsp模型阶段
在早期JavaWeb的开发中,统一把显示层,控制层,数据层的操作全部交给JSP或者JavaBean来进行处理,我们称之为Model1:
2.2Mdel2
jsp+Servlet.jsp负责页面显示,servlet负责控制.MVC,会出现很多重复性的工作(Servlet层):参数接收,作用域传参,页面跳转
2.3MVC框架
为解决持久层中一直为处理好的数据库事务的编程,又为了迎合NoSQL的强势崛起,SpringMVC给出了方案,更加灵活,更加简单的完成上述操作.常见的MVC框架:SpringMVC,Struts2,JFinal(中国人写的)
二.SpringMVC引入
1.遇到的问题
在学习了Spring之后,基于MVC设计模式的项目,我们可以使用Mybatis将数据库替换,使用Spring将Controller层和Service层,以及Service层和数据层之间进行解耦.但是基于MVC的模式中,在Controller层中的Servlet为请求的代码入口.tomcat服务器在接受到请求后,会根据请求地址自定调用对应的servlet的service方法完成请求处理,但是此流程存在如下问题:
a.每个功能都要声明对应的Servlet,麻烦
b.在Servlet中获取请求数据比较麻烦
c.响应的方式的代码其实只想声明对应的响应数据
2.处理方案
项目只声明了Servlet,该Servlet作为项目请求的公共入口.并且在该Servlet必须声明代码,此代码根据请求地址调用对应的逻辑代码处理请求.如果将逻辑方法全部声明在Servlet中造成代码的体系结构不清晰,将逻辑方法单独声明到逻辑类中(Controller类).然后Servlet中根据请求动态的调用对应的逻辑类中的逻辑方法处理请求即可
a.如何在Servlet中获取逻辑类对象呢?
使用Spring容器的子容器,在子容器中存储所有的Controller的实例化对象,然后Servlet一次性从子容器中获取所有的对象即可.在init方法中实现即可
b.如何在Servlet中根据请求动态调用对象的逻辑方法呢
使用反射+注解(BaseServlet)
c.如何处理接受参数,提升效率呢?
3.SpringMVC的概念
SpringMVC是一种基于java实现MVC设计模型的请求驱动类型的轻量级W饿b框架,属于SpringFramework的后续产品,已经融合在SpringWebFlow里面.Spring框架提供了构建Web应用程序的全功能MVC模块.使用Spring可插入的MVC架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2等.SpringMVC已经成为目前最主流的MVC框架之一,并且随着Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架.它通过一套注解,让一个简单的java类成为处理请求的控制器,而无需事先任何接口.同时它还支持RESTful编程风格的请求
本质:SpringMVC就是将Servlet进行了封装,提供一个公共的Servlet.该Servlet可以根据请求动态的调用对应的逻辑方法完成请求处理
4.SpringMVC的优点
(1)清晰的角色划分:
前端控制器(DispatcherServlet)
请求到处理器映射(HandlerMapping)
处理器适配器(HandlerAdapter)
视图解析器(ViewResolver)
处理器或页面控制器(Controller)
验证器(Validator)
命令对象(Command请求参数绑定到的对象就叫命令对象)
表单对象(Form Object提供黑表单展示和提交到的对象就叫表单对象)
2.分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要
3.由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象
4.和Spring其他框架无缝集成,是其他Web框架不具备的
5.功能强大的数据验证,格式化,绑定机制
6.利用Spring提供Mock对象能够非常简单的进行Web层单元测试
7.本地化,主题的解析的支持,使我们更容易进行国际化和主题的切换
8.强大的JSP标签库,使JSP编写更容易
......还有比如RESTful风格的支持,简单的文件上传,约定大于配置的契约式编程支持,基于注解的零配置支持等等
5.理解示意图
6.springmvc和struts2区别
共同点:
1.它们都是表现层框架,都是基于MVC模型编写的
2.它们的底层都离不开原始Servlet
3.它们处理请求的机制都是一个核心控制器
区别:
1.springMVC入口是servlet,而Sturts2是Filter;
2.springMVC是基于方法设计的,而Sturts2是基于类,Struts2每次执行都会创建一个动作类.所以springMVC会稍微比Struts2快些
3.springMVC使用更加简洁,同时还支持JSP303,处理ajax的请求更加方便(JSP303是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了)
4.Struts2的OGNL表达式使页面的开发效率相比springMVC更高些,但执行效率并没有比JSTL提升,尤其是Struts2的表单标签,远没有html执行效率高
三.SpringMVC的框架搭建
1.创建web项目并导入SpringMVC的jar
2.在src下创建MVC的包结构
3.在Controller包下创建控制器类并声明单元方法
4.在src下创建并配置springmvc.xml文件
5.配置web\.xml文件,配置SpringMVC的Servlet
6.浏览器效果
7.执行流程图
四.SpringMVC框架的完善及详解
1.手动指定SpringMVC的位置
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
2.静态资源放行
2.1在web.xml中更改拦截路径
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.Action</url-pattern>
</servlet-mapping>
2.2SpringMVC提供的静态资源放行标签
<!--静态资源放行mapping:指的是网络中URL地址location:放行本地的位置的资源-->
<mvc:resource mapping="/imgs/**" location="/imgs/"></mvc:resources>
3.SpringMVC执行流程详解
3.1DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性
3.2HandlerMapping:处理器映射器
HandlerMapping:负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,注解方式等
3.3Handler:处理器
它就是我们开发中要编写的具体业务控制器.由DispatcherServlet把用户请求转发到Handler.由Handler对具体的用户请求进行处理
3.4HandlerAdapter;处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
3.5View Resolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名,及具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户
3.6View:视图
SpringMVC框架提供了很多的View视图类型的支持,包括:jstlView,freemarkerView,pdfView等.我们最常用的视图就是jsp.一般情况下需要通过页面标签或页面模板技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
3.7<mvc:annotation-driven>说明
在SpringMVC的各个组件中,处理器映射器,处理器适配器,视图解析器称为SpringMVC的三大组件
使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在SpringMVC.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置
3.8流程理解示意图
五.SpringMVC中参数注入
1.servlet中如何接受参数
a.接受方式request.getParameter(name)
b.getParameter方法用于获取单个值,返回类型是String
c.冗余代码较多,使用麻烦,类型需要自己转换
2.使用SpringMVC进行参数接收
a.简化参数接收形式(不需要调用任何方法,需要什么参数,就在控制器方法中提供什么参数)
b.参数类型不需要自己转换了.日期时间得注意,需要使用@DateTimeFormat注解声明日期转换时遵循的格式,否则抛出400异常
c.还可以自动将参数封装为对象
3.SpringMVC接受参数实现方式
3.1紧耦方式
DispatcherServlet中的service方法直接将此次请求的request对象传递给调用的单元方法即可.同时在单元方法上声明形参HttpServletRequest来接收request实参即可
3.2解耦方式
DispatcherServlet在其service方法中将请求数据根据需求从request对象中获取出来后,将数据直接传递给对应的单元方法使用.同时在单元方法上直接声明对应的形参接受请求数据即可
4.紧耦方式(request)在单元方法中获取请求数据
4.1在控制器类中声明请求处理单元方法
在单元方法上声明形参,形参类型为HttpServletRequest,接收DispatcherServlet传递的封装了此次请求的请求数据的request对象
4.2在单元方法中使用request.getParameter("键名")获取请求数据
5.解耦方式获取请求数据
5.1形参属性名即为请求数据的键名
问题:
让DispatcherServlet将请求数据获取后传递给单元方法,但是请求数据的获取需要数据的键,名,而DispatcherServlet不是我们自己声明的无法修改其底层代码,怎么将请求数据的键名告诉给DispatcherServlet呢?
解决:
在单元方法上声明形参来接收请求数据时,形参名必须和请求数据的键名一致,DispatcherServlet会将调用单元方法的形参名作为请求数据的键名获取请求数据,然后传递给单元方法
实现:
5.2获取同键不同值的请求数据
问题:
目前我们在单元方法上可以使用形参或者实体类来接收请求数据,但是有某些请求中,请求数据是同键不同值的.比如,在页面中的多项选择的请求数据,爱好,fav=1&fav=2&fav=3像这样的请求数据,如何获取呢?
解决:
我们自己使用Request对象获取同键不同值的数据,使req.ParameterValues("键名"),返回值是String[] 数组.在单元方法上声明形参,类型为String[]数组类型,要求形参名和请求数据的键名一致即可
实现:
解耦合方式获取同键不同值的数据
使用Spring类型的数组来接收,形参名为请求数据的键名
@RequestMapping("argKeyNotValue")
public String demoArgKeyNotValue(String uname,Integer age,String[] fav){
//处理请求数据
System.out.println("MyController.demoArgKeyNotValue:获取同键不同值的请求数据:"+uname+":"+age+":"+fav[0]);
//响应结果
return "aa";
}
5.3使用实体类对象获取请求数据
问题:
在学习了使用SpringMVC后,我们可以在单元方法上声明形参直接获取请求数据只要形参名和请求数据的键名一致即可.但是如果我们的请求数据过多,总不能咱们声明N个形参来接收请求数据吧?而且按照我们以往的开发经验,请求数据过多我们会将请求封装到实体类对象中进行使用,保证数据的完整性.那么,在SpringMVC中一旦请求数据过多,如何在单元方法上获取请求数据呢?
解决:
我们希望在单元方法中直接获取一个封装好请求数据的实体类对象使用.也就说我们希望DispatcherServlet可以将请求数据封装到实体类对象中,然后将实体类对象作为实参传递给单元方法使用.在单元方法上声明对应的实体类的形参类型,来接收DispatcherServlet传递的封装了请求数据的实体类对象,以及告诉DispatcherServlet使用哪个实体类来封装请求数据.而且,要求实体类的属性名必须和请求数据的键名一致,DispatcherServlet会按照该方式将请求数据赋值黑实体类的属性
实现:
//解耦方式使用实体类对象接受请求
要求
实体类的属性和请求数据的键名一致,必须提供get/set方法
注意:
实体类的属性类型使用包装类,避免请求中没有对应的数据时出现类型转换异常
@RequestMapping("argObject")
public String demoArgObject(User user){
//处理请求
System.out.println("MyController.demoArgObject:请求数据使用实体类型对象接收:"+user);
//响应结果
return "aa";
}
5.4POJ类型作为参数
问题:
如果我们接收过来的数据需要保存到实体中的另外一个依赖POJO类中,我们需要如何处理呢
解决:
我们可以通过更改提交表单中的name属性来操作,name的属性是pojo.属性名的方式接收,这样接收的数据就会保存到对应的属性中
实现:
jsp代码:
<form action="account/saveAccount" method="post">
账户名称:<input type="text" name="name"><br/>
账户金额:<input type="text" name="money"><br/>
账户省份:<input type="text" name="address.proviceName"><br/>
账户城市:<input type="text" name="address.cityName"><br/>
<input type="submit" value="保存">
</form>
控制器代码:
@RequestMapping("/saveAccount")
public String saveAcccount(Account account){
System.out.println("金额:"+account);
return "sxt";
}
5.5POJO类中包含集合类型的参数
问题:
如果我们接收过来的数据需要保存到实体中包含的集合(比如List,Map集合),这种情况下我们需要如何处理呢?
解决:
我们可以改变表单中提交的name属性方式来进行操作,指定表单合适的name属性,后台的对象通过name属性来进行值的接受即可,如果是保存到List集合,书写方式accounts[下标].name,如果是Map集合,接受方式如下:accountMap['one'].money
实现:
jsp代码:
<form action="" method="post">
用户名称:<input type="text" name="username"><br/>
用户密码:<input type="password" name="password"><br/>
用户年龄:<input type="text" name="age"><br/>
账户1名称:<input type="text" name="accounts[0].name"><br/>
账户2金额:<input type="text" name="accounts[1].money"><br/>
账户3名称:<input type="text" name="accountMap['one'].name"><br/>
账户4金额:<input type="text" name="accountMap['two'].money"><br/>
<input type="submit" value="保存">
</form>
控制器代码:
@RequestMapping("/updateAccount")
public String updateAccount(User user){
System.out.println("用户数据.."+users);
return "success";
}
5.6特殊日期类型数据接受
问题:
我们之前接受数据格式都是基本的数据类型,如果我们进行Date类型数据的接受处理,应该如何进行呢?如果我们接收的日期的格式类型没有进行统一的指定,又该如何处理
解决:
我们对于日期格式的处理有两种方法:
第一种就是使用SpringMVC提供的对日期进行格式化的注解标签@DateTimeFormat比较简单
第二种即使自定义类型转换器,比较的麻烦但是不仅仅对Date类型可以处理,对于其他类型的数据可以处理
实现:
方式一:注解方式
@RequestMapping("demo4")
public String demo4(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")Date birth){
System.out.println(birth+"--"+hobby[0]);
return "/success.jsp";
}
方式二:自定义类型转换器
第一步:定义一个类,实现Converter接口,该接口有两个泛型
public class StringToDateC implements Converter<String,Date>{
@Override
public Date convert(String source){
DateFormat format = null;
try{
if(StringUtils.isEmpty(source)){
throw new NullPointerException("日期输入错误");
}
format=newSimpleDateFormat("yyyy-MM-dd");
Date date = format.parse(source);
return date;
}catch(Exception e){
throw new RuntimeException("输入日期有误");
}
}
}
第二步:在spring配置文件中配置类型转换器
<!--配置类型转换工厂-->
<bean id="converterService" class="org.springframework.context.ConversionServiceFactoryBean">
<!--给工厂注入一个新的类型转换器-->
<property name="converters">
<array>
<!--配置自定义类型转换器-->
<bean class="com.sxt.controller.converter.StringToDateConverter"></bean>
</array>
</property>
</bean>
第三步:在annotation-driven标签中引用配置的类型转换服务
<!--引用自定义类型转换器-->
<mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
6.字符编码过滤器
问题:
我们使用SpringMVC对于参数进行接收的时候,非常的方便,但是,现在对于接受数据的中文乱码我们应该如何的处理呢?
解决:
a.Get方式
在server.xml中指定对应的服务器编码
URIEncoding="utf-8"
b.POST方式
在过滤器中增加request.setCharacterEncoding("utf-8");
实现:
a.GET方式:
b.POST方式
<!--Spring中提供的字符串编码过滤器-->
<filter>
<filter-name>encFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encFilter</filter-name>
<url-pattern>/*</url-parrent>
</filter-mapping>
六.SpringMVC中常见注解
1.@RequestMapping
作用:
用于建立请求URL和处理请求方法之间的对应关系
出现位置:
类上:请求URL的第一级访问目录.此处不写的话,就相当于应用的根目录.写的话需要以/开头,它出现的目的是为了使我们的URL可以按照模块化管理
方法上:请求URL的第二级访问目录
属性:
value:用于指定请求的URL.它和path属性的作用是一样的
method:用于指定请求的方式
params:用于指定限制请求参数的条件.它支持简单的表达式.要求请求参数的key和value必须和配置的一模一样
例如:params={"accountName"},表示请求参数必须有accountName
Headers:用于指定限制请求消息头的条件
实现:
@RequestMapping("/account")
public class AccountController{
@RequestMapping(value="/saveAccount",method=RequestMethod.POST)
public String sveAccount(){
System.out.println("保存了账户");
return "success";
}
}
2.@RequestParam
作用:
把请求中指定名称的参数给控制器中的形参赋值
属性:
value:请求参数中的名称
required:请求参数中是否必须提供此参数.默认值:true.表示必须提供,如果不提供将报错
实现:
@RequestMapping("/RequestParam")
public String useRequestParam(@RequestParam("name")String uname,
@RequestParam(value="age",required=false)Integer age){
System.out.println(username+","+age);
return "sxt";
}
3.@RequestBody
作用:
用于获取请求体内容.直接使用的是key=value&key=value..结构的数据,get请求方式不适用
属性:
required:是否必须有请求体.默认值是:true.当取值为true时,get请求方式会报错.如果取值为false,get请求得到是null
实现:
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false)String body){
System.out.println(body);
return "sxt";
}
4.@PathVariable
Restful的简介:
REST(Representation State Transfer,简称REST)restful是一种软件架构风格,设计风格,而不是标准.只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
restful的优点
它结构清晰,符合标准,易于理解,扩展方便,所以正得到越来越多网站的采用
作用:
用于绑定url中的占位符.例如:请求url中/delete/{id},这个{id}就是url占位符.url支持占位符是spring3.0之后加入的/是springmvc支持rest风格URL的一个重要标志
属性:
value:用于指定url中占位符名称
required:是否必须提供占位符
实现:
/**
SpringMVC处理restful格式请求
1.
在@RequestMapping注解中可以使用{字母}来声明单元方法的拦截范围
比如:
@RequestMapping("aa/{uname}/{age}")
表示请求地址要请求的单元方法以aa开头,后面为两位任意
2.
我们可以在单元方法的形参声明上使用注解@PathVariable来告诉DispatcherServlet将请求地址中的数据截取后作为实参传递给单元方法使用,默认按照地址占位中的参数名和形参名一致的规则赋值.如果不一致可以在@PathVariable中指明赋值@PathVariable("un")
3.
注意:
说白了SpringMVC的restful格式的支持就是单元方法模糊拦截+注解声明来实现
*/
@RequestMapping("aa/{un}/{age}")
public String demoRestful(@PathVariable("uu") String uname,@PathVariable Integer age){
//处理请求
System.out.println("MyController.demoRestful:SpringMVC处理restful格式请求:"+uname+":"+age);
//响应结果
return "aa";
}
5.@RequestHeader
作用:
用于获取请求消息头
属性:
value:提供消息头名称
required:是否必须有此消息头
实现
@RequestMapping("/useRequestHeader")
public String useRequestHeader(@RequestHeader(value="Accept",required=false)String requestHeader){
System.out.println(requestHeader);
return "sxt";
}
6.@CookieValue
作用:
用于把指定cookie名称的值传入控制器方法参数
属性:
value:指定cookie的名称
required:是否必须有此cookie
实现
@RequestMapping("/CookieValue")
punlic String CookieValue(@CookieValue(value="JSESSIONID",required=false)String cookieValue){
System.out.println(cookieValue);
return "sxt";
}