【学习笔记】SpringMVC

一、MVC架构

1.1 什么是MVC

  • MVC :包括模型、视图、控制器
  • 将业务逻辑、数据、显示分离的方法来组织代码
  • 作用:降低了视图与业务逻辑间的双向偶合。

Model(模型): 数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或 JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

View(视图): 负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。

Controller(控制器): 接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。

1.2 回顾Servlet

  • 新建一个 maven 项目
    导入依赖:
<dependencies>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>5.1.9.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>2.5</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <version>2.2</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
   </dependency>
</dependencies>
  • 新建一个模块
    模块名:springMVC-01-Servlet
    添加web-app支持(添加框架支持)
    编写Servlet类
public class HelloServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //1. 获取参数
        String method = req.getParameter("method");
        if (method.equals("add")){
    
    
            req.getSession().setAttribute("msg","执行了add方法");
        }
        if (method.equals("delete")){
    
    
            req.getSession().setAttribute("msg","执行了delete方法");
        }
        //2. 业务逻辑
        //3. 视图跳转
        req.getRequestDispatcher("WEB-INF/jspDir/test.jsp").forward(req,resp);/*服务器识别  8080/项目名 */
    }
}

配置Servlet程序

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.study.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/helloServlet</url-pattern><!--浏览器识别 8080端口-->
    </servlet-mapping>

跳转页面 test.jsp

<html>
<head>
    <title>Title</title>
</head>
<body>
    这是跳转过来的jsp页面
    ${msg}
</body>
</html>

首页 index.jsp

<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <form action="/01Servlet/helloServlet" method="get"> <%--浏览器识别 不加项目名,手动加 --%>
      方法:<input type="text" name="method">
      <input type="submit" value="提交">
    </form>
  </body>
</html>

MVC框架要做哪些事情

  1. 将url映射到java类或java类的方法 .

  2. 封装用户提交的数据 .

  3. 处理请求–调用相关的业务处理–封装响应数据 .

  4. 将响应的数据进行渲染 . jsp / html 等表示层数据 .

二、什么是SpringMVC

Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。

2.1 中心控制器

Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。
Spring MVC框架以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)

2.2 SpringMVC执行原理

  • 执行流程图
    在这里插入图片描述

  • 执行流程:

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
    我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
    如上url拆分成三部分:
    http://localhost:8080 服务器域名
    SpringMVC 部署在服务器上的web站点
    hello 表示控制器
    通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。(查找 id 为 /hello 的 bean)

  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。(适配到 hello 的 controller ,class=“com.study.controller.HelloController”)

  6. Handler让具体的Controller执行。

  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView(模型和视图)。

  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名(要跳转的jsp页面)。

  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

三、第一个SpringMVC程序

  • 新建一个模块 ,命名为 SpringMVC-02-hellomvc,添加 web-app框架支持。
    在这里插入图片描述
  • 配置 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.注册DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--绑定Spring【MVC】配置文件-->
        <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>

配置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"
       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"/>


    <!--添加视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前后缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
</beans>
  • 编写操作的业务,交给Controller
public class HelloController implements Controller {
    
    
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    
    
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","跳转到jsp页面!!!");
        mv.setViewName("hello");/*跳转视图名 /WEB-INF/jsp/hello.jsp*/
        return mv;
    }
}
  • 注册bean
    <!--注册 HelloController-->
    <bean id="/hello" class="com.study.controller.HelloController"/>
  • hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${msg}
</body>
</html>
  • 配置到Tomcat服务器

访问出现404问题

  1. 确认导入依赖
    在这里插入图片描述
  2. 在IDEA的项目发布中,添加lib依赖
    在这里插入图片描述
    在这里插入图片描述

四、使用注解开发SpringMVC

  • 新建一个module,添加web-app框架支持
  • 配置web.xml(和普通程序一样)
  • 配置SpringMVC配置文件 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.study.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>
  • 创建Controller(使用注解)
@Controller // 让Spring IOC容器初始化时自动扫描到
@RequestMapping("/HelloController") //映射请求路径
public class HelloController {
    
    

    //真实访问地址 : 项目名/HelloController/hello
    @RequestMapping("/hello")
    public String hello(Model model){
    
     //Model类型的参数是为了把Action中的数据带到视图中
        model.addAttribute("msg","hello,SpringMVC Annotation!!");
        return "hello"; //web-inf/jsp/hello.jsp
    }
}
  • 创建视图层
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

  • 为工程添加 lib 后,启动Tomcat
    使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器
    通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

