Spring MVC 教程

在这个Spring MVC教程中,我们将学习如何使用Spring Tool Suite开发Spring MVC Web应用程序。Spring MVC框架被广泛应用于Java Web应用程序。

春季 MVC

就像Struts框架一样,Spring MVC也是基于Java EE的Servlet和JSP技术,并实现了模型-视图-控制器设计模式。

(Spring MVC 视频教程 / Spring MVC 教程)

我们之前已经学习了Spring的依赖注入原理,在这个教程中,我们将学习如何使用Spring MVC框架创建一个简单的Web应用程序。我们可以使用Eclipse或IntelliJ IDE来进行Spring项目的开发,但SpringSource提供了Spring Tool Suite(STS),这是一个基于Eclipse的IDE,它内置了基于Apache Tomcat容器的VMware vFabric tc Server,并且针对Spring应用程序进行了优化。我会在Spring MVC教程和其他未来的教程中使用STS,因为它通过提供以下功能使开发人员的生活更加轻松:

  • Support for creating skeletal Spring applications (MVC, Rest, Batch etc), good for starting the project from scratch. We will soon see in this spring MVC tutorial how easy it is to create a Spring MVC project.
  • Provides useful features such as creating Spring Configuration files, parsing config files and classes to provide useful information about them.
  • Automatic validation of Spring application
  • Refactoring support to easily make project changes, the changes get reflected in config files too.
  • Code assist for not only classes but configuration files too, I like this feature a lot because most of the times we need to know what we can use and its details.
  • Best support for Aspect Oriented Programming (AOP) through the integration of AspectJ.

看到STS提供的所有功能后,我就决定在Spring应用程序中使用它,并且到目前为止我非常满意。只需从STS官方下载页面下载并安装STS。我正在使用基于Eclipse 4.3.1版本的STS 3.4.0.RELEASE。如果您不想使用STS并且想在现有的Eclipse中获得其功能,则需要从Eclipse Marketplace安装其插件。请使用下面的图片作为参考,并确保选择正确的STS版本进行安装。以下插件适用于Eclipse Kepler。如果您不想使用SpringSource服务器,可以将应用程序部署在任何其他Java EE容器中,例如Tomcat、JBoss等。对于本教程,我将使用随STS一起提供的服务器,但我已经通过将其导出为WAR文件并在单独的Tomcat服务器中进行测试,这个应用程序运行良好。现在我们的服务器环境和IDE都准备就绪,让我们继续创建我们的第一个Spring MVC项目。以下步骤对于STS和带有STS插件的Eclipse都有效。

在STS或Eclipse中创建Spring MVC应用程序。

步骤1:从菜单中创建新的Spring项目。步骤2:在新项目窗口中,将名称设为“SpringMVCExample”,选择模板为“Spring MVC项目”。如果您是第一次使用此模板,STS将从SpringSource网站下载它。如果您愿意,可以将项目添加到任何工作集中。步骤3:当模板下载完成后,在下一个屏幕中需要提供顶层包名称。此包将用作Spring组件的基本包。步骤4:一旦Spring MVC模板创建项目完成,它将如下图所示。如果您没有看到User.java类、login.jsp和user.jsp文件,不要担心,它们是我后来添加的。如果您的项目没有被编译并出现一些错误,请运行Maven / 更新项目。请确保勾选“强制更新快照/发布”选项,参考下图。整体项目看起来就像任何基于Maven的Web应用程序一样,具有一些Spring配置文件。现在是时候分析项目的不同部分并稍作扩展了。

Spring MVC的依赖

我们生成的pom.xml文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.Olivia</groupId>
	<artifactId>SpringMVCExample</artifactId>
	<name>SpringMVCExample</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>4.0.0.RELEASE</org.springframework-version>
		<org.aspectj-version>1.7.4</org.aspectj-version>
		<org.slf4j-version>1.7.5</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
				
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		
		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
				
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>        
	</dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

artifactId将成为Web应用程序的servlet-context,因此如果您希望更改,可以进行修改。对于Spring Framework、AspectJ和SLF4j版本,只定义了少数属性,我发现它们未反映最新版本,因此我将它们更改为截至今天的最新稳定版本。我感兴趣的项目依赖项有:

  • spring-context: Spring Core dependency. Notice the exclusion of commons logging in favor of SLF4J.
  • spring-webmvc: Spring artifact for MVC support
  • aspectjrt: AspectJ API reference
  • SLF4J and Log4j: For logging purposes, Spring is very easy to configure for log4j or Java Logging API because of SLF4J integration.
  • javax.inject – JSR330 API for dependency injection

