SpringMVC初级

文章目录

一、SpringMVC 概述

Spring MVC是Spring Framework提供的Web组件,全称是Spring Web MVC,是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能

Java Web开发者必须要掌握的技术框架

三层架构

  • 表现层:负责数据展示

  • 业务层:负责业务处理

  • 数据层:负责数据操作

概念

  • Spring MVC 是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。

  • MVC(Model View Controller),一种用于设计创建Web应用程序表现层的模式

    • Model(模型):数据模型,用于封装数据
    • View(视图):页面视图,用于展示数据
    • Controller(Handle 处理器):处理用户交互的调度器,用于根据用户需求处理程序逻辑

二、springMVC步骤

1、新建maven的web项目

2、导入maven依赖

java 复制代码
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

<!-- servlet3.1规范的坐标 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.1</version>
  <scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
<!--spring web的坐标-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>

3、创建controller

java 复制代码
@Controller
public class UserController {
    @RequestMapping("/save")
    public String say(){
        System.out.println("你好");
        return "a.jsp";
    }
}

4、创建spring-mvc.xml配置文件(本质就是spring的配置件)

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <context:component-scan base-package="com.xinzhi"/>
</beans>

5、web.xml中配置前端控制器

java 复制代码
<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*:spring-mvc.xml</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>DispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

6、新建a.jsp文件

7、配置tomcat

8、启动测试

注意如果报找不到包: mvn idea:module

三、工作流程分析

  • 服务器启动
    1. 加载web.xml中DispatcherServlet
    2. 读取spring-mvc.xml中的配置,加载所有com.xinzhi包中所有标记为bean的类
    3. 读取bean中方法上方标注@RequestMapping的内容
  • 处理请求
    1. DispatcherServlet配置拦截所有请求 /
    2. 使用请求路径与所有加载的@RequestMapping的内容进行比对
    3. 执行对应的方法
    4. 根据方法的返回值在webapp目录中查找对应的页面并展示
  • web三大组件有 处理器映射,处理器适配器, 视图解析器

    1 dispatcherServlet 前置控制器,负责接收并处理所有的web请求,根据handlerMapping(处理器映射)找到具体的Controller(处理器),由controller完成具体的处理逻辑。

    2 HandlerMapping(处理器映射器):负责处理web请求和具体的Controller之间的映射关系匹配。

    3HandlerAdapter(处理器适配器) 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 主要处理方法参数、相关注解、数据绑定、消息转换、返回值、调用视图解析器等等。

    4.Controller(处理器):DispatherServlet的次级控制器,web请求的具体处理者。DispatherServlet获得handlerMapping的返回结果后,调用controller的处理方法处理当前的业务请求,处理完成后返回ModelAndView对象。

    5 ViewResolver( 视图解析器):用来处理视图名与具体的view实例之间的映射对应关系。根据ModelAndView中的视图名查找相应的View实现类,然后将查找的结果返回给DispatcherServlet,DispatcherServlet最终会将ModelAndView中的模型数据交给返回的View处理最终的视图渲染工作。

Springmvc架构原理解析

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找

第三步:处理器映射器HandlerMapping向前端控制器返回Handler

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView

ModelAndView是springmvc框架的一个底层对象,包括 Model和view

第八步:前端控制器请求视图解析器去进行视图解析

根据逻辑视图名解析成真正的视图(jsp)

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染

视图渲染将模型数据(在ModelAndView对象中)填充到request域

第十一步:前端控制器向用户响应结果

四、请求参数的绑定

  1. 默认类型:

​ 直接放在参数上就可以使用的数据,HttpServletRequest

  1. 简单类型:

​ 直接将简单类型的数据放在方法里,如果前端参数和后端参数名字一样,自动匹配;

​ 名字不一样:@RequsetParam("前端的值") 就可以将前传的值和后端参数映射

  1. 对象 :

​ 前端的参数要和对象的属性名称必须一致,会自动封装。

  1. 对象嵌套:

​ 参数和对象的属性名称一致,前端参数对象子属性必须(子对象.属性)

  1. 自定义数据的绑定

​ 5.1 编写转换器类,作用是将前端的数据类型转换成后端的数据类型,继承converter

​ 5.2 配置文件中,添加转化器驱动

  1. 数组

​ 前端数组中是简单类型的数据,那么前端数组中的name要和后端数组名称一致

  1. 集合

​ 后端接受的对象是含有List<对象>属性的,那么前端的name值格式要和后端list属性名称一致,而且用索引的格式 list0.属性(list集合里对象的属性名称)

