使用IntelliJ IDEA开发SpringMVC网站(二)框架配置

《Maven与IntelliJ IDEA的完美结合》文章中介绍了在IntelliJ IDEA中如何使用Maven创建一个简单的Java Web项目,《SpringMVC简介》文章中对Spring MVC进行了简单的介绍,有了这些基础,本文在《Maven与IntelliJ IDEA的完美结合》创建的项目基础上,讲解一下如何利用Spring MVC技术来搭建一个Web开发框架。

导入相应的jar包

既然我们要使用Spring MVC开发,那肯定少不了Spring MVC的相关jar包。如果不使用Maven的话,那就需要去官网下载相关的jar包,然后导入到项目中(我原来开发项目大多都是使用的这种方式,为了保证编译通过,我们会到处去寻找jar包,当编译通过了,在运行的时候,经常会出现”ClassNotFoundException”,难道还差jar包吗?再根据错误信息再去查找和下载jar包;还有个问题就是有时也会出现下载的jar包存在版本不兼容的问题)。

使用Maven,上面提到的问题很好的得到了解决。

Maven所做的工作其实很简单,就是自动把你需要的jar包下载到本地,然后关联到项目中来。maven的所有jar包都是保存在几个中央仓库里面的,其中一个最常用的是Maven Repository ,即,你需要什么jar包,它就会从仓库中拿给你。那么如何告诉maven需要什么jar包呢?我们看看工程目录,能找到一个pom.xml文件,maven就是靠它来定义需求的,pom.xml代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jasonli</groupId>
<artifactId>springmvcdemo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvcdemo Maven Webapp</name>
<url>http://maven.apache.org</url>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>springmvcdemo</finalName>
</build>
</project>

主要看dependencies标签,dependencies定义了该项目的依赖关系,其中一个dependency对应一个Maven项目,groupId、artifactId、version分别表示Maven项目的组织名、构件号、版本号,它们三个合起来就是Maven坐标,根据这个坐标可以在Maven仓库中对应唯一的一个Maven构件。现在我们要引入spring mvc相关的jar包,该怎么做了。打开前面提到的Maven Repository,搜索spring web,进入如下界面:

image

关于spring-webmvc和spring-web的区别,请参考:http://stackoverflow.com/questions/13533700/maven-dependency-spring-web-vs-spring-webmvc

image

我们这里要用到Spring MVC框架,所以我们选择Spring Web MVC点击进入,可以看到各版本的使用情况

image

选择版本(这里我们使用4.2版本)4.2.6.RELEASE,可以看到其dependency写法如下红框所示:

image

我们将其复制到pom.xml文件中的dependencies中,这样Maven就会开始自动下载jar包到本地仓库,然后关联到你的项目中,下载完成后,我们展开工程目录中的External Libraries:

image

可以发现,虽然我们只写了一个依赖spring-webmvc,但是它导入了7个jar包,包括spring-core,spring-context,spring-beans,spring-aop,我想这就体现了Maven的好处了,如果我们需要使用Spring MVC开发网站的话,只需要记住重要的包的名字(spring-webmvc),就可以轻松将所有包导入项目中。

《架构探险—从零开始写Java Web框架》第一章 第6页 有这一样一段话:”提示:可以简单地将Maven依赖理解为jar包,只不过Maven依赖具备传递性,只要配置某个依赖,就能自动获取该依赖的相关依赖。”

在加入servlet-api的依赖,完成后的pom.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jasonli</groupId>
<artifactId>springmvcdemo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvcdemo Maven Webapp</name>
<url>http://maven.apache.org</url>

<properties>
<!-- base setting -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.locales>zh_CN</project.build.locales>
<project.build.jdk>1.7</project.build.jdk>

<!-- lib versions -->
<junit.version>4.11</junit.version>
<spring.version>4.2.6.RELEASE</spring.version>
<servletapi.version>3.0.1</servletapi.version>
</properties>

<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<!-- spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servletapi.version}</version>
</dependency>
</dependencies>

<build>
<finalName>springmvcdemo</finalName>
</build>
</project>

至此,jar包导入就完成了,下面我们来搭建Spring MVC.

搭建Spring MVC

Spring MVC的核心是DispatcherServlet,这个Servlet充当Spring MVC的前端控制器。与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置。所以在应用程序中使用Spring MVC的第一件事情就是将下面的servlet声明放入web.xml中:

image

