第二章 SpringMVC介绍

在JavaWeb开发中,最经典的就是采用MVC架构模式。Spring MVC是Spring提供的一个实现了MVC设计模式的轻量级Web框架。要想使用Spring MVC的化,就需要添加所需要的JAR包:spring-web-5.3.39.jar 和 spring-webmvc-5.3.39.jar两个文件。

在传统的JavaWeb开发中,我们通常使用Servlet来处理用户请求,并使用JSP作为视图页面返回给用户。如果涉及到数据库的操作,可以在Servlet中完成,也可以封装到对应的模型类中,然后再Servlet中调用。而Spring MVC架构下,我们使用Controller来处理用户请求,它不是Servlet类,而是一个普通的SpringBean而已。对于数据库的操作,Spring则是通过Model层来实现的,它也是一个普通的SpringBean而已。对于页面视图,我们可以使用JSP实现,也可以使用第三方模板框架来实现。SpringMVC处理用户请求的流程大致是,接收到用户url后,根据url和Controller的映射关系来实例化Controller,并执行里面相应的方法,处理完业务逻辑之后,就可以返回View层页面(例如JSP页面)。业务Model层通常是在Controller中被调用,这就涉及到我们上文刚刚讲到的依赖注入,也就是Model类注入到Controller中。也就是说,在Controller中不需要实例化Model类,就可以使用。那么我们在JavaWeb项目中如何使用Spring以及SpringMVC框架呢?在上一个章节中,我们介绍了Spring的核心就是一个ApplicationContext类,我们可以通过一个xml配置文件来实例化得到它。在JavaWeb项目中,ApplicationContext实例化工作由Web容器(例如Tomcat)来完成。Web容器通过ContextLoaderListener来实现对ApplicationContext的实例化工作。这种方式只需要在web.xml中添加如下代码:

<!-- Spring配置文件 -->
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<!-- Spring监听器 -->
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在Web容器启动web应用时,会触发ServletContextEvent事件,这个事件会被ServletContextListener监听,监听到了以后会通过ContextLoaderListener监听器(继承自ServletContextListener)去初始化ApplicationContext对象,同时我们会在默认的WEB-INF/applicationContext.xml文件中配置所有的SpringBean信息,上面的xml配置也明确给出了这样的信息。接下来,我们就来创建一个新的 “SpringMVCDemo” 的 Dynamic Web Project工程,如下所示

如果对于JavaWeb工程还不清楚的,可以查看JavaWeb课程:https://blog.csdn.net/richieandndsc/category_12377686.html

工程创建完毕后,我们需要将如下Jar文件复制到工程lib目录下。

就是在原来Spring基础Jar文件的基础上增加了SpringMVC所需的两个Jar文件而已。添加完毕之后,我们可以右击“SpringMVCDemo”工程选择 “Refresh”刷新一下,在 “Java Resource”下的“Web App Library”目录下就能看到

接下来,我们继续介绍SpringMVC框架。SpringMVC接收用户url并分配Controller层来处理的过程则是由一个叫做DispacherServlet的前端控制器实现的,没错,这个类是一个Servlet类。因此,我们就需要在web.xml文件中添加Spring MVC的前端控制器DispacherServlet的配置,加上之前的配置,完整的web.xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">

	<display-name>SpringMVCDemo</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

	<!-- Spring 前端控制器 -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/springmvc-config.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Spring 前端控制器url映射 -->
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

	<!-- Spring配置文件 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>

	<!-- Spring监听器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

</web-app>