五、控制器Controller

  • 继承Controller
    缺点:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦;
  • 使用@Controller
@Controller
@RequestMapping("/HelloController")
public class HelloController {
    
    
    
    //真实访问地址 : 项目名/HelloController/hello
    @RequestMapping("/hello")
    public String hello(Model model){
    
    
        model.addAttribute("msg","hello,SpringMVC Annotation!!");
        return "hello"; //web-inf/jsp/hello.jsp
    }
    @RequestMapping("/hello01")
    public String hello01(Model model){
    
    
        model.addAttribute("msg","hello01 ,SpringMVC Annotation!!");
        return "hello";
    }
    
}

我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系

六、RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

@Controller
@RequestMapping("/HelloController")
public class HelloController {
    
    
    
    //真实访问地址 : 项目名/HelloController/hello
    @RequestMapping("/hello")
    public String hello(Model model){
    
    
        return "hello"; 
    }
}

七、RestFul风格

概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

使用RestFul风格,通过相同的RUL但是不同的请求方式,会请求到不同的页面。

RestFul风格 样例


@Controller
public class ControllerRestFul {
    
    

    /*
         springAnnotation工程名    
        普通请求方式:http://localhost:8080/springAnnotation/t1?a=1&b=2
     */
    @RequestMapping("/t1")
    public String test1(int a, int b, Model model){
    
    
        model.addAttribute("msg","普通请求方式 t1:"+(a+b));
        return "hello";
    }

    /*
        springAnnotation工程名    
        RestFul 请求风格:http://localhost:8080/springAnnotation/t2/1/3
     */
    @RequestMapping("/t2/{a}/{b}")
    public String test2(@PathVariable int a,@PathVariable int b, Model model){
    
    
        model.addAttribute("msg","RestFul风格: "+(a+b));
        return "hello";
    }
    
}

使用method属性指定请求类型

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@Controller
public class ControllerRestFul {
    
    

    /*
        post请求方式
     */
    //@PostMapping("/t1/{a}/{b}")
    @RequestMapping(value = "/t1/{a}/{b}",method = RequestMethod.POST)
    public String test1(@PathVariable int a,@PathVariable int b, Model model){
    
    
        model.addAttribute("msg","post请求方式 :"+(a+b));
        return "hello";
    }

    /*
        get请求方式
     */
    //  @RequestMapping(path = "/t1/{a}/{b}",method = RequestMethod.GET)
    @GetMapping("/t1/{a}/{b}")// 【推荐使用】
    public String test2(@PathVariable int a,@PathVariable int b, Model model){
    
    
        model.addAttribute("msg"," get请求方式 : "+(a+b));
        return "hello";
    }

}

八、转发和重定向

视图解析器——默认转发

访问后浏览器地址为 :http://localhost:8080/springAnnotation/m1/t1

    @RequestMapping("/m1/t1")
    public String test1(Model model){
    
    
        model.addAttribute("msg","默认转发");
        return "hello";//默认为转发
    }

不需要视图解析器——转发

    @RequestMapping("/m1/t2")
    public String test2(Model model){
    
    
        model.addAttribute("msg","转发 不需要视图解析器");
        return "forward:/WEB-INF/jsp/hello.jsp";//转发操作
    }

在这里插入图片描述
不需要视图解析器——重定向

    @RequestMapping("/m1/t3")
    public String test3(Model model){
    
    
//        model.addAttribute("msg","重定向 不需要视图解析器");
        return "redirect:/WEB-INF/jsp/hello.jsp";//重定向操作
    }

重定向后,无法访问WEB-INF文件夹
在这里插入图片描述

九、前端数据的获取的回传

9.1 获取参数

@Controller
public class Data {
    
    
	//一、不限定参数,通过url传参
    //http://localhost:8080/springAnnotation/d1/t2?name=yaojingyang  
    @RequestMapping("/d1/t1")
    public String test1(String name , Model model){
    
    
        model.addAttribute("msg",name);
        return "hello";
    }

	//二、通过注解限定url参数名称
    //限定参数名 http://localhost:8080/springAnnotation/d1/t2?Username=yaojingyang
    @RequestMapping("/d1/t2")
    public String test2(@RequestParam("Username") String name,Model model){
    
    //获取
        model.addAttribute("msg",name);
        return "hello";
    }
    