为这个Servlet所设置的servlet-name是很重要的。默认情况下,DispatcherServlet在加载时会从一个基于这个Servlet名字的XML文件中加载Spring应用上下文。在这里因为servlet的名字是mvc-dispatcher,所有DispatcherServlet将尝试从一个名为mvc-dispatcher-servlet.xml的文件(位于应用程序的WEB-INF目录下)来加载应用上下文。

下面我们来手动创建这个文件:
image

image

新建该xml文件后,点击右上角的configure,出现Setup Frameworks界面,点击OK,这样,IntelliJ IDEA就识别了Spring MVC的配置文件,mvc-dispatcher-servlet.xml文件如下:

1
2
3
4
5
6
<?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>

在web.xml中通过将DispatcherServlet映射到/,声明了它会作为默认的servlet并且会处理所有的请求,包括对静态资源的请求。

如果你担心DispatcherServlet要处理这些请求的话,那么请稍安勿躁。

Spring提供了一个很方便的配置,使得我们不必过于担心细节。Spring MVC命名空间包含了一个新的新的 mvc:resources 元素,它会处理静态资源的请求,你所要做的就是在Spring配置文件中对其进行配置,配置后的mvc-dispatcher-servlet.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">

<!-- Processing requests for static resources -->
<mvc:resources mapping="/resources/**" location="/resources/" />

</beans>

mvc:resources 建立了一个服务于静态资源的处理器。属性mapping被设置为/resources/**,它包含了Ant风格的统配符以表明路径必须于/resources开始,而且也包括了它的任意子路径。属性location表明了要提供服务的文件位置。以上配置表明,所有以/resources路径开头的请求都会自动由应用程序根目录下的/resources目录提供服务。因此我们在webapp创建resources目录,以后我们的图片、样式表、JavaScript以及其他的静态资源都必须放在应用程序的/resources目录下。

上面解决了Spring MVC请求资源访问问题,下面我们考虑应用程序的功能该如何处理了。

编写基本的控制器

DispatcherServlet需要咨询一个或多个处理器映射来明确地将请求分发给那个控制器。Spring自带了多个处理器映射实现供我们选择,具体如下:

  • BeanNameUrlHandlerMapping: 根据控制器Bean的名字将控制器映射到URL。
  • ControllerBeanNameHandlerMapping: 与BeanNameUrlHandlerMapping类似,根据控制器Bean的名字将控制器映射到URL。使用该处理器映射实现,Bean的名字不需要遵循URL的约定。
  • ControllerClassNameHandlerMpaaing:通过使用控制器的类名作为URL基础将控制器映射到URL。
  • DefaultAnnotationHandlerMapping:将请求映射给使用@RequestMapping注解的控制器好控制器方法。
  • SimpleUrlHandlerMapping:使用定义在Spring应用上下文的属性集合将控制器映射到URL。

使用上述这些处理器映射通常只需在Spring中配置一个Bean。如果没有找到处理器映射Bean,DispatcherServlet将创建并使用BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping。我们恰巧主要使用基于注解的控制器类,所以DispatcherServlet所提供的DefaultAnnotationHandlerMapping就能很好地满足我们的需求了。 我们在mvc-dispatcher-servlet.xml文件中添加一行配置就能得到Spring MVC所提供的注解启动特性:

1
<mvc:annotation-driven />

尽管很小,但 mvc:annotation-driven 标签有足够的威力。它注册了多个特性,包括JSR-303校验支持、信息转换已经对域格式化的支持。

创建Controller

在src\main\java中新建一个用于保存controller的package

image

image

在controller包中新建java类HomeController(名称不固定,可任意取),用于处理首页的请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.springmvcdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

/**
* Home page controller
*/
@Controller
public class HomeController {

@RequestMapping({"/","/home"})
public String homePage(Map<String, Object> model) {
return "home";
}
}

@Controller注解表明这个类是一个控制器类,这个类是@Component注解的一种具体化,这意味着我们需要在mvc-dispatcher-servlet.xml文件中配置一个 context:component-scan 这样HomeController类(以及将要编写的其他控制器)将会自动被发现并注册为Bean,以下是相关的XML片段:

1
<context:component-scan base-package="com.springmvcdemo" />

@RequestMapping注解表明这是一个请求处理方法,更确切地说它指明了这个方法要处理”/“或者”/home”路径的请求。

