SpringMVC介绍
springmvc不是一个单独的框架,是spring框架的其中一个模块。
原始mvc处理过程
Model指和数据和行为,包括dao层和service层。
View是页面。Controller是控制器负责与前端交互,controller层。
最经典的mvc就是javabean+jsp+servlet,这种模式是tomcat容器里有许多个servlet,还有一个wen.xml,当前端有请求过来,就会根据web.xml中的映射,找到请求路径对应的servlet,servlet执行完自己的逻辑把结果响应给前端。
SpringMVC处理过程
springmvc采取的模式是,tomcat容器中只有一个servlet,就是图中的FrontController,是一个DispatcherServlet类对象,所有请求都送到DispatcherServlet,然后它再根据路径找不同的Controller处理,这样一来Controller就可以自己定义,不需要继承HttpServlet,实现了解藕。
DispatcherServlet从Controller那里拿到处理的结果和view的名称,再去找到对应的View,跟数据融合返回一个完整的页面视图给前端。
PS:转发和重定向
转发是客户端请求aaa路径,服务端收到请求后,可能在服务端内部进行多次跳转,跳转了多个处理器终于处理完了,把结果响应给客户端。表现出来就是路径不变。
重定向是客户端请求aaa路径,服务端收到请求后,返回了bbb的请求
配置文件+注解
准备
新建EE项目,pom文件导包:
导了这两个就已经包括了spring-context了。
web.xml文件
配置前端控制器:唯一的servlet:DispatcherServlet:
servlet-name:标识(随便起),
servlet-class:对应的Servlet类,
url-pattern:这个servlet要接收什么请求。
设置初始化参数,指定默认的springmvc配置文件路径:
spring.xml文件
指定注解的扫描路径:
controller
视图
运行
如果报错:实例化Servlet类[....DispatcherServlet]异常,是找不到这个类:
配置试图解析器,指定前缀后缀:
springmvc.xml:
视图:
注意
1、@RequestMapping用来匹配当前方法要处理的请求,其中/可写可不写,一般推荐写。
2、可以不手动在web.xml中指定springmvc.xml的路径,直接放到webapp/WEB-INF/目录下,命名必须为(DispatcherServlet的servlet-name)-servlet.xml。
3、springmvc处理过程:
浏览器发送请求http://localhost:8080/msbssm/hello,交给tomcat容器,
web.xml中配置了DispatcherServlet类,此时会由当前的DispatcherServlet来接受请求,
接受请求之后扫描注解@Controller,到controller中寻找@RequestMapping匹配的方法,
执行方法逻辑,返回一个前端页面名称,由视图处理器根据名称映射到对应的jsp页面路径,
DispatcherServlet拿到路径之后把文件返回给浏览器。
4、/和/*的区别:
都是用来匹配所有请求,/放行jsp文件,/*啥也不放行,通通拦截。
也就是说,用/的话除了jsp文件都经过DispatcherServlet,都要去匹配RequestMapping,用/的话全部请求都经过DispatcherServlet,都要去匹配RequestMapping。
为什么:因为项目web.xml要继承tomcat的web.xml,后者先配置了/匹配DefaultServlet,又配置了jsp文件匹配JspServlet,前者配置了/匹配DispatcherServlet,覆盖了父web.xml,最后得到web.xml是jsp文件匹配JspServlet,/匹配DispatcherServlet。
静态资源放行
配置/虽然能放行jsp文件,但是不能放行html和其他静态文件:
请求html文件也会去匹配@RequestMapping,而我们没有对应的controller,匹配不到所以报错。
只需要在sprignmvc.xml中加一句:
图片也可以请求:
貌似不用写项目虚拟路径也行:
不行:如果项目虚拟路径是/,那/img/bg.png的确会被认为是相对于根路径下的,而如果项目虚拟路径是/aaa,那/img/bg.png会被认为是相对于当前页面路径下的,如果页面路径/aaa/index.jsp,图片路径/img/ccc.jpg,那么在页面中请求该图片,写/img/ccc.jpg,那么请求的是/aaa/img/ccc.jpg
如果访问不到,要检查发布路径中有没有该资源,没有就复制过去:
@RequestMapping注意
1、类上配置该注解,表示在所有方法的注解上加一段路径。
2、参数:
不写参数名默认是value。
Controller获取数据
获取路径中的参数
与controller方法的参数同名就能获取到:
不同名也可以获取到:
RequestParam其他属性:
value和name属性是一样的;required表示是否必须的参数;defaultValue默认值;另外方法里获取参数跟路径中参数的顺序无关,只要有就能获取。
捕获请求头中的信息
@RequestHeader的value要按这个写:
获取Cookie里面的信息
之前Servlet的方式:
controller里用servlet原生对象
捕获路径中的值作为参数
注意:如果路径中捕获的名称和方法参数名称相同,@PathVariable就不需要写("名称"),但最好写上。
这种方式适合用来做REST风格。
REST风格
原始:
增:.../user/save,请求体传数据,
删:.../user/delete?id=1,
改:.../user/update?id=1,请求体传数据,
查:.../user/query?id=1。
rest:
增:.../user,post请求方式,
删:.../user/1,delete请求方式,1是id,
改:.../user/1,put请求方式,
查:.../user/1,get请求方式。
怎样实现:通过一个过滤器Filter,过滤所有请求,提交表单时根据隐藏域的_method变量的值来变更请求方式:
前端表单:
只不过jsp只能通过get/post请求,put和delete可能需要转发/重定向一下。
delete和put请求通过Filter过滤,post正常通过表单提交,get一般通过a标签,避免在url中出现表单信息。
封装pojo类对象
controller方法参数是pojxo类对象,且前端传来的参数和pojo类属性名相同,就能自动封装。
原理:反射调用set、get方法。
封装带有内部对象的对象
只需要在form表单的上修改一下name:
把内部对象的属性对应的字段name,改成内部对象名.属性名。
@ModelAttribute注解用法
注意到上面的username是null,因为我们不允许用户修改username。
这个场景我们应该先查出用户的完整信息,回写到前端,前端更新了允许更新的数据后,提交到后端的是完整的用户数据。也就是说,在方法里传入一个参数User,他会new一个User,再把前端返回的数据赋值上,现在我们需要提前封装一个数据库里的user,根据前端修改对这个User进行修改。
只需要加一个@ModelAttribute,他会先于pojoPlus这个方法执行,用于初始化一个User:
这回,username不是null了:
过滤器设置解码方式
web.xml:
过滤器过滤的请求不是拦截丢掉,而是识别到然后对他附加一些功能。
打开页面,请求了一次,为什么会执行两遍encoding filter:请求图标也会经过过滤器。
注意:如果设置后依然乱码,确认以下两点:
web.xml中设置编码的Filter要放在第一个;tomcat的web.xml文件要添加URIEncoging=utf-8
使用springmvc提供的编码Filter:(也要放在第一个)
controller向jsp中回写数据
四种回写方式
前三种是一样的,有一子类,继承了前三个类,参数传入三个类都是会转换成子类BindingAwareModelMap,因此效果一样。
第四种,是把上面说的子类放到ModelAndView中。
四种方式都是把数据写到request域中,再在jsp中使用。
controller向session中写数据
在类名上加注解@SessionAttributes,属性名不写的话默认是value,value是字符串或字符串数组,表示要向session中写的变量名:
注意是Attributes加s。除了value还有个types,表示这个类型的数据都写入session,value和types不是联合判断,是互相独立的。
方法中,用Model向request域写入变量:
@SessionAttributes工作原理是,把写入request域中的数据顺便也写到session域一份。
运行结果:
写入request中并且@SessionAttributes标注了的变量,会写到session中;写入request中但没在@SessionAttributes标注的变量,会不写到session中。
转发和重定向
转发forward
只需要在return的时候添加一个forward:转向另一个controller:
或最后一个return不用转发直接return字符串
前端的效果是url不变:
重定向redirect
和转发的区别只在于把forward改成redirect。
前端的效果是url会变:
数据合法性校验
导包:
需要验证的属性,加注解:
controller方法,参数pojo类对象前加@Valid注解,加一个BindingResult参数,如果有error,就把字段名和错误信息存到map里,最后把map加到reqyest域中:
前端显示错误信息:
请求form表单页面: