目录
1.SpringMVC执行流程
Spring的web框架围绕着DispatcherServlet设计.DispatcherServlet的作用是将请求分发到不同的处理器.
执行流程:
1.用户发送请求,DispatcherServlet接收并拦截. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心.
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5. Handler 执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的 ViewResolver)返回给DispatcherServlet ;
7. ViewResolver 结合Model和View,来渲染视图
8. 将渲染结果返回给客户端
配置SpringMVC(不使用注解)
1.配置web.xml , 注册DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.编写SpringMVC 的 配置文件!名称:springmvc-servlet.xml : [servletname]-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3.创建HelloController ,实现Controller 接口
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
将HelloController 注入到spring中
<!--Handler-->
<bean id="/hello" class="com.abc.controller.HelloController"/>
就可以使用了
配置SpringMVC(使用注解)
开发之中还是使用注解的
1.第一步还是配置web.xml,内容也一样
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.配置springmvc-servlet.xml配置文件,有些改动
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
3.创建Controller
@Controller
@RequestMapping("/HelloController")
public class HelloController {
//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){
//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");
//web-inf/jsp/hello.jsp
return "hello";
}
}
@Controller是为了让Spring IOC容器初始化时自动扫描到;
@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
方法中声明Model类型的参数是为了把Action中的数据带到视图中;
方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
然后就可以使用了.
通过设置ServletAPI , 不需要视图解析器
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
通过SpringMVC来实现转发和重定向 - 无需视图解析器;
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
//转发
return "/index.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
//转发二
return "forward:/index.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
//重定向
return "redirect:/index.jsp";
}
}
通过SpringMVC来实现转发和重定向 - 有视图解析器;
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
Restful风格
如果使用普通访问localhost:8080/add?a=1&b=2 就能访问了
@Controller
public class RestFulController{
//映射路径
@RequestMapping("/add")
public String index(int a,int b,Model model){
int sum = a+b;
model.addAttribute("sum",sum);
return "test";
}
}
如果使用Restful,地址为localhost:8080/add/1/2
需要在参数上添加@PathVariable
@Controller
public class RestFulController{
//映射路径
@RequestMapping("/add/{a}/{b}")
public String index(@PathVariable int a,@PathVariable int b,Model model){
int sum = a+b;
model.addAttribute("sum",sum);
return "test";
}
}
Restful更加高效和安全
数据处理
处理提交数据
@PathVariable
@PathVariable可以将占位符绑定参数列表
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name") String name){
System.out.println(name);
return "hello";
}
1、提交的域名称和处理方法的参数名一致
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
2、提交的域名称和处理方法的参数名不一致
@RequestParam 来映射请求参数 (request里的值)
属性:
1.value 值即为请求参数名
2.required 该参数是否必须,默认true 必须
3.defaultValue 请求参数的默认值
//@RequestParam("username") : username提交的域的名称 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
3、提交的是一个对象
名称写对,会自动匹配,支持级联属性
@RequestMapping("/user")
public String user(User user){
System.out.println(user);
return "hello";
}
4.请求头
@RequestHeader
@RequestHeader绑定请求头的属性值
@RequestMapping("/user")
public String user(@RequestHeader(value="Accept-Language") string a){
System.out.println(a);//输出请求头里的数据
return "hello";
}
@CookieValue
@CookieValue
可以处理方法入参绑定某个cookie值
@RequestMapping("/user")
public String user(@CookieValue(value="sessionId",required=false) string a){
System.out.println(a);//输出sessionId
return "hello";
}
数据显示到前端
第一种 : 通过ModelAndView
SpringMVC会把ModelAndView中的model放到request域中
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
第二种 : 通过ModelMap
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}
第三种 : 通过Model (也可以说是Map,ModelMap类型)
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Map map){
map.put("name","123");
return "test";
}
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
@SessionAttributes
只能放在类的上面,不能放在方法上面
@SessionAttributes(value={“name”} ,types={String.class})
value是根据名字放入session, types是根据数据类型放入session
@SessionAttributes(value={"name"})//name会被同时放入session域和request域
@Controller
public class SpringMvcController{
@RequestMapping("/ct2/hello")
public String hello( Map<Object,String> map){
map.put("name","123");
return "test";
}
}
@ModelAttributes
有 @ModelAttributes标记的方法,会在每个目标方法执行之前被SpringMVC调用
可以解决一些修改时的问题
运行流程:
1.执行@ModelAttributes注解表示的方法:从数据库抽取对象,把对象放入Map中
2.SpringMVC从Map中抽取对象,并且把前端表单发来的参数赋值给该对象
3.SpringMVC把上述对象传入目标方法的参数列表
注意:@ModelAttributes注解表示的方法中存入Map的对象的名字要和目标方法的参数类型的名称(第一个字母小写)一致,或者和目标方法的参数类型中有:@ModelAttribute(value="")的value值一致.
SpringMVC确定目标方法POJO类型入参的过程:
1.确定一个key:
(1)若目标方法的POJO类型的参数没有使用@ModelAttributes,则key为POJO类名第一个字母小写
(2)若目标方法的POJO类型的参数使用了@ModelAttributes,则key为@ModelAttributes的value属性值
2.在implicitModel中查找key对应的对象,若存在,则作为入参传入
(1)若在@ModelAttributes标记的方法中在map中保存过,且key和第一步确定的key一致,则会获取到.
3.若在implicitModel中不存在key对应的对象,则检查当前Handler是否使用@sessionAttributes注解修饰
(1)若使用@sessionAttributes注解修饰,且@sessionAttributes注解的value值包含了key,则会从httpsession中获取key所对应的value值,若存在则直接传入目标方法,若不存在则抛出异常.
4.若Handler没有表示@sessionAttributes或者@sessionAttributes中vlaue值不含key,则通过反射来创建POJO对象,传入目标方法参数
5.SpringMVC会把key和value保存到implicitModel中,进而会保存到request中
源码分析的流程:
1.调用@ModelAttribute注解修饰的方法,实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中,
2.解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性
(1)创建WebDataBinder对象:
a.确定obejctName属性:
*.若目标使用了@ModelAttribute来修饰,则attrName为value值
*.若传入的attrName属性为"",则objectName为类名第一个字母小写
b.确定target属性:
*.在implicitModel中查找attrName对应的属性值,若存在就OK了,若不存在则验证当前Handler是否使用了@sessionAttributes进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值,若session中没有对应的属性值就报错.
*.若Handler没有使用@sessionAttributes进行修饰,或者@sessionAttributes中没有使用value值指定key和attrName相匹配,则通过反射创建了POJO对象.
(2)SpringMVC把表单传过来的参数赋值给WebDataBinder的target对应的属性
(3)SpringMVC会把WebDataBinder的attrName和target给到implicitModel中,进而传到request域
(4)把WebDataBinder的target作为参数传递给目标方法的入参
@MultipartResolver 实现文件上传
springMVC默认没有装配MultipartResolver ,因此如果想使用文件上传功能,需要配置MultipartResolver
需要commons-fileupload.jar,commons-io.jar
springmvc.xml中配置MultipartResolver
<bean id="multipartResolver " class="org.springframework.web.mutipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000"></property>
</bean>
Controller层代码,处理上传的文件
@RequestMapping("/MultipartResolverUpload")
public String test(@RequestParam("file") MultipartFile file){
System.out.println("filename"+file.getOriginalFilename());
return "success";
}
自定义拦截器
1.写实现类,实现HandlerInterceptor接口
public class FirstInterceptor implements HandlerInterceptor{}
2.配置文件中配置自定义拦截器
<mvc:interceptors>
<!--配置自定义拦截器-->
<bean class="com.abc.Controller.FirstInterceptor "></bean>
</mvc:interceptors>