SpringMVC学习笔记
SpringMVC
1、简介
1.1 概述
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
我们为什么要学习SpringMVC呢? Spring MVC的特点有哪些?
- 轻量级,简单易学
- 高效 , 基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕DispatcherServlet(Servlet调度器)设计。 DispatcherServlet的作用是将请求分发到不同的Controller。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁。
1.2 DispatcherServlet
DispatcherServlet(Servlet调度器)是SpringMVC架构中唯一的Servlet,它的作用就是用来调度不同的Controller(注意SpringMVC中的是controller,继承Controller接口而不是HttpServlet接口)的,最原始的项目模型,一个Servlet用来负责一类业务,当业务种类多起来之后,就需要一个中间层(DispatcherServlet)来负责调度,让用户始终只需要面对一个DispatcherServlet,而不需要考虑什么时候该调用什么Servlet的问题。
原本:
现在:
Spring的web框架围绕DispatcherServlet设计。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。 Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet的本质也是一个Servlet (它继承自HttpServlet 基类)。
SpringMVC的原理如下图所示: 当发起请求时被前置的控制器拦截到请求(其实这里我觉得它就像Filter一样,拦截所有的请求并进行相应的处理),根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,中心控制器再将结果返回给请求者。
1.3 springMVC执行流程
先让我们来回顾一下MVC三层架构:
1.什么是MVC
- MVC是模型(Model)、视图(View)、控制器(controller)的简写,是一种软件设计规范。
- MVC不是一种设计模式,MVC是一种架构模式,不同的MVC存在差异,最经典的MVC就是JavaBean+servlet+JSP的模式。
- MVC架构是为了降低视图和业务逻辑的双向耦合,使项目结构明了,便于维护。
2.MVC的结构划分
M即model是指模型,表示业务规则;V即View视图,是指用户看到并与之交互的界面;C即controller控制器,控制器接受用户的输入并调用模型和视图去完成用户的需求。
Model:
Model层主要用来负责逻辑业务,在项目结构中Model一般包括三个结构:
- pojo(数据实体类):用来定义封装的数据类型,其中实体类中的属性名一般对应数据库中表的列名。
- Dao(数据处理类):专门负责定义程序与数据库之间的操作,不包含项目的业务规则,只包含数据库的增删改查。
- Service(逻辑处理类):负责项目的逻辑业务,所有的业务规则都在Service中定义,然后根据需求调用Dao层定义好的数据库操作来处理不同的业务。
View:
View层就是向用户展示的界面,负责与用户之间的交互,接收用户的指令,返回处理的结果,在项目结构中的View对应着各个jsp和html页面。
Controller:
controller层(Servlet)负责接收用户从前端传输过来的数据,并且调用Service定义好的逻辑来处理这些数据,最后再根据处理结果,决定要返回给用户什么样的界面。
最经典的MVC架构就是JSP+servlet+javabean
3.servlet和controller
在java学习初期,很多人会认为controller就是servlet,这种说法在学习框架之前我一直以为是对的,但是在学习框架之后我发现他们并不是一个东西。
简单来说:在JSP+servlet+javabean模式中,servlet类充当的是MVC架构的Controller角色,这里的controller仅仅指的是架构,而不是具体的类。因为在学习框架之后你会发现,无论是在SpringMVC架构还是SpringBoot架构中,充当MVC架构中Controller角色的类就是controller类。
虽说servlet类和controller类扮演的角色差不多,作用也差不多,但是他们之间也有很大的区别。这里由于个人水平不够现在暂时不做过多介绍,等之后学到了再来补充
servlet类和controller类几个明显的区别:
- 继承的接口不一样,类的返回值类型也不一样
- servlet由servlet容器管理,controller由spring自己的IOC容器管理
4.SpringMVC的执行流程
结合上述内容,让我们来分析一下springMVC的执行流程:
- 用户与视图层发生交互,通过视图层向后台发送业务请求,假设用户发出的请求是http://localhost:8080/SpringMVC/hello。
- 中心控制器接收到视图层发送过来的业务请求,调用HandlerMapping。
- HandlerMapping根据传递过来的url查找到对应的handler
- HandlerExecution就是具体的handler,它的作用就是根据url查找控制器,如上请求被查找到的控制器就是hello,找到对应的控制器之后将信息返回给中心控制器。
- 中心控制器根据返回的信息,调用HandlerAdapter,让适配器按照特定的规则取执行Handler。
- Handler让具体的Controller执行
- controller调用Model层实现逻辑业务
- Model层将处理后的信息返回给controller
- controller将执行信息返回给HandlerAdapter,如ModelAndView
- HandlerAdapter将视图逻辑名或者模型传递给中心控制器
- 中心控制器根据视图名,调用视图解析器进行解析
- 视图解析器将解析后的视图路径返回给中心控制器
- 中心控制器根据解析的视图路径和模型参数,渲染出具体的视图
- 最终视图呈现给用户
2、项目构建
2.1 配置文件版
-
新建Moudle:springMVC-01-helloSpring,既然是MVC框架,首先要添加Web支持
-
导入SpringMVC依赖
<dependencies> <!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!--jsp--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <!--springMVC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.21.RELEASE</version> </dependency> </dependencies>
-
配置web.xml,在里面注册DispatcherServlet
<?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"> <!--注册dispatcherServlet--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--DispatcherServlet需要关联一个spring的配置文件,这里由于只为了理解MVC架构,命名为springmvc-servlet.xml--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> <!--所有的请求都先经过DispatcherServlet--> </servlet-mapping> </web-app>
-
在resources目录下编写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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
添加HandlerMapping、HandlerAdapter和视图解析器
<!--在springMVC的配置文件中配置号handlerMapping、handlerAdapter和视图解析器--> <!--1.handlerMapping--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!--2.handlerAdapter--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--3.视图解析器--> <!--将DispatcherServlet返回给他的ModelAndView拼接成具体的页面路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean>
-
编写controller(实现controller接口或者添加注解),controller需要返回一个ModelAndView,用来携带视图信息和数据
public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { //创建ModelAndView ModelAndView modelAndView = new ModelAndView(); //封装对象,将要传递的属性封装进去 modelAndView.addObject("msg","Hello!SpringMVC!"); //封装要跳转的视图 modelAndView.setViewName("hello"); return modelAndView; } }
-
将自己的类交给SpringIOC容器,注册bean
<!--handler--> <bean id="/hello" class="com.yirui.controller.HelloController"/>
-
写要跳转的jsp页面,显示ModelAndView存放的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
-
配置tomcat启动测试
报错了,查看日志排查原因:
- tomcat里面找不到DispatcherServlet类,而查看项目maven里面依赖已经导入,查看目录结构中也存在jar包,说明是由于编译时Aritifacts中没有导入相应jar包,打开项目结构查看情况。
- 发现Artifacts果然没有lib目录,说明jar包存在,但是没有导入到项目里面,在这里新建lib目录,将所有的jar包导入(项目中,同一个jar包不能放两个版本,不然Artifacts部署会报错)。
-
重启服务器,结果正常。如果第二步弄完之后,报了
org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET ...
错误,有可能是DispatcherServlet里面的错误,比如:<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> <!--所有的请求都先经过DispatcherServlet,注意这里不能用/*--> </servlet-mapping>
原因:
< url-pattern > / </ url-pattern > 不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类 。 < url-pattern > /* </ url-pattern > 会匹配*.jsp。/,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
2.2 注解版
上面的配置文件版,只是为了更好的理解SpringMVC运行的原理,实际开发一般使用注解。
-
新建module,添加web框架支持,pom.xml中导入maven依赖
-
将jar包导入Artifacts
-
配置web.xml,注册dispatcherServlet,关联spring的配置文件
-
resources目录下编写spring配置文件
<?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.yirui.controller"/> <!--让SpringMVC不处理静态资源,例如 .css .js .html .html .mp3 .mp4等后缀的文件--> <mvc:default-servlet-handler/> <!--mvc注解驱动,加了之后会配置并注入handlerMapping和handlerAdapter 在spring中一般采用@RequestMapping注解来完成映射关系。 --> <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和jsp页面
HelloController.java
package com.yirui.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller//使用这个注解返回的数据如果是字符串会自动被视图解析器处理,拼接前缀后缀,因此这里的返回值直接就是要跳转的页面名称 public class HelloConller { @RequestMapping("/hello")//指定调用该方法的请求路径,即调用该方法的真实访问地址 public String hello(Model model){ model.addAttribute("msg","hello!springMVC-annotation");//通过model向模型中封装数据,可以在jsp页面中取出并渲染 return "hello"; } }
hello.jsp
<%-- Created by IntelliJ IDEA. User: yirui Date: 2022/7/12 Time: 15:09 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>