spring MVC基础使用总结

我是学习了尚硅谷的李守红老师的spring MVC视频之后根据课程的内容做的如下总结。不得不说李老师讲课不是很好,不建议初学者学习。
spring MVC是一个MVC框架,是基于spring开发的,所以可以和spring无缝衔接。
我们从spring MVC的helloWorld开始,一步步扩展需求,看spring MVC是如何解决新需求,如何改善繁琐的流程,简化开发者的任务的。

一、SpringMVC的HelloWorld

1、项目目录

SpringMVC的HelloWorld
任何web的框架搭建的基础都是web.xml,它是web应用程序容器的入口。看一个web项目时,只要看web.xml就可以看到该项目用到了哪些框架。

2、配置web.xml

(1)项目入口

web项目的入口:listener、servlet、filter等,作用是截取客户端的请求。
springmvc使用的入口是一个servlet(DispatcherServlet)。

...
  <display-name>springMVC1</display-name>
  ....
  
  <servlet>
  	<servlet-name>springMVC</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  		....
  </servlet>
  ....

(2)配置springmvc拦截哪些请求

下面的配置是拦截所有请求,也就是说,对于所有的请求,都会在配置文件中寻找与之对应的controller。

<servlet-mapping>
   <servlet-name>springMVC</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>

(3)配置启动时间

下面的配置是指tomcat一启动,spring的servlet就会初始化

<servlet>
   <servlet-name>springMVC</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--下面的配置是指tomcat一启动,spring的servlet就会初始化-->
   <load-on-startup>1</load-on-startup>
</servlet>

(4)配置SpringMVC的配置文件路径

SpringMVC的配置文件是指spring-servlet.xml
SpringMVC的配置文件路径

<servlet>
   <servlet-name>springMVC</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <!--指定配置文件的路径-->   
    <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath*:config/spring-servlet.xml</param-value>
      </init-param>
    <!--下面的配置是指tomcat一启动,spring的servlet就会初始化-->
   <load-on-startup>1</load-on-startup>
</servlet>

3、配置文件spring-servlet.xml内容

(1)配置视图解析器

web项目最终要给用户展示视图,这个视图解析器就是配置视图的一些基本参数的。

<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <!--前缀-->
   <property name="prefix" value="/"></property>
   <!--后缀-->
   <property name="suffix" value=".jsp"></property>
</bean>

(2)配置controller与浏览器访问路径的映射关系

<!--当浏览器中输入localhost:8080/springMVC1/test1/helloworld这个网址时,会自动去调用com.tgb.web.controller.HelloWorldController这个类-->
<bean name="/test1/helloworld" class="com.tgb.web.controller.HelloWorldController" />

4、编写Controller类

因为已经在spring-servlet.xml中指定了controller的路径的名称,则按照配置新建controller。
HelloWorldController

package com.tgb.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloWorldController implements Controller {

   @Override
   public ModelAndView handleRequest(HttpServletRequest arg0,
         HttpServletResponse arg1) throws Exception {
      
      System.out.println("-------hello tgb-----");
      
      return new ModelAndView("/welcome");
   }
}

新建的controller类实现了Controller接口。重写Controller接口的方法handleRequest()其返回值是ModelAndView类型的。
上述代码被执行时,首先在工作台打印**"-------hello tgb-----"**,然后返回"/wecome"页面,因为配置文件中有前缀和后缀配置,所以最终返回的是://welcome.jsp,若工程中有该页面,则会显示该页面。

(1)ModelAndView

public class ModelAndView {
    private Object view;
    private ModelMap model;
    private boolean cleared = false;

    public ModelAndView() {
    }

    public ModelAndView(String viewName) {
        this.view = viewName;
    }

ModelAndView类有很多构造函数,若输入参数为一个string字符串,则将视图(view)命名为字符串。

5、编写welcome.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
   hello world!  
</body>
</html>

JSP加载时,将在页面上显示“hello world!”

二、给界面传递参数

需求:View从Controller中拿数据
helloworld中是在jsp页面上写死了页面的内容是"hello world",这个所谓的MVC架构中,View端并没有从Model端拿任何数据。那如果现在想要拿数据呢?
我们知道数据是从Model传到Controller再从Controller传到View的,那我们先看一步:Controller是怎么传到View的,我们先假设Controller已经拿到了数据,不需要Model的参与。
传递数据有很多种,可大体分成两类,一种是基本数据类型,一种是键值对。

1、传递一个字符串

(1)在controller里面定义一个字符串,并返回

public class HelloWorldController implements Controller {

