【Spring】构建Spring Web应用
前言
学习了
Spring
的注解、AOP
后,接着学习Spring Web
,对于Web
应用开发,Spring
提供了Web
框架。
Web应用
Spring MVC初探
MVC
为(Model-View-Control
),当用户在浏览器中点击链接或提交表单时,请求经历的流程大致如下。
Spring MVC
所有的请求都会通过一个前端控制器(front controller servlet),也即是DispatcherServlet
,DispatcherServlet
用于将请求发送给Spring MVC
控制器,而处理器映射
会根据url
信息确定将请求发送给哪个控制器。- 当请求发送给控制器后,请求会等待控制器处理信息,更好的设计是控制器将请求交给服务对象处理。
- 控制器处理完后,会产生信息,该信息需要返回给用户并在浏览器上显示,这些信息称为
模型(Model)
。同时,这些信息需要以用户友好的方式进行格式化,此时需要将信息发送给一个视图(View)
进行处理。 DispatcherServlet
拿到模型及逻辑视图名后会使用视图解析器进行解析,将其转化为特定视图实现。- 指定视图实现后,需要将模型数据数据交付,视图将使用模型数据渲染输出。
配置DispatcherServlet
DispatcherServlet
是Spring MVC
的核心,其负责将请求路由到其他组件中。可通过将Servlet
配置在web.xml
中或者使用Java
显示编码方式将DispatcherServlet
配置在Servlet
容器中,本例中设置的SpittrWebAppInitializer
package ch5;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
其中
getServletMappings
方法会将一个或多个路径映射到DispatcherServlet
上,/
表示它是默认的Servlet
,将会处理所有进入应用的请求。
当DispatcherServlet
启动时,会创建Spring
应用上下文,并加载配置文件或配置类中所声明的bean
,如上述getServletConfigClasses
方法中返回的带有@Configuration
注解的所有定义在WebConfig
中的bean
。与此同时,在Spring Web
应用中,还会有另一个由ContextLoaderListener
创建的应用上下文,如getRootConfigClasses
方法中返回的带有@Configuration
注解的所有定义在RootConfig
中的bean
。AbstractAnnotationConfigDispatcherServletInitializer
会同时创建DispatcherServlet
和ContextLoaderListener
两个应用上下文,DispatcherServlet
应用上下文加载Web
组件的bean
,如控制器
、视图解析器
、处理映射器
;ContextLoaderListener
应用上下文加载其他bean
,如驱动应用后端的中间层
和数据层组件
。
配置Web组件并启动Spring MVC
可使用
<mvc:annotation-driven>
启动注解启动的Spring MVC
,也可使用@EnableWebMvc
注解启动。WebConfig
对应Web
组件配置,其代码如下。
package ch5;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan("ch5")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
}
}
其中配置了
JSP视图解析器
,也配置静态资源处理器
(对静态资源的请求转发到Servlet
容器中默认的Servlet
,而不是使用DispatcherServlet
处理)。
配置非Web组件
RootConfig
对应非Web
组件配置,其源码如下。
package ch5;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = {"ch5"},
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
})
public class RootConfig {
}
通过
RootConfig
的ComponentScan
注解可以添加很多非Web
组件,如驱动应用后端的中间层
和数据层组件
等。
控制器
控制器只是在方法上添加了
@RequestMapping
注解的类,该注解声明了它们所要处理的请求,如HomeController
用来处理对/
的请求,其源码如下。
package ch5;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
@RequestMapping(value = "/", method = GET)
public String home() {
return "home";
}
}
至此,已经完成所有编码,可以启动该项目,可正确在浏览器中显示页面。
测试控制器
Spring
提供了mock Spring MVC
来测试控制器HTTP
请求,这样测试时就不用启动浏览器了。HomeControllerTest
源码如下。
package ch5;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
运行可顺利通过测试。
定义类级别的请求处理
在前面的
HomeController
中,对于处理的请求路径是直接配置在方法上的,更好的处理是将其配置在类上,如下所示。
package ch5;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/")
public class HomeController {
@RequestMapping(method = GET)
public String home() {
return "home";
}
}
上述配置在类上与前面配置在方法上的效果相同,还可配置多个路径,如下所示。
package ch5;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {
@RequestMapping(method = GET)
public String home() {
return "home";
}
}
上述代码中除了配置处理路径
/
外,还可处理/homepage
路径。
传递模型数据至视图
一般情况下,需要传递模型数据至视图中进行渲染,此时,定义接口
SpittleRepository
。
package ch5;
import java.util.List;
public interface SpittleRepository {
List<Spittle> findSpittles(long max, int count);
}
定义接口
SpittleRepository
的实现子类SpittoleRepositoryImp
。
package ch5;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
public class SpittleRepositoryImp implements SpittleRepository {
public List<Spittle> findSpittles(long max, int count) {
List<Spittle> spittles = new ArrayList<Spittle>();
for (int i = 0; i < count; i++) {
spittles.add(new Spittle("Spittle " + i, new Date()));
}
return spittles;
}
}
定义
POJO
类Spittle
。
package ch5;
import java.util.Date;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Spittle {
private final Long id;
private final String message;
private final Date time;
private Double latitude;
private Double longitude;
public Spittle(String message, Date time) {
this(null, message, time, null, null);
}
public Spittle(Long id, String message, Date time, Double longitude, Double latitude) {
this.id = id;
this.message = message;
this.time = time;
this.longitude = longitude;
this.latitude = latitude;
}
public long getId() {
return id;
}
public String getMessage() {
return message;
}
public Date getTime() {
return time;
}
public Double getLongitude() {
return longitude;
}
public Double getLatitude() {
return latitude;
}
@Override
public boolean equals(Object that) {
return EqualsBuilder.reflectionEquals(this, that, "id", "time");
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "id", "time");
}
}
在
/WEB-INF/views
目录下创建spittles.jsp
文件,内容如下。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />" >
</head>
<body>
<div class="listTitle">
<h1>Recent Spittles</h1>
<ul class="spittleList">
<c:forEach items="${spittleList}" var="spittle" >
<li id="spittle_<c:out value="spittle.id"/>">
<div class="spittleMessage"><c:out value="${spittle.message}" /></div>
<div>
<span class="spittleTime"><c:out value="${spittle.time}" /></span>
<span class="spittleLocation">(<c:out value="${spittle.latitude}" />, <c:out value="${spittle.longitude}" />)</span>
</div>
</li>
</c:forEach>
</ul>
</div>
</body>
</html>
添加
SpittleController
控制器。
package ch5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/spittles")
public class SpittleController {
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
@RequestMapping(method = RequestMethod.GET)
public String spittles(Model model) {
model.addAttribute("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));
return "spittles";
}
}
运行,可正确显示20个
Spittle
实例信息。
接受请求的参数
Spring MVC
允许以多种方式将客户端的数据传送到控制器的处理器方法中,包括查询参数
、表单参数
、路径变量
。
处理查询参数
可让用户指定
findSpittles
方法中的max
和count
两个参数,并且在未指定时使用缺省值,修改SpittleController
如下。
package ch5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
@RequestMapping("/spittles")
public class SpittleController {
private static final String MAX_LONG_AS_STRING = "9223372036854775807";
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
@RequestMapping(method = RequestMethod.GET)
public List<Spittle> spittles(
@RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max,
@RequestParam(value = "count", defaultValue = "20") int count) {
return spittleRepository.findSpittles(max, count);
}
}
值得注意的是,此时并没有指定视图,但是启动后仍然可以正确显示结果,这是由于视图未指定情况下与
@RequestMapping("/spittles")
的spittles
相同,若换成其他路径,如/spittles_test
则报无法找到**/spittles_test.jsp
的错误。
处理路径参数
使用
/spittles?show?spittle_id=123
的方式可以传递参数,但是更好的一种方法是使用/spittles/123
方式请求,该方式优于前种方式。修改SpittleController
如下。
package ch5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/spittles")
public class SpittleController {
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
@RequestMapping(value = "/{spittleId}", method = RequestMethod.GET)
public String spittles(
@PathVariable("spittleId") long spittleId, Model model) {
System.out.println("spittleId = " + spittleId);
System.out.println(spittleRepository.findOne(spittleId).getMessage());
model.addAttribute(spittleRepository.findOne(spittleId));
return "spittle";
}
}
再添加
spittle.jsp
页面,其源码如下。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet"
type="text/css"
href="<c:url value="/resources/style.css" />">
</head>
<body>
<div class="spittleView">
<div class="spittleMessage"><c:out value="${spittle.message}"/></div>
<div>
<span class="spittleTime"><c:out value="${spittle.time}"/></span>
</div>
</div>
</body>
</html>
运行即可得到正确结果。
处理表单
Web
应用需要通过表单与用户进行交互,需要展示表单数据和处理用户通过表单提交的数据。对于表单的展示。
- 添加
SpitterController
如下
package ch5;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/spitter")
public class SpitterController {
private SpitterRepository spitterRepository;
@Autowired
public SpitterController(SpitterRepository spitterRepository) {
this.spitterRepository = spitterRepository;
}
@RequestMapping(value = "/register", method = GET)
public String showRegistrationForm() {
return "registerForm";
}
@RequestMapping(value = "/register", method = POST)
public String processRegistration(Spitter spitter) {
spitterRepository.save(spitter);
return "redirect:/spitter/" + spitter.getUsername();
}
@RequestMapping(value = "/{username}", method = GET)
public String showSpitterProfile(@PathVariable String username, Model model) {
Spitter spitter = spitterRepository.findByUsername(username);
model.addAttribute(spitter);
return "profile";
}
}
- 添加
SpitterRepository
如下
package ch5;
public interface SpitterRepository {
Spitter findByUsername(String username);
void save(Spitter spitter);
}
- 添加
SpitterRepositoryImp
,源码如下。
package ch5;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class SpitterRepositoryImp implements SpitterRepository {
Map<String, Spitter> spitters = new HashMap<String, Spitter>();
public void save(Spitter spitter) {
spitters.put(spitter.getUsername(), spitter);
}
public Spitter findByUsername(String username) {
return spitters.get(username);
}
}
- 添加
registerForm.jsp
文件,内容如下。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css"
href="<c:url value="/resources/style.css" />" >
</head>
<body>
<h1>Register</h1>
<form method="POST">
First Name: <input type="text" name="firstName" /><br/>
Last Name: <input type="text" name="lastName" /><br/>
Email: <input type="email" name="email" /><br/>
Username: <input type="text" name="username" /><br/>
Password: <input type="password" name="password" /><br/>
<input type="submit" value="Register" />
</form>
</body>
</html>
运行后,访问
http://localhost:8080/spitter/register
即可正常显示表单。
处理表单控制器
当成注册表单的
POST
请求时,控制器需要接受表单数据并将表单数据保存为Spitter
对象,在注册完成后重定向至用户的基本信息页面,修改SpitterController
如下
package ch5;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/spitter")
public class SpitterController {
private SpitterRepository spitterRepository;
@Autowired
public SpitterController(SpitterRepository spitterRepository) {
this.spitterRepository = spitterRepository;
}
@RequestMapping(value = "/register", method = GET)
public String showRegistrationForm() {
return "registerForm";
}
@RequestMapping(value = "/register", method = POST)
public String processRegistration(Spitter spitter) {
spitterRepository.save(spitter);
return "redirect:/spitter/" + spitter.getUsername();
}
@RequestMapping(value = "/{username}", method = GET)
public String showSpitterProfile(@PathVariable String username, Model model) {
Spitter spitter = spitterRepository.findByUsername(username);
model.addAttribute(spitter);
return "profile";
}
}
添加
profile.jsp
文件,内容如下
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />">
</head>
<body>
<h1>Your Profile</h1>
<c:out value="${spitter.username}"/><br/>
<c:out value="${spitter.firstName}"/> <c:out value="${spitter.lastName}"/><br/>
<c:out value="${spitter.email}"/>
</body>
</html>
运行后,访问
http://localhost:8080/spitter/register
完成注册后会成功返回到用户信息页面。
总结
本篇博文讲解了
Spring Web
相关的知识点,其核心是DispatcherServlet
来派发请求,借助框架,可以快速开发Web
应用。
【Spring】构建Spring Web应用的更多相关文章
- 2.Spring构建REST Web Service
上篇文章我们已经对Spring 已经有了一个初步的认识,接下来本篇文章我们将继续一起在官网学习新技术. 原文地址:https://spring.io/guides/gs/rest-service/ 本 ...
- 使用 Spring 3 MVC HttpMessageConverter 功能构建 RESTful web 服务
原文地址:http://www.ibm.com/developerworks/cn/web/wa-restful/ 简介: Spring,构建 Java™ 平台和 Enterprise Edition ...
- 构建一个基于 Spring 的 RESTful Web Service
本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...
- Spring实战5:基于Spring构建Web应用
主要内容 将web请求映射到Spring控制器 绑定form参数 验证表单提交的参数 对于很多Java程序员来说,他们的主要工作就是开发Web应用,如果你也在做这样的工作,那么你一定会了解到构建这类系 ...
- 使用XFire+Spring构建Web Service(一)——helloWorld篇
转自:http://www.blogjava.net/amigoxie/archive/2007/09/26/148207.html原文出处:http://tech.it168.com/j/2007- ...
- Spring Boot——2分钟构建spring web mvc REST风格HelloWorld
之前有一篇<5分钟构建spring web mvc REST风格HelloWorld>介绍了普通方式开发spring web mvc web service.接下来看看使用spring b ...
- 使用XFire+Spring构建Web Service
XFire是与Axis 2并列的新一代Web Service框架,通过提供简单的API支持Web Service各项标准协议,帮助你方便快速地开发Web Service应用. 相 对于Axis来说,目 ...
- [转]Spring Boot——2分钟构建spring web mvc REST风格HelloWorld
Spring Boot——2分钟构建spring web mvc REST风格HelloWorld http://projects.spring.io/spring-boot/ http://spri ...
- 《Spring实战》学习笔记-第五章:构建Spring web应用
之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. ...
- SpringBoot实战(十)之使用Spring Boot Actuator构建RESTful Web服务
一.导入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http ...
随机推荐
- Atom的追踪函数插件和自定义语法
atom网盘链接:http://pan.baidu.com/s/1nvt7vJz 密码:om26 追踪函数插件 直接使用最新autom版本傻瓜式安装 名字:goto-definition 地址:htt ...
- Tiled Editor 图块的两种导入方式
一.图块集图块的导入. 打开或者创建地图后,新建 新图块. 弹出新图块面板 图块类型选择 "基于图块集图块",一定要选择"嵌入地图",否则需要另存为其他类型的文 ...
- MVC查询数据接收及校验
本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了. 这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询, ...
- .NET 动态脚本语言
Script.NET (S#) 是一种允许为你的应用程序自定义行为,与本地.NET对象.类型和组件交互动态的脚本语言.托管应用程序本身可以改变一个xml配置脚本运行时的默认行为,更换绑定的方法,属性, ...
- java 多态(动态绑定)
一.面向对象最核心的机制--动态绑定,也叫多态 1.1.通过下面的例子理解动态绑定,即多态 1 package javastudy.summary; 2 3 class Animal { 4 /** ...
- Apache Spark 2.2.0 中文文档 - 集群模式概述 | ApacheCN
集群模式概述 该文档给出了 Spark 如何在集群上运行.使之更容易来理解所涉及到的组件的简短概述.通过阅读 应用提交指南 来学习关于在集群上启动应用. 组件 Spark 应用在集群上作为独立的进程组 ...
- pdf去水印
问: 我用Adobe acrobat professional 7.0 版想去掉添加的水印,不知道如何删除,请各位大 侠指点! 答:1.(功能表)工具→高级编辑工具→TouchUp对象工具 2.用滑鼠 ...
- 集 降噪 美颜 虚化 增强 为一体的极速图像润色算法 附Demo程序
在2015年8月份的时候,决心学习图像算法. 几乎把当时市面上的图像算法相关书籍都看了一遍, 资金有限,采取淘宝买二手书,长期驻留深圳图书馆的做法, 进度总是很慢,学习算法不得其法. 虽然把手上所有书 ...
- 关于Android路由的实现
先说一下背景,目前有需求从外部包括其他应用和WEB跳转到我们自己的APP,就这么个简单的需求-- 要实现这种外部跳转的功能,我们可以理解为打算跳转的一方有多少方式通知到APP进行相对的响应行为.所以, ...
- java数据库编程之DAO模式
第八章:DAO模式 8.1:JDBC封装 为了提高代码的维护性和扩展性,我们使用JDBC进行封装数据, 先定义统一的API,将操作数据的代码抽象到接口中,业务逻辑代码只需要调用这些接口的实现类的对象, ...