有一些其他的依赖项被添加,例如Servlet、JSP、JSTL和JUnit API,但对于初学者应用程序来说,我们可以忽略它们。

春季MVC教程 – Log4j配置

生成的log4j.xml文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="https://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.Olivia.spring">
		<level value="info" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

请注意,它将所有信息都打印到控制台,我们可以很容易地添加附加器将日志重定向到文件。

Spring MVC教程-部署描述符配置

让我们来看看我们的web.xml文件并进行分析吧。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="https://java.sun.com/xml/ns/javaee"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

ContextLoaderListener将ApplicationContext的生命周期与ServletContext的生命周期绑定,并自动创建ApplicationContext。ApplicationContext是Spring bean的存放位置,我们可以通过contextConfigLocation上下文参数来提供它的配置。root-context.xml文件提供了WebApplicationContext的详细配置信息。DispatcherServlet是Spring MVC应用程序的控制器类,所有客户端请求都由该servlet处理。配置从servlet-context.xml文件加载。

Spring MVC教程 – 配置文件

根上下文.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

我们可以在这里定义共享的bean,现在还没有任何内容。servlet-context.xml代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="https://www.springframework.org/schema/beans"
	xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.Olivia.spring" />	
	
</beans:beans>

标准的Spring配置文件是这样的,想象一下自己写下这些内容,你会喜欢使用STS工具。使用annotation-driven元素让Controller servlet知道将使用注释来进行bean配置。resources元素定义了可以放置静态文件(如图像、HTML页面等)的位置,我们不希望这些文件通过Spring框架来获取。InternalResourceViewResolver是视图解析器,我们可以通过前缀和后缀属性来提供视图页面的位置。因此,我们所有的JSP页面都应该放在/WEB-INF/views/目录下。context:component-scan元素用于提供扫描Controller类的基本包位置。记住在创建项目时给出的顶级包的值,这里使用的就是相同的值。

Spring MVC控制器类

控制器HomeController会自动创建并附带home()方法,虽然我稍微扩展了一下,添加了loginPage()和login()方法。

package com.Olivia.spring;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String loginPage(Locale locale, Model model) {
		return "login";
	}
	
	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String login(@Validated User user, Model model) {
		model.addAttribute("userName", user.getUserName());
		return "user";
	}
}

@Controller注解用于指示一个Web控制器类。@RequestMapping与类和方法一起使用,将客户端请求重定向到特定的处理方法。注意处理方法返回的是String类型,这应该是用作响应的视图页面的名称。正如您所看到的,我们有三个返回不同字符串的方法,因此我们需要创建具有相同名称的JSP页面。注意login()方法将使用HTTP方法POST调用,因此我们在此处期望一些表单数据。我们有一个User模型类,并且使用@Validated注解将其标记为进行验证。每个方法都包含Model作为参数,我们可以设置属性以在JSP响应页面中稍后使用。

Spring MVC模型类

模型类用于存储表单变量,我们的User模型bean如下所示。

package com.Olivia.spring;

public class User {

	private String userName;

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}
	
}

一个带有变量名及其getter和setter方法的简单Java bean。

春季MVC教程 – 视图页面

我们有三个JSP页面如下。home.jsp代码:

<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Hello world!  
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>

请注意使用JSP表达式语言获取属性值。login.jsp代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login Page</title>
</head>
<body>
<form action="home" method="post">
<input type="text" name="userName"><br>
<input type="submit" value="Login">
</form>
</body>
</html>

一个简单的JSP页面,用户可以输入用户名。注意,表单变量名与User类变量名相同。同时,表单的action是“home”,方法是“post”。很明显,HomeController的login()方法将处理这个请求。user.jsp代码:

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

用户的简单主页,显示用户名,注意这个属性是在登录方法中设定的。

Spring MVC示例应用程序测试

我们的应用程序已经准备好执行了,只需在VMware tc服务器或您选择的任何其他servlet容器上运行它,您将获得以下页面作为响应。这就是Spring MVC教程的全部内容,您可以看到使用STS插件创建Spring MVC应用程序是多么简单。代码体积非常小,大部分配置都由Spring MVC处理,以便我们可以专注于业务逻辑。从以下链接下载示例Spring MVC项目并尝试操作它。

下载Spring MVC项目

发表回复 0

Your email address will not be published. Required fields are marked *