	/* 当有一个实体类
	public class User {
    	private String name;
	    private int age;
	    //get、set tostring方法构造
	}
	*/
    //参数为类 http://localhost:8080/springAnnotation/d1/t3?name=tst&age=12
    @RequestMapping("/d1/t3")
    public String test3(User user,Model model){
    
    
        model.addAttribute("msg",user.toString());
        return "hello";
    }

}

9.2 回传参数

  • 通过ModelAndView :返回一个模型视图对象(继承Controller用到)
public class HelloController implements Controller {
    
    
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    
    
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","跳转到jsp页面!!!");
        mv.setViewName("hello");/*跳转视图名 /WEB-INF/jsp/hello.jsp*/
        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
    public String test3(User user,Model model){
    
    
        model.addAttribute("msg",user.toString());
        return "hello";
    }

三者对比
在这里插入图片描述

十、乱码问题解决

首页提交表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <form method="post" action="/springAnnotation/e1/t1">
      <input type="text" name="name">
      <input type="submit" value="提交">
    </form>
  </body>
</html>

Controller控制器


@Controller
public class EncodingTest {
    
    

    @RequestMapping("/e1/t1")
    public String test1(String name, Model model){
    
    
        model.addAttribute("msg",name);
        return "hello";
    }
}

测试出现乱码
配置web.xml

    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern><!--注意:/* 包含jsp文件-->
    </filter-mapping>

其他处理方式

  • 修改 Tomcat 编码方式 apache-tomcat-8.0.50\conf\server.xml
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443" />

在这里插入图片描述

  • 自定义过滤器
package com.kuang.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
    
    

   @Override
   public void destroy() {
    
    
  }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
       //处理response的字符编码
       HttpServletResponse myResponse=(HttpServletResponse) response;
       myResponse.setContentType("text/html;charset=UTF-8");

       // 转型为与协议相关对象
       HttpServletRequest httpServletRequest = (HttpServletRequest) request;
       // 对request包装增强
       HttpServletRequest myrequest = new MyRequest(httpServletRequest);
       chain.doFilter(myrequest, response);
  }

   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
    
    
  }

}

//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
    
    

   private HttpServletRequest request;
   //是否编码的标记
   private boolean hasEncode;
   //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
   public MyRequest(HttpServletRequest request) {
    
    
       super(request);// super必须写
       this.request = request;
  }

   // 对需要增强方法 进行覆盖
   @Override
   public Map getParameterMap() {
    
    
       // 先获得请求方式
       String method = request.getMethod();
       if (method.equalsIgnoreCase("post")) {
    
    
           // post请求
           try {
    
    
               // 处理post乱码
               request.setCharacterEncoding("utf-8");
               return request.getParameterMap();
          } catch (UnsupportedEncodingException e) {
    
    
               e.printStackTrace();
          }
      } else if (method.equalsIgnoreCase("get")) {
    
    
           // get请求
           Map<String, String[]> parameterMap = request.getParameterMap();
           if (!hasEncode) {
    
     // 确保get手动编码逻辑只运行一次
               for (String parameterName : parameterMap.keySet()) {
    
    
                   String[] values = parameterMap.get(parameterName);
                   if (values != null) {
    
    
                       for (int i = 0; i < values.length; i++) {
    
    
                           try {
    
    
                               // 处理get乱码
                               values[i] = new String(values[i]
                                      .getBytes("ISO-8859-1"), "utf-8");
                          } catch (UnsupportedEncodingException e) {
    
    
                               e.printStackTrace();
                          }
                      }
                  }
              }
               hasEncode = true;
          }
           return parameterMap;
      }
       return super.getParameterMap();
  }

   //取一个值
   @Override
   public String getParameter(String name) {
    
    
       Map<String, String[]> parameterMap = getParameterMap();
       String[] values = parameterMap.get(name);
       if (values == null) {
    
    
           return null;
      }
       return values[0]; // 取回参数的第一个值
  }

   //取所有值
   @Override
   public String[] getParameterValues(String name) {
    
    
       Map<String, String[]> parameterMap = getParameterMap();
       String[] values = parameterMap.get(name);
       return values;
  }
}

猜你喜欢

转载自blog.csdn.net/DREAM_yao/article/details/113880671