java 复制代码
# 1 默认类型:
 @RequestMapping("/m1")
    public ModelAndView say(HttpServletRequest request, ModelAndView modelAndView){
        String name = request.getParameter("name");
        System.out.println(name);
        modelAndView.setViewName("a.jsp");
        modelAndView.addObject("name", name);
        return modelAndView;
}

jsp页面中要引入
<%@ page isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<body>
   ${name}
</body>

# 简单类型:  
@RequestMapping("/m2")
    public String say2(@RequestParam("username") String name, String age){
        System.out.println(name);
        System.out.println(age);
        return "a.jsp";
}

# 对象类型
@RequestMapping("/m3")
    public String say3(Student student){
        System.out.println(student);
        return "b.jsp";
}
访问路径: http://localhost:8080/webdemo_war/m4?id=1&num=111122223333&user.age=11&user.username=%E9%9F%A9%E5%93%A5%E5%93%A5

# 数组
 @RequestMapping("/m5")
    public String say5(Integer[] ids){
        if(ids!=null){
            for (Integer id : ids) {
                System.out.println(id);
            }
        }
        return "b.jsp";
    }
访问路径:http://localhost:8080/webdemo_war/m5?ids=1&ids=2

# list类型
 @RequestMapping("/m6")
    public String say6(@RequestParam("hobby")List<String> hobby){
        System.out.println(hobby);
        return "b.jsp";
}
http://localhost:8080/webdemo_war/m6?hobby=%E6%B8%B8%E6%B3%B3&hobby=%E6%B3%A1%E5%A6%9E
注意: SpringMVC默认将List作为对象处理,赋值前先创建对象,然后将hobby作为对象的属性进行处理。由于
List是接口,无法创建对象,报无法找到构造方法异常;修复类型为可创建对象的ArrayList类型后,对象可
以创建,但没有hobby属性,因此数据为空。此时需要告知SpringMVC的处理器hobby是一组数据,而不是一个单
一数据。通过@RequestParam注解,将数量大于1个names参数打包成参数数组后, SpringMVC才能识别该数
据格式,并判定形参类型是否为数组或集合,并按数组或集合对象的形式操作数据。 
  • 自定义数据绑定

    • 定义转换器
    java 复制代码
    public class MyDateConverter implements Converter<String, Date> {
    
        @Override
        public Date convert(String s) {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            Date date = null;
            try {
                date = df.parse(s);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    • 注解驱动,使转换器起作用
    java 复制代码
    <!--1.将自定义Converter注册为Bean,受SpringMVC管理-->
    <bean id="myDateConverter" class="com.xinzhi.converter.MyDateConverter"/>
    <!--2.设定自定义Converter服务bean-->
    <bean id="conversionService"
          class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--3.注入所有的自定义Converter,该设定使用的是同类型覆盖的思想-->
        <property name="converters">
            <!--4.set保障同类型转换器仅保留一个,去重规则以Converter<S,T>的泛型为准-->
            <set>
                <!--5.具体的类型转换器-->
                <ref bean="myDateConverter"/>
            </set>
        </property>
    </bean>
    <!--开启注解驱动,加载自定义格式化转换器对应的类型转换服务-->
    <mvc:annotation-driven conversion-service="conversionService"/>

五、响应

1、转发(默认)

  • controller返回值是String return "forward:page.jsp";

2、重定向

  • controller返回值是String return "redirect:page.jsp";

3、配置视图解析器

java 复制代码
# 展示页面的保存位置通常固定,且结构相似,可以设定通用的访问路径,简化页面配置格式
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/page/"/>
    <property name="suffix" value=".jsp"/>
</bean>

六、拦截器

1、拦截器( Interceptor)

  • 是一种动态拦截方法调用的机制
java 复制代码
# 作用:
	1. 在指定的方法调用前后执行预先设定后的的代码
	2. 阻止原始方法的执行

# 核心原理: 
	AOP思想
# 拦截器链:
	多个拦截器按照一定的顺序,对原始被调用功能进行增强  

2、拦截器使用步骤

2.1、实现HandlerInterceptor接口

java 复制代码
/**
 *  三个方法的运行顺序为    preHandle -> postHandle -> afterCompletion
 *  如果preHandle返回值为false,三个方法仅运行preHandle
 */
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("前置运行----a1");
        //返回值为false将拦截原始处理器的运行
        //如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("后置运行----b1");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("完成运行----c1");
    }
}

2.2、配置拦截器

xml 复制代码
  <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/m8"/>
            <bean class="com.xinzhi.intercepter.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

2.3、拦截器配置项

xml 复制代码
<mvc:interceptors>
    <!--开启具体的拦截器的使用,可以配置多个-->
    <mvc:interceptor>
        <!--设置拦截器的拦截路径,支持*通配-->
        <!--/**         表示拦截所有映射-->
        <!--/*          表示拦截所有/开头的映射-->
        <!--/user/*     表示拦截所有/user/开头的映射-->
        <!--/user/add*  表示拦截所有/user/开头,且具体映射名称以add开头的映射-->
        <!--/user/*All  表示拦截所有/user/开头,且具体映射名称以All结尾的映射-->
        <mvc:mapping path="/*"/>
        <mvc:mapping path="/**"/>
        <mvc:mapping path="/handleRun*"/>
        <!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的-->
        <mvc:exclude-mapping path="/b*"/>
        <!--指定具体的拦截器类-->
        <bean class="MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

2.4、拦截器的使用场景

1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。

2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;

3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);

2.5、拦截器登录案例

java 复制代码
@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        String uri = request.getRequestURI();
        if(uri.equal("/login")){
            return true;
        }
        HttpSession session = request.getSession();
        Object user = session.getAttribute("USER_SESSION");
        if(user!=null){
            return true;
        }
        request.setAttribute("msg","未登陆状态");
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }

<mvc:interceptors>
    	<mvc:interceptor>
        	<mvc:mapping path="/**" />
        	<bean class="com.soft.interceptor.LoginInterceptor" />
    	</mvc:interceptor>
</mvc:interceptors>	

七、文件上传

1、导入maven依赖

commons-fileupload commons-fileupload 1.4

2、前端页面

xml 复制代码
<form action="/fileupload" method="post" enctype="multipart/form-data">
    上传LOGO: <input type="file" name="file"/><br/>
    <input type="submit" value="上传"/>
</form>

3、配置多媒体解析器

xml 复制代码
<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>

4、后台代码

java 复制代码
package com.xinzhi.controller;

import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.UUID;

@Controller
public class FileUploadController {

    /**
     * 上传页面跳转
     * @return
     */
    @RequestMapping(value = "/upload",method = RequestMethod.GET)
    public String upload(){
        return "fileUpload";
    }

    /**
     * 下载页面跳转
     * @return
     */
    @RequestMapping(value = "/down",method = RequestMethod.GET)
    public String down(){
        return "download";
    }

    /**
     * 上传逻辑
     * @param uploadfile 上传的文件数组
     * @return
     */
    @RequestMapping(value = "/fileUpload",method = RequestMethod.POST)
    public String uploadFile(MultipartFile[] uploadfile){
        for (MultipartFile file : uploadfile) {
            //获取文件名称
            String filename = file.getOriginalFilename();
            //存在服务器上名称的修改
            filename = UUID.randomUUID()+"_"+filename;
            // 定义服务器上的存储路径
            String dirPath = "C:/file/";
            File filePath = new File(dirPath);
            // 判断路径是否存在,不存在就创建
            if(!filePath.exists()){
                filePath.mkdir();
            }
            try {
                // 文件上传的核心
                file.transferTo(new File(dirPath+filename));
            } catch (IOException e) {
                e.printStackTrace();
                return "error";
            }
        }
        return "success";
    }


    @RequestMapping("/download")
    public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,String filename) throws UnsupportedEncodingException {
        //指定文件下载地址的目录   filename -> 美女.jpg
        String dirPath = "F:/file";
        // 指定下载的文件名称
        File file = new File(dirPath + File.separator + filename);
        HttpHeaders headers = new HttpHeaders();
        // 解决不同浏览器之间乱码问题
        filename = getFilename(request, filename);
        //告诉浏览器,打开方式(附件)
        headers.setContentDispositionFormData("attachment",filename);
        //以二进制字节流的方式下载
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseEntity<>(e.getMessage().getBytes(), HttpStatus.EXPECTATION_FAILED);
        }
    }

    /**
     * 不同浏览器的版本兼容
     * @param request
     * @param filename
     * @return
     * @throws UnsupportedEncodingException
     */
    private String getFilename(HttpServletRequest request,String filename) throws UnsupportedEncodingException {
        //判断是不是IE内核的关键字
        String[] IEBrowerKeyWords = {"MSIE","Trident","Edge"};
        String keywords = request.getHeader("User-Agent");
        for (String keyWord : IEBrowerKeyWords) {
            if(keywords.contains(keyWord)){  //判断是否为IE浏览器
                return URLEncoder.encode(filename,"UTF-8");
            }
        }
        // 其他浏览器编码格式ISO-8859-1
        return new String(filename.getBytes("UTF-8"),"ISO-8859-1");
    }
}

5、前端代码使用现成的。如果jquery不能用了,可以引用网络地址

java 复制代码
<script src="http://code.jquery.com/jquery-latest.js"></script>
相关推荐
Scott9999HH2 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔2 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社3 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海3 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖4 小时前
JDK 26 新特性详解
java·开发语言
马优晨5 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大7 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json