SpringMVC学习记录(一)

本文深入剖析SpringMVC框架的原理与应用,涵盖三层架构、MVC模型、springMVC入门、执行流程及注解配置等内容,适合初学者及进阶开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、三层架构和MVC

1.1 三层架构
  1. 一般来说开发服务端程序,一般都是两种形式,一种C/S架构,另一种就是B/S架构
  2. 我们使用Java语言基本上都是开发B/S架构的程序。B/S架构又分为了标准的三层架构:表现层、业务层、持久层。
  3. 三层架构
    (1)表现层:也就是WEB层,用来和客户端进行数据交互。一般使用MVC的设计模式
    (2)业务层:处理一些具体的业务逻辑
    (3)持久层:用来操作数据库的
1.2 MVC模型
  1. MVC全名是Model View Controller 模型视图控制器,每个部分各司其职。在MVC的编程思想中,前端的请求交给Controller,由Controller调用相应的model,最后将结果交给视图,返回给客户端程序。
  2. Model:数据模型,JavaBean的类,用来进行数据封装。
  3. View:指JSP、HTML用来展示数据给用户
  4. Controller:用来接收用户的请求,整个流程的控制器。用来进行数据校验等。
    在这里插入图片描述

二、springMVC入门

2.1 springMVC概述
  1. springMVC是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级WEB框架。
  2. springMVC 属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
  3. 使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架。(struts 框架,webwork框架等)
    springMVC的有点所在。
1、清晰的角色划分:
前端控制器(DispatcherServlet)
请求到处理器映射(HandlerMapping)
处理器适配器(HandlerAdapter)
视图解析器(ViewResolver)
处理器或页面控制器(Controller)
验证器( Validator)
命令对象(Command 请求参数绑定到的对象就叫命令对象)
表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。
2、分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要。
3、由于命令对象就是一个 POJO,无需继承框架特定 API,可以使用命令对象直接作为业务对象。
4、和 Spring 其他框架无缝集成,是其它 Web 框架所不具备的。
5、可适配,通过 HandlerAdapter 可以支持任意的类作为处理器。
6、可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制。
7、功能强大的数据验证、格式化、绑定机制。
8、利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。
9、本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
10、强大的 JSP 标签库,使 JSP 编写更容易。
2.2 springMVC入门程序

第一步:构建项目,导入一定的依赖jar包
在这里插入图片描述
导入MAVEN的依赖(pom.xml)

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
     <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

第二步:配置springMVC的核心控制器(Dispatch erServlet)
在web.xml中新增servlet的配置

<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