以上配置了一个DispatcherServlet,它将处理所有后缀为.do的url请求(星号匹配所有字符)。该Servlet在Web应用程序启动时候立即加载,DispatcherServlet加载的时候需要一个Spring MVC的配置文件,默认情况下,应用会去应用程序文件夹的WEB-INF下查找对应的[servlet-name]-servlet.xml文件。例如本例的<servlet-name>是springmvc,默认查找的就是 /WEB-INF/springmvc-servlet.xml 文件。当然,我们也可以把Spring MVC的配置文件放到应用程序文件夹中的其他地方,使用Servlet元素的init-param表示配置文件路径和名称。Spring会使用该文件创建一个WebApplicationContext容器对象,也就是Web应用上下文,WebApplicationContext继承自ApplicationContext容器。虽然这个WebApplicationContext继承自ApplicationContext,那么它和我们上文中通过ContextLoaderListener创建的ApplicationContext是什么关系呢?解释一下,ContextLoaderListener会先于DispatcherServlet创建ApplicationContext;DispatcherServlet在创建WebApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传递给DispatcherServler的WebApplicationContext的setParent()方法,作为它的父上下文。他们是父子关系。也就是说,JavaWeb工程中会有两个应用上下文,一个是父亲ApplicationContext(对应applicationContext.xml配置文件),另一个是儿子WebApplicationContext(对应springmvc-config.xml配置文件)。两者该如何区分使用呢?ContextLoaderListener中创建的ApplicationContext主要用于整个Web应用需要共享的一些组件,比如数据库操作对象(Dao);而由DispatcherServlet创建的WebApplicationContext主要用于和该Servlet相关的组件,比如Controller,ViewResovler(视图解析器)等等。对于作用范围而言,在WebApplicationContext中可以引用由ContextLoaderListener所创建的ApplicationContext,而反过来不行。请注意,两个应用上下文都可以管理Spring Bean。因此,如果一些bean在父ApplicationContext中声明了,就不需要在子WebApplicationiContext中声明了。当一些bean在MVC的上下文(WebApplicationContext)中无法获取的话,Spring就会溯源到父上下文(ApplicatonContext)中寻找。但是,尽量不要在两个上下文中同时配置同一个bean对象。看到这里,大家应该就比较清晰了吧。关于如何在代码中获取到ApplicationContext或者WebApplicationiContext这两个对象的方法有很多,大家可以自己研究一下。这里推荐一种方式,就是借助org.springframework.web.context.support.WebApplicationContextUtils的getRequiredWebApplicationContext方法来获取,这个方法需要传递 ServletContext 对象。这个ServletContext对象可以使用 request.getSession().getServletContext() 来获取,大家可以试一试。接下来我们就创建applicationContext.xml和springmvc-config.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
   https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

这个applicationContext.xml暂时为空就行,因为目前我们没有bean进行配置。

<?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:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc
    https://www.springframework.org/schema/mvc/spring-mvc.xsd">

</beans>

这个是springmvc-config.xml配置文件,它的内容暂时也是空。但是,想要让Spring MVC正常工作的话,我们肯定得需要配置一点什么。我们再回头详细说明一下Spring MVC程序的完整执行流程如下:

1. 用户通过浏览器向服务器发送请求,该请求会被DispatcherServlet所拦截
2. DispatcherServlet拦截请求后,会调用HandlerMapping映射器
3. HandlerMapping映射器会根据请求URL找到具体的Controller(这个过程比较复杂)
4. Controller执行完成后,会返回一个ModelAndView对象,该对象中包含视图名
5. DispatcherServlet会根据ModelAndView对象选择一个合适的ViewReslover(视图解析器)
6. ViewReslover解析后,会向DispatcherServlet返回具体的View视图
7. DispatcherServlet会将视图返回给客户端浏览器,请求处理完成

DispatcherServlet在Spring中充当一个前端控制器的角色,它的核心功能是分发请求。在上面的流程中,我们涉及到了 HandlerMapping映射器,ModelAndView对象 以及ViewReslover视图解析器三个新的内容。HandlerMapping映射器可以简单理解为一组K-V对,就是请求url和Controller类的对应关系。因此,这个HandlerMapping映射器需要我们在springmvc-config.xml中配置。ModelAndView对象包含了模型数据和视图名称,它一般在我们controller类中使用,不需要在springmvc-config.xml中配置。最后一个就是ViewReslover视图解析器,它来决定我们使用那种技术来实现视图层,例如JSP,或者Thymeleaf模板技术等等。因此,它也需要我们在springmvc-config.xml中配置,如下所示:

	<!-- 声明映射器:那个url对应那个控制器 -->
	<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
		<props>
			<!--<prop key="xxx.do">xxxController</prop>-->
		</props>
		</property>
	</bean>

	<!-- 声明视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>

