SpringMVC 简介
SpringMVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一部分,它和Struts2一样都属于表现层的框架。MVC(Model模型 View视图 Controller控制器):这是一种软件架构思想,是一种开发模式,将软件划分为三种不同类型的模块,分别是模型,视图,和控制器。
模型:用于封装业务逻辑处理(java类);
视图:用于数据展现和操作界面(Servlet);
控制器:用于协调视图和模型(jsp);
SpringMVC五大组件
前端控制器(DispatcherServlet)
映射处理器(HandlerMapping)
处理器(Controller)
模型和视图(ModelAndView)
视图解析器(ViewResoler)
SpringMVC运行流程
① 用户发送请求至前端控制器DispatcherServlet。
② DispatcherServlet收到请求调用HandlerMapping处理器映射器。
③ 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
④ DispatcherServlet调用HandlerAdapter处理器适配器。
⑤ HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
⑥ Controller执行完成返回ModelAndView。
⑦ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
⑧ DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
⑨ ViewReslover解析后返回具体View。
⑩ DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11. DispatcherServlet响应用户。
SpringMVC的配置
1、配置前端处理器
在web.xml中配置前端控制器,在服务器启动时就被创建,用来对请求和响应进行接收 和 分发处理,其在配置时可以设置一个初始化参数,用来定位SpringMVC.xml的地址:
<web-app> <display-name>display null</display-name> <servlet> <!--配置dispatcherServlet --> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置dispatcherServlet的初始化参数:定位 spring-mvc.xml的地址--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/*</url-pattern> <!--映射所有请求--> </servlet-mapping> <welcome-file-list> <!--欢迎页--> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
2、配置映射处理器
① 根据 controller 的 name 名称来映射寻找 controller:BeanNameUrlHandlerMapping(默认)
<!-- 开启该映射,默认是开启的 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <!-- 定义bean的name属性 --> <bean id="helloController" name="/hello1.do" class="com.controller.HelloController"></bean> <!--界面访问URL:与bean的name属性值一致--> http://localhost:5080/springmvc/hello1.do
② 根据URL来映射寻找controller:SimpleUrlHandlerMapping(推荐使用)
此时当请求的url为a.action或b.action或c.action 时,映射器都会映射为id是indexController的controller。
<!-- 开启该映射 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <property name="mappings"> <props> <prop key="/a.action">indexController</prop> <prop key="/b.action">indexController</prop> <prop key="/c.action">indexController</prop> </props> </property> </bean> <!-- 定义bean --> <bean id="indexController" name="/index.action" class="com.lh.controller.testController"></bean>
③ 根据controller的类名来映射寻找controller:ControllerClassNameHandlerMapping(不推荐使用)
这种方式一般不采用,因为一旦控制器名称更改的话,访问路径也得跟着更改,变动性较大。
<!-- 开启该映射 --> <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean> <!-- 定义bean --> <bean id="hello3Controller" class="com.controller.UserController"></bean> <!-- 界面访问URL --> http://localhost:8080/springmvc/userController.action
注意:在用控制器类名处理器映射时,类名必须为XxxController,URL名字为类名首字母小写,以action为请求后缀。
如果在映射配置文件中配置了多个映射处理器,请求的时候具体走哪一个是有顺序的,可以通过添加一个参数进行设置。
<bean name="/home.action" class="cn.itcast.springmvc.TestController"> <property name="order" value="0"/> <!--value的值可以是0,1,2,越小优先级越高--> </bean>
④ 使用注解来映射寻找controller:
在实际开发中一般用注解的方式实现控制器映射。@Controller 声明该类是一个 SpringMVC Controller。 @RequestMapping除了修饰方法,还可以修饰类。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径,在方法上使用:@RequestMapping("/login") 声明该方法处理哪一个请求。
首先需要在配置文件中声明开启支持@RequestMapping请求和Controller映射的功能:
<mvc:annotation-driven></mvc:annotation-driven>
配置请求处理方法
@Controller public class Login { @RequestMapping("/login") public ModelAndView testModelAndView(){ String viewName = "success"; ModelAndView modelAndView = new ModelAndView(viewName); //添加模型数据到ModelAndView中 modelAndView.addObject("time",new Date()); return modelAndView; }
3、配置视图解析器
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <!-- 前缀匹配 --> <property name="suffix" value=".jsp"></property> <!-- 后缀匹配 --> </bean>
获取请求参数
1、使用Servlet的原生API获取请求参数方法,直接通过获取表单提交上来的name属性来获取表单内容。
@RequestMapping("login-action.form") public String login(HttpServletRequest request){ String name = request.getParameter("username"); String pwd = request.getParameter("password"); return "success"; }
2、通过@RequestParam注解,自动将表单的参数注入到方法参数里面,实现对应关系。
- @RequestParam:来映射请求参数;
- value:值即请求参数的参数名;
- required:该参数是否必须;
- defaultValue:请求参数的默认值;
@RequestMapping(value="login-action2.form") public String login2(@RequestParam(value="username",required=false)String uname,String pwd){ System.out.println(uname); System.out.println(pwd); return "success"; }
3、自动注入Bean属性
定义了User实体类之后,并且属性名要与表单组件的name属性名一致,就可以实现使用POJO对象绑定请求参数值的功能,SpringMVC会按请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值。其中的参数User对象,里面的属性值已经从前端获取到,比如name变量的值就是前端input标签name属性的value值,pwd属性的值就是前端pwd属性的值。并且在登陆成功页面,通过EL表达式:${user.name }也可以被读取出来,上面两个方法就读取不出来,支持级联属性,例如:address.city、address.province等。
<form action="springmvc/testPOJO" method="post"> username: <input type="text" name="username"/> password: <input type="password" name="password"/> email: <input type="text" name="email"/> age: <input type="text" name="age"/> city: <input type="text" name="address.city"/> province: <input type="text" name="address.province"/> 提交:<input type="submit" value="Submit"/> </form>
这个时候控制台就会收到前台传过来的所有的值,包括address里面的city和province。
@RequestMapping("/testPOJO") public String testPOJO(User user) { System.out.println("testPOJO User: " + user); return "success"; }
4、通过@PathVariable注解
该注解用来映射请求URL中绑定的占位符。通过@PathVariable可以将URL中占位符的参数绑定到controller处理方法的入参中。
@RequestMapping("/testPathVariable/{id}") public String testPathVariable(@PathVariable(value="id") Integer id){ System.out.println("testPathVariable:" + id); return SUCCESS; }
在index.jsp中我们添加一条连接,用来触发一个请求:
<a> href="springmvc/testPathVariable/1">testPathVariable</a><br/><br/>
可以看到这里有一个超链接,点击后会进入到springmvc/testPathVariable/1对应的controller处理的方法中,那我们现在就是想获取到这个请求参数中的“1”,所以在testPathVariable方法上加入“/testPathVariable/id”,关于id”,关于{id}的具体对应在该方法的参数中,通过@PathVariable(value="id")来声明要接收的请求参数,并通过Integer id来绑定和接收。通过该种方式,我们就可以得到前台页面请求的参数“1”。
处理模型数据
1、ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据。
@RequestMapping("login-action.form") public ModelAndView login4(String name,String pwd,HttpServletRequest request){ Map data = new HashMap(); User user = userService.login(name, pwd); data.put("user",user); //这样在success.jsp页面内就可以直接利用EL表达式${user.name };${user.pwd }的方式使用这些参数,如果没写则调用不到 request.getSession().setAttribute("user",user); //将用户数据存储到Session中 return new ModelAndView("success",data); } @RequestMapping("testModelAndView") public ModelAndView testModelAndView(){ String viewName = "success"; ModelAndView modelAndView = new ModelAndView(viewName); //添加模型数据到ModelAndView中 modelAndView.addObject("time",new Date()); //这样在success.jsp页面上可以用${requestScope.time}获取它的值:new Date() return modelAndView; }
2、Map,Model和ModelMap参数
如果方法的入参为Map,Model和ModelMap类型,Spring MVC会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
@RequestMapping("/testmap") public String testmap(Map<String,Object> map) { map.put("age", 13); return "success"; } @RequestMapping("/testModel") public String testModel(Model model) { model.addAttribute("email","ddd@qq.com"); return "success"; } @RequestMapping("/testModelmap") public String testModelmap(ModelMap modelMap) { modelMap.addAttribute("city", "Beijing"); return "success"; } //前台页面通过el表达式语言 $attributeName 或者是C标签库下的方法,来获取并展示map中的数据 ${requestScope.age } ${requestScope.email} ${requestScope.city}
使用Model和ModelAndView这两个类在spring的视图解析时有着很大的区别,具体就表现在Model只是用来传输数据的,并不会进行业务的寻址。其次,两者还有一个最大的区别,那就是Model是每一次请求可以自动创建,但是ModelAndView 是需要我们自己去new的。
3、@SessionAttributes注解
若希望在多个请求之间共用某个模型属性数据,则可以在控制器类名上标注一个 @SessionAttributes 注解,SpringMVC将在模型中对应的属性暂存到HTTPSession中。@SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
//SessionAttributes只能放在类上,不能在方法上 //这样在jsp页面内用 ${sessionScope.user} 也可以获取到值了。会将隐含模型中所有类型为 User 的属性添加到会话中 @SessionAttributes(types=User.class) //将名为 user1 和 user2 的模型属性添加到会话中。 @SessionAttributes(value={"user1", "user2"}) //将模型中所有类型为 User 及 Dept 的属性添加到会话中 @SessionAttributes(types={"User.class", "Dept.class"}) //将名为 user1 和 user2 的模型属性添加到会话中,同时将所有类型为 Dept 的模型属性添加到会话中 @SessionAtributes(value={"user1", "user2"}, types={Dept.class})
4、@ModelAttribute注解
- 作为方法参数
写好控制器,将@ModelAttribute标注在user入参的地方。可以实现User实体类字段和表单参数之间的绑定关系,在success.jsp页面直接通过EL表达式:${user.name }、${user.pwd },就可以获取用户输入的数据,需要注意的是:实体类字段名称要和表单组件的name属性名称要一致。
@Controller @RequestMapping("/model") public class ModelController { @RequestMapping("/parameter") public String parameter(@ModelAttribute("user") User user) { return "index"; } }
- 作用在方法上
将控制器对象更改为如下。
@Controller @RequestMapping("/model") public class ModelController { // 方法返回类型为void时,只是单纯的作为请求路由的第一站,使用者可在方法内部操作Model和Request等参数实现功能。 @ModelAttribute public void modelAttributeMethod(HttpServletRequest request, String reqParam, Model model){ model.addAttribute("reqParam",reqParam); request.setAttribute("methodParam","Hello ModelAttribute"); } // 方法返回类型不为void时,@ModelAttribute会将返回值放到Model中,并将该类型名称的首字母小写作为model中的属性名。 @ModelAttribute public User initUser(){ User user = new User(); user.setName("default"); return user; } @RequestMapping("/parameter") public String parameter(@ModelAttribute("user") User user) { return "index"; } }
再次进行测试,虽然我们没有传入name参数,但是还要有name的值存在。
注意:在一个Controller内,被 @ModelAttribute 标注的方法会在此 Controller 的每个 handler 方法执行前被执行。
可以理解为:
1. 请求到达Controller后,不论其他handler方法的RequestMapping值是多少,请求都会路由至被@ModelAttribute标注的方法;
2. 由springMVC再对request执行一次forward,路由至真正的handler方法。
SpringMVC的转发与重定向
1.转发是在服务端完成的,重定向是在客户端完成的。
2.转发速度快,重定向速度慢
3.转发是同一次请求,重定向是两次请求
4.转发地址栏没有变化,重定向有变化
5.转发是在同一台服务器,重定向不必。
具体的举个例子,转发速度快,一般默认情况下用转发。但是有一些场景却不能用转发,比如我们登陆后要提交表单,如果用转发的话,当用户刷新页面,会造成重复提交。
转发:
@RequestMapping(value = "test") public String test(HttpServletRequest request, HttpServletResponse response) { return "redirect:/test.jsp";
重定向:
@RequestMapping(value = "test") public String test(HttpServletRequest request, HttpServletResponse response) { return "redirect:/test.jsp"; }
注意:model的生命周期是request,所以用重定向的话,model无效!