   @Override
   public ModelAndView handleRequest(HttpServletRequest arg0,
         HttpServletResponse arg1) throws Exception {
      String hello = "lsh hello 提高班";
      return new ModelAndView("/welcome","result",hello);
   }
}

返回的页面的参数名为“result”,值为变量hello的值。

(2)在JSP页面中获取传递的参数

<body>
   欢迎大家收看我的视频!提高班 李守宏
   <br/>
   <h>传递数据</h>
   ${result }
</body>

2、传递一个Map

(1)在controller中定义一个map,并返回

public class HelloWorldController implements Controller {

   @Override
   public ModelAndView handleRequest(HttpServletRequest arg0,
         HttpServletResponse arg1) throws Exception {

      Map<String,Object> map = new HashMap<String,Object>();
      map.put("map1", "提高班1");
      map.put("map2", "提高班2");
      map.put("map3", "提高班3");
      
      return new ModelAndView("/welcome","map",map);
   }
}

(2)在JSP页面中获取传递的参数

<body>

   <div>
      <c:forEach items="${map }" var="m">
      ${m.key } -----> ${m.value }
      </c:forEach>
   </div>

</body>

三、一个controller中写多个方法

问题:helloworld中,一个网址和一个controller绑定。在浏览器输入网址后,调用controller执行里面的方法,然后再返回视图。若controller里面有两个方法的话,程序会不知道调用哪个。所以,若是按照helloworld的设计,一个方法必须对应一个controller才行。
spring MVC当然有解决方法。

1、新建一个controller定义两个方法

MultiController.java

package com.tgb.web.controller;

public class MultiController extends MultiActionController {

   public ModelAndView add(HttpServletRequest request,HttpServletResponse response){
      System.out.println("----add----");
      return new ModelAndView("/multi","method","add");
   }
   