映射器目前也是空的,因为我们还没有创建controller控制器。但是我们使用注释的方式给出了配置样例,也就是xxx.do的请求url会交给xxxController进行处理。最后就是视图解析器,我们目前就使用jsp来做视图展示。

使用Spring开发Web应用程序有两种方式,一种是基于接口,另一种是基于注解。两种方式的区别仅仅是继承Spring框架库的方式不同而已,具体的API使用还是一样的。我们先介绍XML的方式,后面再介绍注解方式。接下来,我们就来创建一个Controller控制器,它需要实现org.springframework.web.servlet.mvc.Controller接口。在该接口中,必须实现handleRequest方法(也就是处理请求的代码方法)。

接下来,我们在src目录下创建一个 “HelloController” 控制器类,并继承上面的接口,如下所示

package com.demo;

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 HelloController implements Controller {

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		// TODO Auto-generated method stub
		return null;
	}

}

HelloController是一个实现Controller接口的控制器,它可以处理一个单一的请求动作。HandleRequest是Controller接口必须实现的方法,Controller调用该方法来处理请求。该方法的对应参数是HttpServletRequest和HttpServletResponse,前者是请求对象,后者是响应对象,我们就不在详细介绍了。我们可以使用HttpServletRequest获取用户提交的数据,使用HttpServletResponse设置返回响应。但是,我们一般不直接使用HttpServletResponse直接返回数据,而是通过handleRequest方法的返回值。该方法返回值一个包含视图名和模型的ModelAndView对象。SpringMVC会根据这个对象返回视图层。

接下来,我们补全HandleRequest方法。

	// TODO Auto-generated method stub
	ModelAndView mv = new ModelAndView();
	mv.addObject("message", "hello,springmvc!");
	mv.setViewName("/hello.jsp");
	return mv;

上述代码实例化了一个名称为mv的ModelAndView对象,这个对象可以理解为一个模型数据集。我们可以从数据库中查询记录数据,并放入到该对象中。放置的方法就是调用addObject方法,参数就是对应的key和value,key是一个字符串对象,而value则是一个Object对象,它可以是一个字符串,也可以是一个List列表等等。本案例代码中添加了一个名为“message”的字符串对象“hello,springmvc!”。接下来,就是ModelAndView对象的setViewName方法,它可以传递一个JSP文件作为视图View层进行返回。本案例代码返回的视图为“/hello.jsp”,其真正的路径就是“/WebContent/hello.jsp”。因此,我们需要在WebContent目录下创建hello.jsp文件,如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>hello</title>
</head>
<body>

<h1>${message}</h1>

</body>
</html>

在JSP文件中,我们如何获取message中的字符串呢?非常简单,直接使用EL表达式即可。从这里我们可以看出,ModelAndView对象的本质就是Servlet中的转发,并且将模型数据message放置到了requestScope请求域中。那么我们就很容易在JSP文件中使用EL表达式来获取Controller传递过来的requestScope请求域中字符串数据变量message。目前为止,我们已经创建了HelloController,也创建了JSP View,暂时我们不需要Model层,因此就不创建了。接下来,我们将那个url绑定到HelloController呢?为此,我们可以在WebContent下继续创建一个index.jsp文件(在Web.xml文件中已经配置了,但是还没有创建),其内容如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<a href="hello.do">hello</a>

</body>
</html>

