SpringMVC
# SpringMVC
Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet(底层运行)
Spring MVC 分工明细 ,和框架无缝结合,采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性 和灵活性
MVC设计模式了解:JavaWeb学习记录 MVC与三层的设计模式_ (opens new window)
SpringMVC优势
- 轻量级,不依赖特性接口 和 类
- 分工明确,实现前后端分离
- Spring的一部分 可以 应用 Ioc 和 Aop
- 国际化支持
- 面向接口编程
# 首次应用
添加依赖
<!-- web项目-->
<packaging>war</packaging>
<dependencies>
<!-- spring-webmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- springmvc底层 servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<!--插件运行的时候没有范围插件启动会失败-->
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<!--jdk版本-->
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
业务类及控制器
UserService
类 add业务
@Service
public class UserService {
public void add() {
System.out.println("UserService---add---");
}
}
UserController
类 控制器
//@Controller注解 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean
@Controller
public class UserController {
@Autowired
private UserService userService;
//注解值 url请求 后缀
@RequestMapping("/hello.do")
public ModelAndView add(){
System.out.println("UserController---add---");
userService.add();
//用于响应
ModelAndView mv = new ModelAndView();
//响应至 前端指定变量
//相当于 request.setAttrubuite("userName","Sanscan12")
mv.addObject("userName","Sanscan12");
//经过springmvc的视图解析器处理,转换成物理资源路径(配置跳转页面设置)
//相当于 request.getRequestDispatcher("index.jsp").forward(); (跳转页面)
mv.setViewName("index");
//经过InternalResourceViewResolver对象的处理之后加上前后缀就变为了/index.jsp
return mv;
}
}
建立 配置文件(resources目录下)
Spring容器 applicationContext.xml
配置文件
<?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.service,com.dao"/>
</beans>
SpringMVC容器 springmvc.xml
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<context:component-scan base-package="com.controller"/>
<!-- 分发用户请求-->
<mvc:annotation-driven/>
<!-- 视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
配置 web.xml
文件 (web目录下)
<?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">
<!-- spring配置-->
<context-param>
<!--contextConfigLocation:用于加载bean的配置文件-->
<param-name>contextConfigLocation</param-name>
<!--指定spring配置文件的位置 -->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Springmvc配置-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<!--前端控制器的完全限定名,在spring-webmvc-5.2.5.RELEASE.jar包中的 org.springframework.web.servlet下-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 创建前端控制器时会读取springmvc配置文件启动ioc容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--指定的配置文件。如果没有将会在 WEB-INF目录下 找 servlet.xml -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--Tomcat启动 load-on-startup
标记是否在Web服务器(Tomcat)启动时会创建 Servlet 实例
通过数值进行配置优先级
number > 0 初始化加载就创建
number < 0 Servlet应用时创建
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置拦截路径url,所有以.do结尾的请求都会被前端控制器拦截处理 -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
注意:
如果没有配置,启用默认的规则:即如果配置文件放在 webapp/WEB-INF/ 目录下,并且配置文 件的名字等于 DispatcherServlet 的名字 + -servlet(即这里的配置文件路径是 web/WEB-INF/dispatcherServlet-servlet.xml),如果是这样的话,可以不用添加 init-param 参数,即不用手动配置 springmvc 的配置文件,框架会自动加载。
而一般情况下,配置文件是放在类路径下,即 resources 目录下。所以,在注册前端控制器时, 还需要设置查找 SpringMVC 配置文件路径。
其中contextConfigLocation属性:来自DispatcherServlet的父类FrameworkServlet, 该类中的contextConfigLocation属性用来配置springmvc的路径和名称。
web文件夹下创建 页面
index.jsp
页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h1>index---------------${userName}</h1>
</body>
</html>
<%-- 执行结果 访问页:http://localhost:8088/hello.do
index---------------Sanscan12
--%>
说明
项目中有 Spring 和 SpringMVC 两个容器
Spring 容器通过 ContextLoaderListener 加载
SpringMVC 容器通过 DispatcherServlet 加载
ContextLoaderListener 初始化的上下文加载的 Bean 是对于整个应用程序共享的,一般 dao层、service层 的bean
DispatcherServlet 初始化的上下文加载的 Bean 是只对 Spring Web MVC 有效的 bean,如 Controller、HandlerMapping、HandlerAdapter 等等,该初始化上下文应该只加载 Web相关组件
# 工作流程
步骤详细说明:
- 用户点击某个请求路径,发起一个 HTTP request请求,该请求会被提交到 DispatcherServlet(前端控制器)
- 由 DispatcherServlet 请求查找Handler 一个或多个 HandlerMapping(处理器映射器)
- 每个HandlerMapping响应 返回一个执行链(HandlerExecutionChain)
- DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器)
- HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller)
- Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息)
- HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet
- DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析
- ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet
- DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图)
- 视图负责将结果显示到浏览器(客户端)
面试口诀
口诀 : 前映配处析视图
1前端控制器 2处理器映射器 3处理器适配器 4处理器 5视图解析器 6视图
顺序 : 1 -> 2 -> 1 -> 3 -> 4 -> 1 -> 5 -> 1 -> 6
# 组件
DispatcherServlet (前端控制器/中央控制器/核心控制器)
DispatcherServlet 相当于是 SpringMVC 的大脑,由它调用其它组件处理用户的请求,它降低组件之间的耦合性,有利组件之间的拓展。SpringMVC框架提供的该核心控制器需要在
web.xml
文件中配置HandlerMapping (处理器映射器)
HandlerMapping也是控制器,派发请求的控制器。该控制器无需自行控制。其作用是根据请求的 URL 路径,通过 注解 或者 XML 配置,寻找匹配的处理器(Handler)信息。在实际开发中,通常使用注解方式
Handler (处理器)
Handler 和 Java Servlet 扮演的角色一致。其作用是执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至 ModelAndView 对象中。由于 Handler 涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发 Handler
HandlAdapter (处理器适配器)
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过映射器找到的处理器(Handler)信息,按照特定规则执行相关的处理器(Handler),可支持多种 类型的处理器,调用处理器传递参数等工作
View Resolver (视图解析器)
ViewResolver 负责将处理结果生成 View 视图,通过 ModelAndView 对象中的 View 信息将逻辑视图名解析成真正的视图 View(如通过一个 JSP 路径返回一个真正的 JSP 页面),最后对 View 进行渲染将处理结果通过页面展示给用户。 SpringMVC 框架提供了很多的 View 视图类型,包括:jstlView、freemarkerView、pdfView 等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务 需求开发具体的页面
View (视图)
其本身是一个接口,实现类支持不同的 View 类型(JSP、FreeMarker、Excel 等)
# 注解应用
Spring 2.5 版本新增了 SpringMVC 注解功能,用于替换传统的基于 XML 的 SpringMVC 配置
SpringMVC最重要的两个注解类型 @Controller
和 @RequestMapping
注解
使用基于注解的控制器具有以下优点:
- 在基于注解的控制器类中可以编写多个处理方法,从而处理多个请求,这就允许将相关的操作编写在同一个控制器类中,减少控制器类的数量,方便以后维护
- 基于注解的控制器不需要在配置文件中部署映射,仅需要使用 @RequestMapping 注解一个方法进行请求处理即可
# @Controller
@Controller 注解用于声明某类的实例是一个控制器
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
注解应用前提 需要被 Spring 扫描机制 扫描到才能用!!!
# @RequestMapping
@RequestMapping 注解定义了处理器对于请求的映射规则
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
//用于 类 或 方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
//注释使用
String name() default "";
//默认值(可省略),指定访问的URL路径后缀
//如:@RequestMapping(value="/User/UserAdd.do") 表示 http://localhost:8080/User/UserAdd.do
@AliasFor("path")
String[] value() default {};
//和 value 属性都用来作为映射使用
@AliasFor("value")
String[] path() default {};
//该方法支持哪些 HTTP 请求,如果省略则支持所有HTTP类型的请求 (GET、POST、...)
//如 :@RequestMapping(value = "User",method = {RequestMethod.GET,RequestMethod.POST})
RequestMethod[] method() default {};
//指定请求中规定的参数,请求中必须包含 指定参数 时才能执行该请求
//如:@RequestMapping(value = "User",params = "type") => http://localhost:8080/toUser?type=xxx
String[] params() default {};
//请求中必须包含某些指定的 header 值,请求中必须包含 指定的请求头 才能执行该请求
//如:@RequestMapping(value = "User",headers = "Referer=http://www.xxx.com") => http://www.xxx.com
String[] headers() default {};
//指定处理请求的提交内容类型 (application/json、text/html)
//如:@RequestMapping(value = "User",consumes = "application/json")
String[] consumes() default {};
//指定返回的内容类型,请求的返回类型 必须包含 指定的请求头 才能执行该请求(也可设置编码返回类型)
//如:@RequestMapping(value = "User",produces = "application/json,charset=utf-8")
String[] produces() default {};
}
该注解声明于 方法 或 类 上
声明注解说明
方法:请求的 URI 路径后缀
类:当前类中的所有响应请求的方法都以该地址作为父路径
通过以上第一次用的代码进而修改实例:(常用示例)
UserController类
package com.controller;
import com.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//访问:http://localhost:8080/user/hello.do
@RequestMapping("hello.do")
public ModelAndView add(){
System.out.println("UserController---add---");
userService.add();
ModelAndView mv = new ModelAndView();
mv.addObject("userName","Sanscan12");
mv.setViewName("index");
return mv;
}
/*
访问不同资源路径
地址访问:http://localhost:8080/ => 控件测试
物理资源路径:/test/update.jsp
*/
@RequestMapping("update.do")
public ModelAndView update() {
System.out.println("UserController---update---");
ModelAndView mv = new ModelAndView();
mv.setViewName("test/update");
return mv;
}
/*
不同类型请求测试
地址访问:http://localhost:8080/user/delete.jsp
物理资源路径:/test/delete.jsp
*/
@RequestMapping(value = "delete.do",method = {RequestMethod.POST})
public ModelAndView delete() {
System.out.println("UserController---delete---");
ModelAndView mv = new ModelAndView();
mv.setViewName("test/update");
return mv;
}
}
index.jsp
页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<p>测试访问</p>
<a href="/user/delete.do">/user/delete.do(GET)</a>
<form action="/user/delete.do" accept-charset="UTF-8" method="post">
<button type="submit">/user/delete.do (POST)</button>
</form>
</body>
</html>
<%--
a标签访问会异常,因请求类型被限制!
--%>
# SpringMVC问题
# Tomcat 首次访问 index 404
原因
wab目录 未被idea识别为web项目
编译器输出路径 未同步 target缓存,导致
web.xml
找不到在
web.xml
配置。拦截路径url,所有请求都会被前端控制器拦截处理设置(静态文件的加载也会拦截)···· <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> ····
配置文件
springmvc.xml
未进行 静态资源处理,导致找不到资源DispatcherServlet找不到url。未加载到 配置文件 所配置的静态资源(如:spring.xml、springmvc.xml等...)
解决方案
恢复项目结构。点击链接恢复说明:Web项目未被idea识别 (opens new window)
刷新项目工件
编译器编辑输出,设置说明如下图:
避免拦截。修改
url-patternj节点
。(如:==.do==、==do/==)静态资源处理。添加扫描处理,点击链接配置说明:SpringMVC 访问静态资源 (opens new window)
在 pom.xml 中的插件配置模块(主要指定加载目录) 在
directory节点
写上自己配置的资源路径<build> <resources> <resource> <!--配置文件加载目录--> <directory>src/main/resources</directory> <includes> <!-- 扫描文件对应的后缀 .properties 、 .xml 未扫描,则无法 加载配置文件 --> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> ····· </build>