homePage()方法所做的事情比较简单,就是返回一个String类型的值,这个只是要渲染结果的逻辑视图的名字。控制器类不应该直接参与渲染结果给客户端,而应只是声明一个视图实现,由这个视图实现给客户端渲染数据。

解析视图
处理请求的最后一件必须要做的事情就是为用户渲染输出。这个任务落在了视图实现上-通常会是JSP(JavaServer Page),但是其他的视图技术(如Velocity或Freemarker)也是可以使用的。为了确定指定的请求使用哪个视图,DispatcherServlet会查找一个视图解析器来讲控制器返回的逻辑视图名称转换成渲染结果的实际视图。

Spring自带了多个视图解析器实现供选择:

  • BeanNameViewResolver 查找ID与逻辑视图名称相同的view实现
  • ContentNegotiatingViewResolver 委托给一个或多个视图解析器,而选择那一个取决于请求的内容类型
  • FreeMarkerViewResolver 查找一个基于FreeMarker的模板,它的路径根据加完前缀和后缀的逻辑视图名称来确定
  • InternalResourceViewResolver 在Web应用程序的WAR文件中查找视图模板。视图模板的路径根据加完前缀和后缀的逻辑视图名称来确定
  • JasperReportsViewResolver 根据加完前缀后后缀的逻辑视图名称来查找一个Jasper Report报表文件
  • ResourceBundleViewResolver 根据属性文件(properties file)来查找View实现
  • TilesViewResolve 查找通过Tiles模板定义的视图。模板的名字与逻辑视图名称相同
  • UrlBasedViewResolver 这是一些其他视图解析器(如InternalResourceViewResolver)的基类。它可以单独使用,但是没有它的子类强大。
  • VelocityLayoutViewResolver 它是VelocityViewResolver的子类,它支持通过Spring的VelocityLayoutView来进行页面组合
  • VelocityViewResolver 解析基于Velocity的视图,Velocity模板的路径根据加完前缀和后缀的逻辑视图名来确定
  • XmlViewResolver 查找在XML文件(/WEB-INF/views.xml)中声明的View实现。这个视图解析器与BeanNameViewResolver很类似,但在这里视图bean的声明与应用程序Spring上下文的其他bean是分开的
  • XsltViewResolver 解析基于XSLT的视图,XSLT样式表的路径根据加完前缀和后缀的逻辑视图名称来确定

这里我们以JSP作为视图实现,假设我们将JSP文件放在”/WEB-INF/views”目录下,基于这样的安排,我们需要在mvc-dispatcher-servlet.xml中配置InternalResourceViewResolver,如下所示:

1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

当DispatcherServlet要求InternalResourceViewResolver解析视图的时候,它将获取一个逻辑视图名称,添加”/WEB-INF/views/“ 前缀和”*.jsp”后缀。得到的结果就是渲染输出的JSP路径。在内部,InternalResourceViewResolver接下来会将这个路径传递给View对象,View对象将请求传递(dispatch)给JSP。所以,当HomeController返回home作为逻辑视图名称时,它最终会被解析成”/WEB-INF/views/home.jsp”路径。完成以上配置后,mvc-dispatcher-servlet.xml文件如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">

<!-- Processing requests for static resources -->
<mvc:resources mapping="/resources/**" location="/resources/" />

<!-- Using Spring MVC provides annotation-driven feature -->
<mvc:annotation-driven />

<!-- Spring Auto scanning components -->
<context:component-scan base-package="com.springmvcdemo" />

<!-- View Resolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

</beans>

创建JSP文件
接下来在WEB-INF目录下创建名为views的目录,并在views目录下面新建home.jsp,代码如下:

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
this is the home page.
</body>
</html>

测试
删除原来工程目录下面的 index.jsp 文件

然后修改一下Tomcat deployment面板里面的Application context 为/,如下图所示

image

然后启动Tomcat,启动后浏览器将自动弹出系统首页,如下所示:

image

测试下Controller向视图传递参数,修改HomeController代码如下:

image

home.jsp页面使用EL表达式获取message的值:

image

重启Tomcat服务,重启完成后再次访问首页,会发现Controller里面设置的message的值可以在页面上获取到了,如下所示:

image

关于SpringMVC前端传值到Controller与Controller中传值到View解析的问题,请参考:http://blog.csdn.net/mexican_jacky/article/details/50194667。

至此,基于Spring MVC的一个简单的框架就搭建完成了,后面会慢慢对这个框架进行一些完善。