我们只所以在WebContent下创建index.jsp文件,是因为在web.xml配置文件中,有一项<welcome-file-list>标签,用来指定默认的启动页。也就是说,当我们启动Web容器(Tomcat)的时候,我们直接访问当前项目工程(http://localhost:8080/SpringMVCDemo/),就会自动加载这个index.jsp文件响应给客户端浏览器。因此,我们在index.jsp文件中添加了一个超链接,这个超链接对象会发起Get请求,请求地址为:hello.do

我们知道,在Java Web项目中使用Servlet来处理用户的请求(hello.do)的。还记得我们之前在web.xml中配置的前端控制器DispatcherServlet吗?它就是一个Servlet,而且它对应处理的url是:*.do ,星号代表匹配一切字符,也就是说,这个前端控制器DispatcherServlet可以接收任何形式的后缀为do的请求,当然包括我们上面的hello.do请求了。那么问题来了?DispatcherServlet接收这个hello.do请求后,如何来分配给HelloController来处理呢?这就需要我们在springmvc-config.xml中进行配置了。如下图所示:

	<!-- 声明控制器 -->
	<bean name="helloController" class="com.demo.HelloController"></bean>

	<!-- 声明映射器:那个url对应那个控制器 -->
	<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
		<props>
			<prop key="hello.do">helloController</prop>
		</props>
		</property>
	</bean>

	<!-- 声明视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>

在上述中,我们配置了三个bean对象。第一个bean对象,就不用解释了,就是我们创建的HelloController对象。第二个对象是SimpleUrlHandlerMapping,它是一个映射对象,负责指定那个Controller来处理那个url。这里,我们就配置了“hello.do”的请求url被我们的HelloController所处理。这样就实现了,我们点击 hello.do的超链接 的时候,就指定了HelloController中的 handleRequest方法 来处理了,后续就会返回“hello.jsp”视图文件。最后,我们还配置了一个视图解析器InternalResourceViewResolver,它的目的就是让我们ModelAndView中的"/hello.jsp"解析到WebContent下的hello.jsp文件。此时我们的工程文件结构如下

所有配置完成之后,我们就可以run我们的SpringMVCDemo工程了。我们右击”SpringMVCDemo“工程,选择”RunAS“ --> "Run On Server",截图如下

接下来,我们回顾一下Spring MVC的整个流程。首先,用户通过浏览器向服务器发送请求hello.do,该请求会被DispatcherServlet所拦截。然后,DispatcherServlet拦截请求后,会调用HandlerMapping映射器,HandlerMapping映射器会根据请求hello.do的URL找到具体的HelloController,然后执行里面的 handleRequest 方法。在该方法中会返回一个ModelAndView对象,该对象中包含视图名hello.jsp。Spring中的视图解析器ViewReslover会向DispatcherServlet返回具体的View视图,最后,DispatcherServlet会将视图返回给客户端浏览器。Spring MVC的整个工作就完毕了。

接下来,我们来演示Spirng的依赖注入的使用。

首先,我们需要创建一个HelloDao.java文件,如下所示:

package com.demo;

public class HelloDao {

	public String getMessage() {

		return "hello, dao!";
	}

}

按照正常的使用方式,如果我们想要在HelloController中使用HelloDao的话,我们需要先实例化HelloDao类得到一个对象,然后调用getMessage方法。既然我们使用Spirng的依赖注入,我们就不能像上面的方式使用HelloDao了。首先,我们需要在HelloController中声明HelloDao类变量,如下所示:

	// 依赖注入
	private HelloDao helloDao;
	
	public void setHelloDao(HelloDao helloDao) {
		
		this.helloDao = helloDao;
	}

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		// TODO Auto-generated method stub
		ModelAndView mv = new ModelAndView();
		//mv.addObject("message", "hello,springmvc!");
		mv.addObject("message", helloDao.getMessage());
		mv.setViewName("/hello.jsp");
		return mv;
	}

