[TOC]
- SpringMVC:Spring web mvc:是属于表现层的框架:是spring框架的一部分:发布于Spring3.0后
- Spring由四大部分组成:IoC容器(Core),AOP,SpringDao(DAO+ORM),Web(JavaEE+Web)
-
首先创建一个web项目
-
web项目依赖servlet-api-3.1.0.jar与jsp-api-2.3.1.jar
-
maven:
-
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> <scope>provided</scope> </dependency>
-
项目结构:
project
| ------ src
| ------ main
| ------ java 源程序文件夹,java程序在此构建
| ------ resources 资源文件夹,资源的classpath
| ------ webapp web应用文件夹
| ------ WEB-INF
| ------ web.xml web配置文件夹,主要配置servlet
| ------ index.jsp
| --- target ctrl+shift+alt+s → project → project cpmpiler output 输出文件夹
| --- pom.xml
-
pom.xml中添加build标签,子标签plugins,添加插件
-
<build> <plugins> <!--maven jdk 1.8--> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!--servlet容器 jetty插件--> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.8.v20171121</version> </plugin> </plugins> </build>
-
-
web.xml的头,目前是:
-
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> </web-app>
-
-
ctrl+shift+alt+s → 配置Artifacts, 名称为项目名:war exploded 构建输出目录为target\项目名
-
tomcat配置页中的On 'Update' action 为Update classes and resources
-
除之前的spring的jar包外,增加两个jar包
-
spring-context-support:包含支持UI模板,邮件服务,缓存等方面的类
-
spring-webmvc:对spring-mvc的实现
-
maven:
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.2.RELEASE</version> </dependency>
- 在web.xml中注册**调度器,是一个servlet
<!--**调度器-->
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--spring-context设置-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--随服务器启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
-
全限定性类名
- 该**调度器为一个Servlet,名称为DispatcherServlet
- 全类名:org.springframework.web.servlet.DispatcherServlet
-
<load-on-startup/>标签
- 标记此servlet是否在tomcat服务器启动时创建实例
- 大于等于0时为容器启动时加载并初始化,数值越小优先级就越高
- 小于0或没指定时,当使用servlet时才创建实例
- 当两个servlet的load on startup值相同时,容器自动选择顺序
-
<url-pattern/>标签
- 对于<url-pattern/> ,不能写为/*,最好也不要写为/,建议写为*.do 的形式。详见 1.4 再解<url-pattern/>
-
配置文件与名称
-
为**调度器指定spring-context的配置文件路径,一般在resources的根目录下
-
<init-param> <!--spring-context设置--> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param>
-
-
可任意命名,一般在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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>
-
处理器需要实现Controller接口
-
public class MyController implements Controller { @Override public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("welcome", "hello spring mvc world"); modelAndView.setViewName("/WEB-INF/jsp/welcome.jsp"); return modelAndView; } }
-
ModelAndView类中的addObject()方法用于向model中添加数据;Model中可连续添加多个数据
- Model底层是一个Map
- addObject()方法的返回值是ModelAndView;
- addlAllObjects(Map<String,?> modelMap>方法:添加modelMap中的数据Map集合
-
在 springmvc.xml 中注册处理器。不过,需要注意处理器的 id 属性值为一个请求 URI。
- 当客户端提交该请求时,会访问 class 指定的这个处理器
-
<!--注册处理器,id为一个请求URI--> <!--当客户端提交此id的请求时,会访问这个指定的处理器--> <bean class="com.sqm.servlet.handler.MyController" id="/hello.do"/>
### 1.2.6 定义目标页面
- 主页放置链接:`<a href="hello.do">welcome</a>`
- 将modelAndView中的welcome的数据接收
- ```jsp
<body>
服务端信息:${welcome}
</body>
```
### 1.2.7 视图解析器的注册
- 视图解析器:InternalResouceViewResolver
- SpringMVC 框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器引入了**请求的前辍与后辍的属性**
- ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会**自动完成拼接**
- 内部资源视图解析器:org.springframework.web.servlet.view.InternalResourceViewResolver
- ```xml
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
-
将modelAndView中的ViewName设置为"welcome",与视图解析器拼接即可
-
modelAndView.setViewName("welcome");
- 浏览器提交请求到**调度器
- **调度器直接将请求转给处理器映射器
- 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给**调度器
- **调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器
- 处理器适配器调用执行处理器
- 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器
- 处理器适配器直接将结果返回给**调度器
- **调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象
- 视图解析器将封装了的视图对象返回给**调度器
- **调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象
- **调度器响应浏览器
- DispatcherServlet
- **调度器,也称为前端控制器,在 MVC 架构模式中充当控制器 Controller
- DispatcherServlet是整个流程的控制中心
- 由它调用诸如处理器映射器、处理器适配器、视图解析器等其它组件处理用户请求
- **调度器的存在降低了组件之间的耦合度
- HandlerMapping
- 处理器映射器,负责根据用户请求找到相应的将要执行的 Handler,即处理器
- 用于完成将用户请求映射为要处理该请求的处理器,并将处理器封装为处理器执行链传给**调度器
- HandlAdapter
- 处理器适配器,通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。**调度器会根据不同的处理器自动为处理器选择适配器,以执行处理器
- Handler
- 处理器,也称为后端控制器,在 DispatcherServlet 的控制下 Handler 调用 Service层对具体的用请求进行处理。
- 由于 Handler 涉及到具体的用户业务请求,所以一般情况下需要程序员根据业务需求己开发Handler
- ViewResolver
- 视图解析器,负责将处理结果生成 View 视图,ViewResolver 首先将逻辑视图名解析为物理视图名,即具体的页面地址,再生成 View 视图对象。最后将处理结果通过页面形式展示给用户
- SpringMVC框架提供了很多的 View 视图类型,包括:JstlView、RedirectView 等
-
SpringMVC组件的默认配置:
-
组件名称 默认值 处理器映射器 BeanNameUrlHandlerMapping
DeafultAnnotationHandlerMapping处理器适配器 HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
AnnotationMethodHandlerAdapter视图解析器 InternalResourceViewResolver
- 建议写为*.do形式
- 在没有特殊要求的情况下,SpringMVC 的**调度器 DispatcherServlet 的<url-pattern/> 常使用后辍匹配方式是写为*.do
- 不能写为/*
- 导致所有的服务器请求都会被**调度器当作controller请求
- 最好也不要写为/
- DispatcherServlet会向静态资源获取请求作为一个普通controller请求
- 配置流程:
- 经过配置后,当**控制器的url-pattern设为/后,仍然能访问静态资源
-
使用Tomcat中名为default的Servlet
-
使用tomcat的默认web.xml,的DefaultServlet,servlet名为default
-
用于处理各种静态资源访问
-
直接在自己项目的web.xml中设置default的servlet-mapping,将静态资源的后缀与其关联
-
<!--静态资源与defaultServlet对应--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping>
-
-
使用 <mvc:default-servlet-handler/>
-
在springmvc配置文件中加入<mvc:default-servlet-handler>标签
-
此标签会将静态资源的访问请求添加到SimpleUrlHandlerMapping的urlMap中,key是请求的URI,value为默认Servlet请求处理器对象DefaultServletHttpRequestHandler,此handler调用了defaultServlet来处理静态资源的访问
-
<!--添加默认静态访问资源servlet--> <mvc:default-servlet-handler/>
- 使用\<mvc:...>标签,会添加spring-mvc约束 - ```xml xmlns:mvc="http://www.springframework.org/schema/mvc" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
-
-
使用 <mvc:resources/>
-
spirng-3.0.4后,spring中定义了专门处理静态资源访问请求的处理器ResourceHttpRequestHandler
-
并且添加了**<mvc:resources/>标签**,专门用于解决静态资源无法访问的问题
-
spring配置如下:
-
<!--springmvc处理静态资源--> <mvc:resources mapping="/images/**" location="/images/"/>
- mapping:表示对目录的请求,要在此目录下一级加两个星号 - loaction:表示静态资源所在目录,这里的目录可以包含WEB-INF下的目录 - 该配置会把静态资源的访问请求添加到SimpleUrlHandlerMapping的**urlMap**中,key是请求所匹配的uri,value为静态资源处理器对象ResourceHttpRequestHandler
-
-
在index.jsp添加一个超链接,跳转到hello.do
-
在超链接中无斜杠开头,而在springmvc.xml中注册id时,其id属性值是斜杠开头:"/hello.do"
-
<a href="hello.do">welcome</a>
### 1.5.1 访问路径与资源名称
- URL:Uniform Resource Locator:统一资源定位地址:由**访问路径**与**资源名称**组成
- 资源名称指的是要访问资源的直接名称,如welcome.jsp,或要访问资源映射的名称,如servlet的名称,handler的注册名称show.do
- 访问路径:定位到资源名称的路径
- 在URL中,访问路径为最后一个斜杠之前(包括斜杠),资源名称为做后一个斜杠之后的内容
- http://localhost/springmvc/ --- 为访问路径
- index.jsp --- 为资源名称
- 根据**访问路径是否可以独立完成资源的准确定位**,可将访问路径分为绝对路径和相对路径
### 1.5.2 绝对路径
- 绝对路径:根据给出的访问路径加资源名称可以准确定位到资源的路径
- 计算机中:web应用的绝对路径,指带访问协议的路径
- 如http://localhost/springmvc/index.jsp
### 1.5.3 相对路径
- 相对路径:仅仅根据访问路径与资源名称无法准确定位的路径;**必须要给出参照路径才可以准确定位资源**;参照路径不同,定位的绝对路径也不同
- 在web应用中,相对路径有两种写法,以斜杠开头和不以斜杠开头;这两种的相对路径的参照路径是不同的
1. 以斜杠开头的相对路径
- 根据路径所在文件所处的位置分为两种:
1. 前台路径:
- 由**浏览器解析执行的代码**中所包含的路径;如html,css,js中的路径,与jsp中的静态部分的路径,如\<img src=""/>,\<a href=""/>,\<form action="">\</form>等;css中background:img("");js中window.lacation.href=""等
- 前台路径的参照路径是web服务器的根路径:http://127.0.0.1:8080/
2. 后台路径
- 由**服务器解析执行的代码及文件**所包含的路径;如java代码中的路径,jsp动态部分(java动态代码块)中的路径,xml文件中的路径(xml文件被java代码加载到内存并由java代码解析)
- 后台路径的参照路径是web应用的根路径:http://127.0.0.1:8080/springmvc/
3. 后台路径特例
- 当代码中使用response的sendRedirect()方法进行重定向时,其参照路径时web服务器的根路径;重定向时,要么写无斜杠的相对路径`response.sendRedirect("welcome.jsp");`要么**写在路径上添加项目名称(推荐)**`response.sendRedirect(request.getContextPath + "/show.jsp"); `
2. 不以斜杠开头的相对路径
- 无论是前台路径还是后台路径,其参照路径都是**当前资源的访问路径**(即在浏览器浏览时访问路径),而不是当前资源的保存路径
### 1.5.4 相关现象解析
1. 为什么页面超链接的 herf 属性值加上斜杠会报错
1. 原因分析
- 为什么在 springmvc.xml 中注册处理器时其 name 属性值是以斜杠开头,而 index.jsp 页面中的超链接的 herf 属性值加上斜杠后会报错
- springmvc.xml 中注册处理器的<bean/>标签的 id 属性中加上斜杠的路径,是后台路径,其参照路径是当前 Web 应用的根:http://localhost/springmvc/。即指出当前注册的处理器MyController 的资源访问绝对路径是:http://localhost/springmvc/hello.do
2. 解决办法
- 若还想加上斜杠,还不想出错,则解决为法就是手工为href 属性值的相对路径上添加项目名称
- ```jsp
<a href="/springmvc/hello.do">welcome</a>
```
- 但这样做不好的是,若在项目发布时项目名称发生了改变,则必须将每一个请求的路径进行变。所以,可以通过 EL表达式**${pageContext.request.contextPath}动态的获取到项目名称**
- **pageContext.request.contextPath:jsp中获取目前页面的项目路径**
2. 为什么跳转回 index 页面后地址栏会多出一个 test
1. 问题重现
1. 修改 springmvc配置文件
- 为了构建发生这种异常的场景,在注册处理器时,为该\<bean/>的 id 属性中添加上一个/test,表示模块信息。即表示的意义为,若要访问 MyController 这个处理器,则需要提交请求http://localhost/springmvc/test/hello.do
2. 修改 index页面
- 修改 MyController 中 ModelAndView 中指定要跳转的页面为/index.jsp,表示重新返回index 页面,用于模拟当表单数据填写错误后重新返回表单页面的场景
- ```jsp
<a href="test/hello.do">welcome</a>
```
```
3. 修改处理器
- ```java
public class MyController implements Controller {
@Override
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
return new ModelAndView("/index.jsp");
}
```
- 项目发布运行后,在 index 页面中点击链接,可以正常返回 index 页面。但若此时再次点击这个链接,会抛出 404 异常。然后却发现地址栏**多出了一个test**
2. 原因分析
- 超链接中的href为无斜杠的相对路径,其参照路径为当前访问路径第一次点击时访问的是http://127.0.0.1:8080/springmvc/test/login.do
- 在此访问路径下,其当前访问路径已经发生变化,变为http://127.0.0.1:8080/springmvc/test/,所以就会出错
3. 解决办法
1. **使用斜杠开头的请求路径**
```jsp
<a href="${pageContext.request.contextPath}/hello.do">welcome</a><br/>
```
2. 不使用斜杠开头的请求路径
- 若不使用斜杠开头的请求,则需要使用 JSP 的\<base/>标签。页面中的\<base/>标签,会在当前页面的资源请求路径前自动加上\<base/>标签所代表的路径,将相对路径变为带访问协议的绝对路径
- 该方式实际上是修改了不带斜杠的相对路径的参照路径,将由原来的当前访问路径作为参照路径,改为了以 basePath 为参照路径
- 所以这种方式要求页面中的请求不能以斜杠开头
- ```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<html>
<head>
<base href="<%basePath%>"/>
<title>$Title$</title>
</head>
<body>
<a href="hello.do">welcome</a><br/>
<body>
```