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无效!

 

posted on 2018-05-05 23:26  FuYingju  阅读(48)  评论(0编辑  收藏  举报