第三步:编写配置文件(springMVC.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"
   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">
  <!--开启扫描-->
  <context:component-scan base-package="com.gx"/>
</beans>

第四步:编写HelloController以及index.jsp

//作用是将java类交给spring容器
//语义注解,控制层
@Controller
public class WorldController {
  @RequestMapping("world")
  public String world(){
    System.out.println("hello spring mvc annotation");
    return "/index.jsp";
 }
}

第五步:启动Tomcat,进行测试
可以发现返回index.jsp页面,并且页面后台控制台打印hello spring mvc annotation

三、分析springMVC的执行流程

在这里插入图片描述
上面的图是我们入门案例的流程大致图。编写一个HelloController,和index.jsp,,访问controller层,然后我们就可以看到index.jsp页面。分析中间的执行流程?

1.认识DispatcherServlet

DispatcherServlet是springMVC的前端控制器,作用是处理客户端请求。
分析配置DispatcherServlet的配置中的属性:

<!-- 配置 spring mvc 的核心控制器 -->
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,用于读取 SpringMVC 的配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

DispatcherServlet需要读取一个配置文件,如果没有传入init-param的话,就会报出异常。

Caused by: java.io.FileNotFoundException: Could not open ServletContext 
resource [/WEB-INF/DispatcherServlet-servlet.xml]

表示的是默认DispatcherServlet配置的时候,如果没有配置配置文件的话,DispatcherServlet默认读取一个名称为DispatcherServlet-servlet.xml的配置文件。

DispatcherServlet的映射路径(servlet-mapping)设置为/。而不是/*。
如果使用/*的话,那么请求时虽然可以通过DispatcherServlet转发到对应的Action(struts)和Controller(springMVC)中,但是返回的内容如果是jsp文件的话,就会报404错误(访问不到jsp)。

补充:spring中servlet的拦截器匹配规则

1 拦截 .do、.htm, 例如:/user/add.do

这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。

2 拦截 / 例如:/user/add

可以实现现在很流行的REST风格。但是会导致静态文件(html,js,css)不能正常显示

3 拦截 / * 请求虽然可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。

controller中配置@RequestMapping的路径是不会被拦截的。配置了@RequestMapping就相当于在web.xml中注册了servlet

优化:DispatcherServlet的初始化动作,配置load-on-startup
<init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
</init-param>
       <!--让服务器启动的时候加载servlet-->
      <load-on-startup>1</load-on-startup>

问题–>配置 / 之后,静态资源无法访问,而jsp确可以访问?

tomcat中有一个专门处理jsp的servlet(org.apache.jasper.servlet.JspServlet)映射路径为*.jsp和*.jspx
tomcat/conf/web.xml文件中的配置:

    <!--配置JSPServlet(Tomcat中的web.xml配置)-->
	<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>
    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
2.SpringMVC的执行流程分析

我们在DispatcherServlet的配置文件中可以找到三个处理器的定义:

 HandlerMapping  处理器映射器(查找)
 HandlerAdapter  处理器适配器(执行)
 ViewResolver    视图解析器(解析)

在DispatcherServlet的配置文件默认加载,就有上面三个的定义。DispatcherServlet可以分为xml方式和注解方式。分别对应不同的实现类。

处理器映射器
	xml方式:BeanNameUrlHandlerMapping 处理springMVC.xml中配置的name
	注解方式:RequestMappingHandlerMapping 处理@RequestMapping 路径映射
处理器适配器
	xml方式:SimpleControllerHandlerAdapter 执行实现了Controller接口的
	注解方式:RequestMappingHandlerAdapter 执行配置@RequestMapping的方法
视图解析器:
	xml和注解的方式是一样的:
	InternalResourceViewResolver 默认的视图解析器

在这里插入图片描述

1.页面发送请求到前端控制器(DispatcherServlet)(注意:配置Spring的配置文件)
2.前端控制器请求处理器映射器(HandlerMapping)查找Handler,如果设置有拦截器,也会查找
相应的handler(xml配置和注解配置不同的实现类)
3.处理器映射器查找到,向前端控制器返回一个handler
4.前端控制器请求处理器适配器(HandlerAdapter)执行相应的handler
5.处理器适配器执行完毕之后,返回一个ModelAndView(springMVC的一个底层对象,包括Model和View)
6.前端控制器请求试图解析器(ViewResolver)根据返回ModelAndView去进行试图解析
7.试图解析器解析一个真正的view返回给前端控制器
8.前端控制器进行视图的渲染,将模型数据(ModelAndView对象里面的)填充到request域中
9.前端控制器向前端相应结果(前端的jsp可以获取存放在request域中的数据)
3. 其中涉及的组件

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。

3.1 DispatcherServlet :前端控制器

用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。

从请求离开浏览器以后,第一站到达的就是 DispatcherServlet,看名字这是一个 Servlet,通过 J2EE 的学习,我们知道 Servlet 可以拦截并处理 HTTP 请求,DispatcherServlet 会拦截所有的请求,并且将这些请求发送给 Spring MVC 控制器。

3.2 HandlerMapping:处理器映射器

HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3.3 Handler:处理器

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

3.4 HandlAdapter :处理器适配器

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

3.5 View Resolver :视图解析器

View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

3.6 View :视图

SpringMVC 框架提供了很多的 View 视图类型的支持,我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户。

四、springMVC中的注解配置

4.1 RequestMapping注解

RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系,可以作用在类和方法上

作用在方法上:直接访问即可。访问路径: test1

    /*
    * value和path属性均表示客户端访问的路径 有无/均可以
    * 都是从应用程序的根目录开始查找
    * */
    @RequestMapping(value = "/test1",params = {"uid","uname"})
    public String test1(){
        System.out.println("RequestMappingTest1 Method invoke");
        return "index";
    }

作用在类上:相当于在类所有的配置映射路径前加上一个地址,访问路径为:a/test1

@RequestMapping("/a/")
public class reuestMapping {
 @RequestMapping(value = "/test1",params = {"uid","uname"})
    public String test1(){
        System.out.println("RequestMappingTest1 Method invoke");
        return "index";
    }
}

属性

4.1.1 value属性

用于指定请求的URL,与path属性的作用是一样的。

4.1.2 method属性

用于指定请求的方式。表单提交只支持get和post 其他方式的请求 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

    @RequestMapping(value = "/test2",method = {RequestMethod.POST})
    public String test2(){
        System.out.println("RequestMappingTest2 Method invoke POST");
        return "index";
    }

注意:配置了请求方式之后,如果使用的是其他提交方式,则会出现405错误,表示提交方式不符。
补充一个:DELETE提交方式的操作:那么我们就需要手动配置一个过滤器,springMVC会对请求资源进行判断和增强
web.xml中配置:

 <!-- spring提供的用于客户端访问http协议的8种请求方式-->
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
    <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

jsp页面的表单消息:在表单中增加 一个隐藏 表单域 _method = 访问方式

<form action="/test2" method="post">
	<!---->
    <input type="hidden" name="_method" value="DELETE"/>
    <input type="submit" value="delete方式提交,返回动态资源">
</form>

controller代码:
返回jsp页面的话会报错,因为jsp不支持delete的请求方式。

JSPs only permit GET POST or HEAD
    //DELETE提交方式 大多数是ajax请求
    //提交方式为delete
    @RequestMapping(value = "/test2",method = {RequestMethod.DELETE})
    public String test3(){
        System.out.println("RequestMappingTest3 Method invoke  DELETE jsp");
        //请求转发携带了客户端上一次请求的数据,继续交给下一个资源处理
        //jsp不支持delete
        return "index";
    }
4.1.3 params属性

指定限制请求参数的条件,具体使用方式代码中很清楚的写了过程。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%  String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/";
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
请求参数和对象中的属性一致(方法中的是自定义类)
<form action="<%=basePath%>show3" method="post">
    id:<input type="text" name="id"><br>
    name:<input type="text" name="name"><br>
    password:<input type="password" name="password"><br>
    <!--date 默认的格式为yyyy/MM/dd-->
    date:<input type="text" name="date">
    <input type="submit" value="提交">
</form>
表单name属性和方法的参数名称一样
<form action="<%=basePath%>show1" method="post">
    age:<input type="text" name="age"><br>
    name:<input type="text" name="name"><br>
    sex:<input type="radio" name="sex" value="true"><input type="radio" name="sex" value="false"><br>
    <input type="submit" value="提交"><br>
</form>
表单name属性和方法的参数名称不一样
<form action="<%=basePath%>show2" method="post">
    age:<input type="text" name="uage"><br>
    name:<input type="text" name="name"><br>
    sex:<input type="radio" name="sex" value="true"><input type="radio" name="sex" value="false"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

控制层代码:

@Controller
public class paramers {
    @RequestMapping("/show1")
    public String show1(int age,String name,boolean sex){
        System.out.println(age);
        System.out.println(name);
        System.out.println(sex);
        return "index";
    }
    @RequestMapping("/show2")
    //@RequestParam可以指定请求参数的哪一项赋值给后面的方法参数
    //required属性表示后面的参数不是必须项。可以不传递
    public String show2(@RequestParam(name = "uage",required = false) Integer age, String name, @RequestParam(required = false) Boolean sex
    , Model model){
        //int类型修饰的时候,不能够默认不传参数
        //不传值默认为null,int类型不能为null 这里需要的时Interger包装类
        System.out.println(age);
        System.out.println(name);
        //boolean类修饰sex时,默认不传参数的时候为false
        //包装类Boolean修饰的时候,默认不传参数为false
        System.out.println(sex);
        //将name存储在request范围内 和之前的request.setAttribute作用一样
        model.addAttribute("name",name);
        return "index";
        /*
        * String接受的时候,如果时中文,会出现乱码的清空
        * get请求不乱码
        * spring提供了一个编码字符集过滤器,解决post方式提交中存在中文乱码的情况
        * */
    }
    @RequestMapping("/show3")
    public String show3(Account account){
        System.out.println(JSON.toJSONString(account));
        System.out.println(JSON.toJSONString(account,true));
        return "index";
    }
}
4.2 @PathVariable注解

另外一种url风格的写法:使用占位符,然后使用@PathVariable将传递的属性赋值给方法中的形参。

/*
    * 资源路径,之前,比如查找一个用户id为100的信息 /QueryInfoServlet?id=100
    * 另外一种url传参 /hello/123
    * url占位符
    * */
    @RequestMapping(value = "/test6/{uid}",params = {"uid","uname"})
    public String test6(@PathVariable("uid") Integer id){
        System.out.println("将路径传入的数字传递给方法的参数"+id);
        return "index";
    }
4.3 路径访问的问题

我们在平时可以大多数直接使用映射路径对应到Controller中@RequestMapping注解中的属性。但是当我们使用url占位符的方式传递参数的时候,又或者是类上面也是用来 @RequestMapping注解,就会出现路径访问错误,也就是常见的404错误。
在这里插入图片描述
我们在上图可以看到,当访问完成之后,页面的css属性的路径前出现了/hello(比如:@RequestMapping使用url占位符的情况,/hello/xxx,访问结束之后相对路径就会多一级hello目录)的情况。导致css文件等一些静态资源访问不到。这是因为我们在引用静态资源的时候使用的是绝对路径。

解决方案:

我们在静态资源的引用,以及资源的访问的时候,使用相对路径,就不会出错。

第一种就是我们可以使用jsp里面的el表达式:${pageContext.request.contextPath}+路径名称

第二种就是在jsp声明一个basepath

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%  String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/";
%>

然后在资源前加上<%=basePath%>即可。

4.4 SessionAttributes和SessionAttribute

@SessionAttributes用于在请求之间的HTTP Servlet会话中存储model属性。 它是类型级别的注解,用于声明特定控制器使用的会话属性。 这通常列出应透明地存储在会话中以供后续访问请求的模型属性的名称或模型属性的类型。
@SessionAttribute只是获取存储在session中的属性。

@Controller
//@RequestMapping("/a/")
@SessionAttributes("account")
public class reuestMapping {
@RequestMapping("/login")
    public void login(Account account,HttpServletResponse response) throws IOException {
        account.setId(100);
        response.getWriter().write("{success:true}");
    }
    @RequestMapping("/edit")
    public void edit(@SessionAttribute("account") Account account){
        System.out.println(account.getId()+account.getName()+account.getPassword());
        //控制层没有返回值时,默认查找方法名.jsp界面
    }
}

@SessionAttributes是类注解,用来session中存储model,@SessionAttribute是从session中获取之前存取的数据。

@SessionAttributes中绑定的model可以通过如下几个途径获取:

  • 在视图中通过request.getAttribute或session.getAttribute获取
  • 在后面请求返回的视图中通过session.getAttribute或者从model中获取
  • 自动将参数设置到后面请求所对应处理器的Model类型参数或者有@ModelAttribute注释的参数里面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值