以上代码我们有两处改动,第一是增加类变量helloDao以及set注入方法,第二就是在handleRequest方法中调用helloDao的getMessage方法来返回字符串"hello,dao!",将之前的硬代码部分注释掉即可。然后我们还需要进行xml的配置。这里,我们将helloDao配置到 applicationContext.xml 文件中去,如下

<bean id="helloDao" class="com.demo.HelloDao"></bean>

然后我们在去 springmvc-config.xml 文件中配置

<!-- 声明控制器 -->
<bean name="helloController" class="com.demo.HelloController">
    <property name="helloDao" ref="helloDao"></property>
</bean>

有的同学会问,HelloDao是在applicationContext.xml中配置的,它能在springmvc-config.xml 发现并使用吗?回想一下 两者的关系就会明白,子类可以继承父类里面的配置的,所以它是可以发现并使用的。我们运行一下我们的工程,就知道这个配置方式到底行不行了。

视图解析器介绍

Spring MVC中的视图解析器负责解析视图,可以通过在配置文件(springmvcConfig.xml)中定义一个ViewResolver来配置视图解析器。Spring MVC 提供了很多视图解析类,其中每一项都对应特定的视图技术。

URLBasedViewResolver

UrlBasedViewResolver 是对 ViewResolver 的一种简单实现,主要提供了一种拼接 URL 的方式来解析视图。UrlBasedViewResolver 通过 prefix 属性指定前缀,suffix 属性指定后缀。当 ModelAndView 对象返回具体的 View 名称时,它会将前缀 prefix 和后缀 suffix 与具体的视图名称拼接,得到一个视图资源文件的具体加载路径,从而加载真正的视图文件并反馈给用户。使用 UrlBasedViewResolver 除了要配置前缀和后缀属性之外,还需要配置“viewClass”,表示解析成哪种视图。


<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">            
   <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
   <property name="prefix" value="/WEB-INF/jsp/"/>
   <property name="suffix" value=".jsp"/>  
</bean>

上述视图解析器配置了前缀和后缀两个属性,这样缩短了 view 路径。上述 viewClass 值为 InternalResourceViewResolver,它用来展示 JSP 页面。如果需要使用 jstl 标签展示数据,将 viewClass 属性值指定为 JstlView 即可。另外,存放在 /WEB-INF/ 目录下的内容不能直接通过 request 请求得到,所以为了安全性考虑,通常把 jsp 文件放在 WEB-INF 目录下。

InternalResourceViewResolver

InternalResourceViewResolver 为“内部资源视图解析器”,是日常开发中最常用的视图解析器类型。它是 URLBasedViewResolver 的子类,拥有 URLBasedViewResolver 的一切特性。

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="viewClass">
		<value>org.springframework.web.servlet.view.JstlView</value>
	</property>
	<property name="prefix" value="/WEB-INF/jsp/"/>
	<property name="suffix" value=".jsp"/>  
</bean>

我们的项目案例中使用的就是这个视图解析器,但是我们没有配置prefix和suffixe属性。

FreeMarkerViewResolver

FreeMarkerViewResolver 是 UrlBasedViewResolver 的子类,可以通过 prefix 属性指定前缀,通过 suffix 属性指定后缀。FreeMarkerViewResolver 最终会解析逻辑视图配置,返回 freemarker 模板。不需要指定 viewClass 属性。FreeMarkerViewResolver 配置如下

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
   <property name="prefix" value="fm_"/>
   <property name="suffix" value=".ftl"/>
</bean>

说到这里,就不得不说一说模型引擎。Java模版引擎有很多,Freemarker就是其中一个。当然我们上文中的FreeMarkerViewResolver其实就是Spring对Freemarker的支持。模板引擎是为了解决用户界面(显示)与业务数据(内容)分离而产生的。他可以生成特定格式的文档,常用的如格式如HTML、xml以及其他格式的文本格式。

整体项目下载地址: https://download.csdn.net/download/richieandndsc/89876567

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值