今天我们开始新的课程,看一下spring对于MVC模式是如何使用的。我先简单介绍一下MVC模式吧,MVC三层分别是Model、View、Controller三个层次,代表数据层、视图层、控制层。视图层先获取用户的输入,通过Controller层修改了Model层次的数据,Model层数据的改变,通过Controller层触发了View层的视图。
1、如何实现springmvc的hello world?
如果使用springmvc框架,要导入相关的jar包,看一下我们pom.xml的依赖:
pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
访问springmvc的过程其实就是访问DispatcherServlet这个Servlet,这个Servlet我们后面会单独讲到。我们在web.xml中配置这个servlet如下:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Test</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
我们写出一个控制器,也就是访问的页面,但是这个控制器不再是Servlet,而是一个普通的类,实现了Controller接口,并重写他的handleRequest方法,代码如下:
SpringTest.java
package com.springmvc.test;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class SpringTest implements Controller{
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
System.out.println("访问本网站成功。");
return null;
}
}
springmvc会默认加载WEB-INF下的springmvc-servlet.xml(“-”前的名字和web.xml的servlet-name一致),我们在这个目录下创建xml文件如下:
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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 配置处理映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 处理适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
<bean id="test" name="/test" class="com.springmvc.test.SpringTest"></bean>
</beans>
然后在tomcat上运行控制器,发现可以访问到一个空白网页,控制台上打印如下:
访问本网站成功。
这样一个简单springmvc的helloworld就完成了,接下来我们仔细的分析这个demo,来对springmvc有个更深的了解。
2、怎么指定某个xml作为springmvc的配置文件?
如果不想让spring加载默认的springmvc配置文件,也可以手动加载某文件,在web.xml的<servlet>
中写上如下代码:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.xml</param-value>
</init-param>
这样,我们就可以将/WEB-INF/springmvc-config.xml当做springmvc的配置文件了。
3、springmvc的流程是什么样的?
从接受到请求开始,springmvc各组件之间开始通力合作、各司其职,他们有条不紊的做着自己的工作,现在我们从上帝角度来看一下springmvc的各个组件是如何工作的吧!
整个过程开始于用户发出的一个HTTP请求,web服务器接收到这个请求之后如果符合DispatcherServlet中央处理器的映射路径,那么web容器会将这个请求交给DispatcherServlet进行处理。
中央处理器接收到这个信息,会根据HandlerMapping处理映射器进行匹配,找到处理请求的处理器,我们称之为handler,也就是我们上面demo的controller接口实现类,这个类是我们自己写的。
HandlerMapping通过请求来找到对应的handler标识符,接下来就由HandlerAdapter处理适配器使用统一的接口来调用相应的handler。
调用相应的handler后,接下来就是这个处理器对于业务逻辑的处理,然后这个处理器会再返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名称也就是页面的路径,还包括模型数据信息也就是需要转发的参数。
处理器的ModelAndView返回的视图逻辑名称并不是真正的视图对象,DispatcherServlet借助ViewResolver视图解析器完成逻辑视图名到真实视图对象的操作。
视图解析器解析出来的真实对象再对ModelAndView中的模型数据信息进行渲染,然后客户端就可以得到这个响应了。
我们上面几个步骤说的是一个请求的处理和响应,也可以说是View到Controller层再反馈给View的过程,为了举例简单,我们没有说数据的持久层也就是Model层。
3、处理映射器的作用是什么?
我们先来看一下HandlerMapping的结构图:
我们看到上面有五个类都实现了同一个接口HandlerMapping,但无论是哪个实现类,他们都是将请求以不同的形式找到对应的处理器,还是以第一问的demo为例子,我们分别来看一下这些实现类有什么区别?
BeanNameURLHandlerMapping实现类配置处理器
<!-- 配置处理映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 给SpringTest处理器类添加映射注解“/test.action” -->
<bean id="test.action" name="/test" class="com.springmvc.test.SpringTest"></bean>
这种处理映射器使用bean的name属性,给处理器添加映射路径。
SimpleUrlHandlerMapping处理映射器
<!-- 配置处理映射器 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!-- 给SpringTest处理器类添加映射注解“/test.action” -->
<property name="mappings">
<props>
<prop key="/test.action">test</prop>
<prop key="/test.do">test</prop>
</props>
</property>
</bean>
<bean id="test" class="com.springmvc.test.SpringTest"></bean>
这种处理映射器不再使用bean的name,而是在处理映射器的bean里面的属性给某个bean添加映射路径。这样做的好处是可以给一个bean赋予多个映射路径,也可以给多个bean赋予映射路径。
ControllerClassNameHandlerMapping处理映射器
<!-- 配置处理映射器 -->
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>
<!-- 处理器的bean必须配置 -->
<bean class="com.springmvc.test.SpringTest"></bean>
这种处理映射器会自动将[处理映射器小写类名.do]映射成处理器的路径,类名必须全部小写。例如上面的处理器链接,可以访问如下url:
http://localhost:8080/Test/springtest.do
剩下的两个处理映射器RequestMappingHandlerMapping和DefaultAnnotationHandlerMapping都是处理注解的处理映射器,前者在3.1版本开始提供,后者在2.5版本开始提供,前者比后者提供更多的扩展。
4、处理适配器的作用是什么?
我们先来看一下HandlerAdapter的结构图:
我们看到SimpleServletHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter、SimpleControllerHandlerAdapter四个类共同实现了HandlerAdapter接口,那这四个处理适配器有什么区别呢?我们分别来看一下不同的处理适配器都是使用什么样的处理器:
SimpleControllerHandlerAdapter的配置
<!-- 处理适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
SimpleControllerHandlerAdapter的处理类
package com.springmvc.test;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class SpringTest implements Controller{
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
System.out.println("访问本网站成功。");
return null;
}
}
SimpleControllerHandlerAdapter的处理适配器会调用Controller接口的handleRequest方法。
SimpleServletHandlerAdapter处理适配器的配置
<!-- 处理适配器 -->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"></bean>
SimpleServletHandlerAdapter处理适配器的处理类
package com.springmvc.test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SpringTest extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("访问网站成功。");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
}
SimpleServletHandlerAdapter处理适配器会调用Servlet的service方法,这个servlet不需要在xml中配置映射路径,也不需要注解配置,而是通过处理映射器进行配置映射路径。
HttpRequestHandlerAdapter处理适配器的配置
<!-- 处理适配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
HttpRequestHandlerAdapter处理适配器的处理类
package com.springmvc.test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
public class SpringTest implements HttpRequestHandler{
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("访问网站成功。");
}
}
HttpRequestHandlerAdapter处理适配器会调用HttpRequestHandler实现类的handleRequest方法。
AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter处理适配器是注解的适配器,我们在这先不花费时间去研究,后面会专门讲到注解形式的springmvc。
有人又问了,上面讲了四种适配器,那我应该选择哪个适配器呢?条条大路通罗马,只要是能达到你的目的的就是好适配器。
5、视图解析器的作用是什么?
在控制器中,会返回String、View、ModelAndView或者ModelMap其中的一个,无论是什么对象,都会被springmvc封装成一个ModelAndView对象返回,最终 借助视图解析器得到最终的视图对象View,可以是jsp,可以使html等各种视图对象,最后调用View的render()方法进行视图渲染,得到相应结果。
视图解析器就是讲逻辑视图转变成物理视图的过程。我们来看一下视图解析器的结构图:
我们主要来了解一下常见的视图解析器InternalResourceViewResolver,他将视图名解析成一个url文件,一般使用该解析器将视图名映射为一个保存在WEB-INF下的视图文件。配置代码如下:
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置视图的前缀 -->
<property name="prefix" value="/WEB-INF/views/"></property>
<!-- 设置视图的后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>