   public ModelAndView update(HttpServletRequest request,HttpServletResponse response){
      System.out.println("----update----");
      return new ModelAndView("/multi","method","update");
   }
   
}

注意这个controller类不再实现Controller接口了,而是集成了MultiActionController 类。
定义了两个方法,add和update。两个方法虽然返回的页面相同,但是传递的数据不同。

2、配置文件修改

(1)注册支持多个方法的解析器组件

springMVC和spring一样,所有的功能都是一个,只要将类注入,就拥有了该功能。

<bean id="paramMethodResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
   <property name="paramName" value="a"></property>   
</bean>

里面的value值可以任意设置,只要不重复就行,最后在浏览器输入网址时需要使用。

(2)注册调用新建的MultiController的路径的配置

<bean name="/test1/multi" class="com.tgb.web.controller.MultiController">
   <property name="methodNameResolver">
      <ref bean="paramMethodResolver"/>
   </property>
</bean>

注意这个bean调用了(1)步创建的解析器组件paramMethodResolver。

3、新建一个JSP页面

multi.jsp
调用method变量的值。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
   <h>多方法controller</h>
   本次方法是${method }
</body>
</html>

启动项目后浏览器中输入网址
localhost:8080/SpringMVC2/test1/multi?a=add
结果:本次方法是add
localhost:8080/SpringMVC/test1/multi?a=update
结果:本次方法是update

四、访问静态文件

问题:之前在web.xml文件中配置了拦截,是拦截所有的请求,将拦截的请求在注册的映射中搜索,找到请求对应的 controller类的具体方法,然后调用该方法。

<servlet-mapping>
   <servlet-name>springMVC</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>

当若页面请求静态文件(图片、样式等等)时,本应该是截取去项目路径去拿就好,配置文件中并没有配置静态文件和controller的映射关系的。所以,若还是按照之前的配置,将会访问不到静态资源。

1、配置静态资源

下面的配置是指,当浏览器访问/img/路径时,映射到/img/**路径下,并且该路径下的所有文件都不会被拦截。

<!-- 静态资源访问 -->
 <mvc:resources location="/img/" mapping="/img/**"/>  
 <mvc:resources location="/js/" mapping="/js/**"/>   

五、启用注解

问题:一个controller就需要注册一个bean,当controller很多时,注册会很麻烦,配置文件管理也成问题。
可以使用注解来代替繁琐的注册bean,达到的效果相同。

1、新建一个配置类

springAnnotation-servlet.xml
以前的配置文件中,值保留静态资源访问的配置和视图解析器的配置,其他的都删掉。

     <!-- 注解扫描包 -->
   <context:component-scan base-package="com.tgb.web.controller.annotation" />
   <!-- 开启注解 -->
   <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
   <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>
   <!-- 静态资源访问 -->
    <mvc:resources location="/img/" mapping="/img/**"/>  
    <mvc:resources location="/js/" mapping="/js/**"/>   
   
   <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/"></property>
      <property name="suffix" value=".jsp"></property>
   </bean>
 </beans>  

(1)首先配置注解扫描包

上面的配置是,扫描com.tgb.web.controller.annotation路径下的所有类。

(2)然后开启注解

有两个需要注册
DefaultAnnotationHandlerMapping是url和类的映射;
AnnotationMethodHandlerAdapter是类和方法的映射。

2、在web.xml中将原来的配置文件的路径改成springAnnotation-servlet.xml

<init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath*:config/springAnnotation-servlet.xml</param-value>
</init-param>

3、新建annotation路径,并在下面新建一个控制类

UserController.java

package com.tgb.web.controller.annotation;

//使用注解来代替以前实现Controller接口或继承MultiActionController 类
@Controller
public class UserController {

   //通过注解可以指定访问该方法的路径和访问方式
   @RequestMapping(value="/user/delUser",method=RequestMethod.GET)
   public ModelAndView delUser(){
      String result ="this is delUser------";
      return new ModelAndView("/jquery","result",result);
   }

   //通过注解可以指定访问该方法的路径和访问方式
   @RequestMapping(value="/user/toUser",method=RequestMethod.GET)
   public ModelAndView toUser(){
      return new ModelAndView("/jquery");
   }
}

六、其他优化

1、开启注解配置的优化

问题:启动注解时固定要注册两个bean,还是嫌麻烦,能不能再简单点儿

(1)优化前

因为每次使用注解都要注册两个

<!-- 开启注解 -->
   <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
   <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>

(2)优化后

<!-- 开启注解 -->
<mvc:annotation-driven/>

2、请求路径的优化

问题:通常一个controller下请求的路径都属于同一个根目录,每个方法从根目录写起,嫌麻烦,能不能简单点儿
把根目录提取,利用注解写到类上。

(1)优化前

@Controller
public class User2Controller {
   
   @RequestMapping(value="/user2/addUser")
   public String addUser(...){
      ...
   }
   
   @RequestMapping(value="/user2/delUser")
   public String delUser(...){
      ...
   }

(2)优化后

@Controller
@RequestMapping(value="/user2")
public class User2Controller {
   
   @RequestMapping(value="/addUser")
   public String addUser(HttpServletRequest request){
      ...
   }
   
   @RequestMapping(value="/delUser")
   public String delUser(HttpServletRequest request){
      ...
   }

3、“value=”也可以去掉

问题:请求路径前都要写“value=”每个都写,嫌麻烦,能不能去掉

(1)优化前

@Controller
@RequestMapping(value="/user2")
public class User2Controller {
   
   @RequestMapping(value="/addUser")
   public String addUser(HttpServletRequest request){
      ...
   }
   
   @RequestMapping(value="/delUser")
   public String delUser(HttpServletRequest request){
      ...
   }

(2)优化后

@Controller
@RequestMapping("/user2")
public class User2Controller {
   
   @RequestMapping("/addUser")
   public String addUser(HttpServletRequest request){
      ...
   }
   
   @RequestMapping("/delUser")
   public String delUser(HttpServletRequest request){
      ...
   }

4、请求方式可以不写

问题:每个方法都要写接口的类型,嫌麻烦,而且GET和POST傻傻分不清楚,能不能不写
优化后可以直接忽略掉,忽略之后,既可以使用GET方法也可以使用POST方法。

(1)优化前

@RequestMapping("/addUser",method=RequestMethod.POST)
public ModelAndView addUser(){
   ...
}

@RequestMapping("/delUser",method=RequestMethod.GET)
public ModelAndView delUser(){
   ...
}

(2)优化后

@RequestMapping("/addUser")
public ModelAndView addUser(){
   ...
}

@RequestMapping("/delUser")
public ModelAndView delUser(){
   ...
}

5、ModelAndView去掉,替换成返回值类型

问题:返回值类型总是ModelAndView,嫌麻烦,能不能不写
可以去掉,若有参数需要传递,可以使用经典的request.setAttribute()方法。

(1)优化前

@Controller
@RequestMapping("/user2")
public class UserController {

   @RequestMapping("/addUser")
   public ModelAndView addUser(){
      String result ="this is addUser------";
      return new ModelAndView("/jquery","result",result);
   }

   @RequestMapping("/delUser")
   public ModelAndView delUser(){
      String result ="this is delUser------";
      return new ModelAndView("/jquery","result",result);
   }
}

(2)优化后

@Controller
@RequestMapping("/user2")
public class UserController {

   @RequestMapping("/addUser")
   public String addUser(HttpServletRequest request){
      String result ="this is addUser------";
      request.setAttribute("result", result);
      return "/jquery";
   }

   @RequestMapping("/delUser")
   public String delUser(){
      String result ="this is delUser------";
      request.setAttribute("result", result);
      return "/jquery";
   }
}

七、从View向Controller传递数据

前面讲到了如何从controller想View传递数据。那么有没有从View向Controller传递数据的需求呢?有的,设想一下如下场景:新用户注册时,用户输入了账号、年龄、性别、密码等个人信息,点击注册,此时应该把用户填写的内容存储到数据库中,这样下次用户登录时,才可以在用户信息表中找到已经注册的用户信息。想存储到数据库中,首先要先传到Controller。

1、创建一个addUser.jsp页面,页面上可以输入用户名和年龄信息,还有提交按钮

参数传递

2、controller中创建addUser()方法

@RequestMapping("/addUser")
//若方法传入的参数名和jsp页面返回的参数名相同时,会自动将值传递,进行实例化
public String addUser(String userName,String age,HttpServletRequest request){
   System.out.println(userName);
   System.out.println(age);
}

若传递的参数有很多个时,可以通过创建POJO类型来简化参数的传递

(1)创建一个实体类User.java

package com.tgb.web.controller.entity;

public class User {
   
   private String userName;
   
   private String age;

   get()   set()
}

(2)Controller中接收参数

@RequestMapping("/addUser")
public String addUser(User user,HttpServletRequest request){
   request.setAttribute("userName", user.getUserName());
   request.setAttribute("age", user.getAge());
   
   return "/userManager";
}

八、从View页面上传文件

也会有上传文件的需求,比如在登录某个填报系统的时候,需要上传附件。

1、配置文件

注册组件
Annotation-servlet.xml

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <property name="defaultEncoding" value="utf-8" />
      <property name="maxUploadSize" value="10485760000" />
      <property name="maxInMemorySize" value="40960" />
</bean>

2、upload.jsp

创建该页面上传文件

<body>
   <h>添加用户</h>
   <form name="userForm" action="/springMVC7/file/upload2" method="post" enctype="multipart/form-data" >
      选择文件:<input type="file" name="file"> 
      <input type="submit" value="上传" >
   </form>
</body>

3、UpLoadController.java

@RequestMapping("/upload2")
public String upload2(HttpServletRequest request,HttpServletResponse response) throws IllegalStateException, IOException{
   //初始化一个解析器,解析spring MVC的上下文
   CommonsMultipartResolver multipartResolver  = new CommonsMultipartResolver(request.getSession().getServletContext());
   //使用解析器去解析request,看是否是Multipart类型
   if(multipartResolver.isMultipart(request)){
      //定义一个spring mvc封装好的MultipartHttpServletRequest去接收request
      MultipartHttpServletRequest  multiRequest = (MultipartHttpServletRequest)request;
      //迭代文件名
      Iterator<String>  iter = multiRequest.getFileNames();
      while(iter.hasNext()){
            //根据文件名拿到文件
            MultipartFile file = multiRequest.getFile((String)iter.next());
         if(file != null){
            String fileName = "demoUpload" + file.getOriginalFilename();
            String path = "D:/" + fileName;
            //新建一个文件类型
            File localFile = new File(path);
            //将file文件写到指定的文件localFile中
            file.transferTo(localFile);
         }
      }
   }
   return "/success";
}

4、success.jsp

<body>
   上传文件成功!!
</body>

九、总结

spring MVC是MVC设计理念的一种实现方式,用户只需要在JSP页面上写前端展示相关的代码,在controller中写页面跳转和数据传递的逻辑,在Model中处理数据逻辑就可以实现web开发。
spring MVC简化和优化了一些MVC的规则,大大简化了开发人员的配置工作。
关于Spring MVC的基础的使用大概就这些了,读者们肯定会纳闷儿,光将了View和Controller的交互,为啥没有Controller和Model的交互呢。
因为Model和数据库相关,直接写数据可操作的JDBC特别麻烦,而且关于对数据库的操作有成型的工具,比如Hibernate和Mybatis,所以我们都是使用Hibernate或Mybatis来间接操作数据库。所以要将Controller与Model的交互,要先介绍Hibernate或Mybatis。这部分操作放在下一讲进行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值