软件开发实训(720科技)Spring MVC 第四章 基于注解的控制器

本文详细介绍了Spring MVC框架中@Controller和@RequestMapping注解的使用方法,包括控制器类的定义、请求映射配置、依赖注入实现等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内容关键字  :
spring mvc 注解类型
controller注解类型 
requestmapping 注解类型 
编写请求控制的方法    
controlller 类
依赖注入 

授课老师 720科技 张森鹏

一:知识笔记

4.1 Spring MVC 注解类型
使用基于注解的控制器的几个优点。其一,一个控制器类可以处理多个动作(而实现了 Controller 接口的一个控制器只能处理一个动作)。这就允许将相关的操作写在同一个控制器类中,从而减少应用程序中类的数量。 其二,基于注解的控制器的请求映射不需要存储在配置文件中。使用RequestMapping 注释类型,可以对一个方法进行请求处理。 Controller RequestMapping 注释类型是 Spring MVC API 最重要的两个注解类型。本章重点介绍这两个,并简要介绍了一些其他不太流行的注解类型。 4.1.1 Controller 注解类型 org.springframework.stereotype.Controller 注解类型用于指示 Spring 类的实例是一个控制器。下面是一个带注解@Controller 的例子。
package com.example.controller;
import org.springframework.stereotype;
...
@Controller
public class CustomerController {
// request-handling methods here }
Spring 使用扫描机制来找到应用程序中所有基于注解的控制器类。为了保证 Spring 能找到你的控制器,需要完成两件事情。首先,需要在 Spring MVC 的配置文件中声明 spring- context,如下所示:
<beans ...
... >
然后,需要应用<component-scan/>元素,如下所示:

<context:component-scan base-package="basePackage"/>

请在<component-scan/>元素中指定控制器类的基本包。例如,若所有的控制器类都在

com.example.controller 及其子包下,则需要写一个如下所示的<component-scan/>元素:

<context:component-scan base-package="com.example.controller"/>

现在,整个配置文件看上去如下所示:

<?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: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.xsd">

 

<context:component-scan base-package="com.example.controller"/>

 <!-- ... -->

</beans>

请确保所有控制器类都在基本包下,并且不要指定一个太广泛的基本包(如指定 com.example, 而非com.example.controller,前者就更广泛),因为这会使得Spring MVC 扫描了无关的包。

 4.1.2 RequestMapping 注解类型

现在,我们需要在控制类的内部为每一个动作开发相应的处理方法。要让 Spring 知道用

哪一种方法来处理它的动作,需要使用 org.springframework.web.bind.annotation.Request

Mapping 注解类型映射的 URI 与方法。

RequestMapping 注解类型的作用同其名字所暗示的:映射一个请求和一种方法。可以使用@RequestMapping 注解一种方法或类。一个采用@RequestMapping 注解的方法将成为一个请求处理方法,并由调度程序在接收到对应 URL 请求时调用。

下面是一个 RequestMapping 注解方法的控制器类。

package com.example.controller;

import org.springframework.stereotype.Controller;

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

...

 @Controller

public class CustomerController {

 @RequestMapping(value = "/input-customer ") public String inputCustomer() {

 // do something here

 return "CustomerForm";

}

}

 使用 RequestMapping 注解的 value 属性将 URI 映射到方法。在上面的例子中,我们将

input-customer 映射到 inputCustomer 方法。这样,可以使用如下 URL 访问 inputCustomer 方法。

http://domain/context/input-customer

由于 value 属性是 RequestMapping 注释的默认属性,因此,若只有唯一的属性,则可以省略属性名称。换句话说,如下两个标注含义相同。

@RequestMapping(value = "/input-customer ") @RequestMapping("/input-customer ")

但如果有多个属性时,就必须写入 value 属性名称。

请求映射的值可以是一个空字符串,此时该方法被映射到以下网址:

http://domain/context

RequestMapping 除了具有 value 属性外,还有其他属性。例如,method 属性用来指示该方法仅处理哪些 HTTP 方法。

例如,仅当在HTTP POST PUT 方法时,才访问到下面的 ProcessOrder 方法。...

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod;

...

@RequestMapping(value="/process-order", method={RequestMethod.POST, RequestMethod.PUT})

public String processOrder() {

 // do something here

 return "OrderForm";

}

 method 属性只有一个HTTP 方法值,则无需花括号。例如,

@RequestMapping(value="/process-order", method=RequestMethod.POST)

如果没有指定 method 属性值,则请求处理方法可以处理任意 HTTP 方法。此外,RequestMapping 注解类型也可以用来注解一个控制器类,如下所示:

import org.springframework.stereotype.Controller;

...

 @Controller @RequestMapping(value="/customer") public class CustomerController {

在这种情况下,所有的方法都将映射为相对于类级别的请求。例如,下面的 deleteCustomer

方法。

...

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

...

@Controller @RequestMapping("/customer") public class CustomerController {

 @RequestMapping(value="/delete", method={RequestMethod.POST, RequestMethod.PUT})

public String deleteCustomer() {

 // do something here

return ...;

}

 由于控制器类的映射使用“/customer”,而 deleteCustomer 方法映射为“/delete”,则如下

URL 会映射到该方法上。

http://domain/context/customer/delete

 4.2 编写请求处理方法

每个请求处理方法可以有多个不同类型的参数,以及一个多种类型的返回结果。例如, 如果在请求处理方法中需要访问HttpSession 对象,则可以添加的 HttpSession 作为参数,Spring会将对象正确地传递给方法。

@RequestMapping("/uri")

public String myMethod(HttpSession session) {

...

session.addAttribute(key, value);

...

}

或者,如果需要访问客户端语言环境和 HttpServletRequest 对象,则可以在方法签名上包括这样的参数:

@RequestMapping("/uri")

public String myOtherMethod(HttpServletRequest request, Locale locale) {

...

// access Locale and HttpServletRequest here

...

}

 下面是可以在请求处理方法中出现的参数类型:

— javax.servlet.ServletRequest javax.servlet.http.HttpServletRequest

— javax.servlet.ServletResponse javax.servlet.http.HttpServletResponse

— javax.servlet.http.HttpSession

— org.springframework.web.context.request.WebRequest org.springframework.web.

context.request.NativeWebRequest


4.2 编写请求处理方法

— java.util.Locale

— java.io.InputStream java.io.Reader

— java.io.OutputStream java.io.Writer

— java.security.Principal

— HttpEntity<?>paramters

— java.util.Map/org.springframework.ui.Model /

— org.springframework.ui.ModelMap

— org.springframework.web.servlet.mvc.support.RedirectAttributes

— org.springframework.validation.Errors /

— org.springframework.validation.BindingResult

— 命令或表单对象。

— org.springframework.web.bind.support.SessionStatus

— org.springframework.web.util.UriComponentsBuilder

— @PathVariable@MatrixVariable@RequestParam@RequestHeader@RequestBody @RequestPart 注释的对象。

特别重要的是 org.springframework.ui.Model 类型。这不是一个 Servlet API 类型,而是一个包含 Map Spring MVC 类型。每次调用请求处理方法时,Spring MVC 都创建 Model 对象并将其 Map 注入到各种对象。

请求处理方法可以返回如下类型的对象:

— ModelAndView

— Model

— 包含模型的属性的 Map

— View

— 代表逻辑视图名的 String

— void


 

— 提供对 Servlet 的访问,以响应HTTP 头部和内容 HttpEntity ResponseEntity 对象。

— Callable

— DeferredResult

— 其他任意类型,Spring 将其视作输出给 View 的对象模型。

本章后续会展示一个例子,以进一步学习如何开发一个请求处理方法。

 

4.3 应用基于注解的控制器

本章的示例应用 annotated1 基于第 2 章和第 3 章的例子重写,展示了包含有两个请求处理方法的一个控制器类。

annotated1 和前面的应用程序间的主要区别在于,annotated1 的控制器类增加了注解

@Controller。此外,Spring 配置文件也增加了一些元素,后续小节中会详细介绍。

4.3.1 目录结构

4.1 展示了 annotated1 的目录结构。注意,annotated1 中只有一个控制器类,而不是两个,同时新增了一个名为 index.html HTML 文件,以便 Spring MVC Servlet URL 模式设置为“/”时,依然可以访问静态资源。

 

4.3.2 配置文件

annotated1 有两个配置文件。第一个为部署描述符(web.xml 文件)中注册 Spring MVC

Dispatcher Servlet。第二个为 springmvc-config.xml,即 Spring MVC 的配置文件。

清单 4.1 和清单 4.2 分别展示部署描述符和 Spring MVC 的配置文件。

清单 4.1 annotated1(web.xml)的部署描述符

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

 

<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/config/springmvc-config.xml

</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

 

<servlet-mapping>

<servlet-name>springmvc</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>

4.1 annotated1 的目录结构。

 

4.1    annotated1 的目录结构

另外,在部署描述符中的<servlet-mapping/>元素,Spring MVC 的 dispatcher-servlet 的 URL

 

模式设置为“/”,当 URL 模式设置为“/”时,意味着所有请求(包括那些用于静态资源)都被映射到Dispatcher Servlet。为了正确处理静态资源,需要在 Spring MVC 配置文件中添加一些<resources/>元素。

清单 4.2 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" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/springcontext.xsd">

<context:component-scan base-package="controller"/>

<mvc:annotation-driven/>

<mvc:resources mapping="/css/**" location="/css/"/>

<mvc:resources mapping="/*.html" location="/"/>

 

<bean id="viewResolver" class="org.springframework.web.servlet.view.

InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/jsp/"/>

<property name="suffix" value=".jsp"/>

</bean>

</beans>

清单 4.2Spring MVC 的配置文件)中最主要的是<component-scan/>元素。这是要指示Spring

MVC  扫描目标包中的类,本例是  controller  包。接下去是一个<annotation-driven/>元素和两个

<resources/>元素。<annotation-driven/>元素做了很多的事情,其中包括注册用于控制器注解的bean

对象。<resources/>元素则指示Spring MVC 哪些静态资源需要单独处理(不通过Dispatcher Servlet)。

在清单 4.2 的配置文件中,有两个<resources/>元素。第一个确保在/ CSS 目录下的所有文件可见,第二个允许显示所有的.html 文件。

注意

如果没有<annotation-driven/><resources/>元素会阻止任意控制器被调用。若不需要使


 

resources,则不需要<annotation-driven/>元素。

4.3.3 Controller 

如前所述,使用 Controller 注释类型的一个优点在于:一个控制器类可以包含多个请求处理方法。如清单 4.3 所示,ProductController 类中有 inputProduct saveProduct 两个方法。

清单 4.3 ProductController 

package controller;

import java.match.Bigoecimal

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping; import domain.Product;

import form.ProductForm;

 

@Controller

public class ProductController {

 

private static final Log logger = LogFactory.getLog(ProductController.class);

 

@RequestMapping(value="/input-product") public String inputProduct() {

logger.info("inputProduct called"); return "ProductForm";

}

 

@RequestMapping(value="/save-product")

public String saveProduct(ProductForm productForm, Model model){ logger.info("saveProduct called");

// no need to create and instantiate a ProductForm

// create Product

Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try {

product.setPrice(new BigDecimal(productForm.getPrice()));

} catch (NumberFormatException e) {


 

}

 

// add product model.addAttribute("product", product); return "ProductDetails";

}

}

 

其中,ProductController saveProduct 方法的第二个参数是 org.springframework.ui.Model 类型。无论是否会使用,Spring MVC 都会在每一个请求处理方法被调用时创建一个 Model 实例,用于增加需要显示在视图中的属性。例如,通过调用 model.addAttribute 来添加 Product 实例:

model.addAttribute("product", product);

Product 实例就可以像被添加到 HttpServletRequest 中那样访问了。

 

4.3.4 View

annotated1 也有与前面章节示例类似的两个视图:ProductForm.jsp 页面(见清单 4.4)和

ProductDetails.jsp 页面(见清单 4.5)。

清单 4.4 ProductForm.jsp 页面

<!DOCTYPE HTML>

<html>

<head>

<title>Add Product Form</title>

<style type="text/css">@import url(css/main.css);</style>

</head>

<body>

 

<div id="global">

<form action="save-product" method="post">

<fieldset>

<legend>Add a product</legend>

<p>

<label for="name">Product Name: </label>

<input type="text" id="name" name="name" tabindex="1">

</p>

<p>

<label for="description">Description: </label>

<input type="text" id="description" name="description" tabindex="2">

</p>


 

<p>

 

<label for="price">Price: </label>

<input type="text" id="price" name="price" tabindex="3">


</p>

<p id="buttons">

<input id="reset" type="reset" tabindex="4">

<input id="submit" type="submit" tabindex="5" value="Add Product">

</p>

</fieldset>

</form>

</div>

</body>

</html>

清单 4.5 ProductDetails.jsp 页面

<!DOCTYPE HTML>

<html>

<head>

<title>Save Product</title>

<style type="text/css">@import url(css/main.css);</style>

</head>

<body>

<div id="global">

<h4>The product has been saved.</h4>

<p>

<h5>Details:</h5>

Product Name: ${product.name}<br/> Description: ${product.description}<br/> Price: $${product.price}

</p>

</div>

</body>

</html>

 

4.3.5 测试应用
在浏览器中输入如下 URL 来测试 annotated1

http://localhost:8080/annotated1/input-product

浏览器会显示Product 表单,如图 4.2 所示。

单击“Add Product”按钮,会调用 saveProduct 方法。


 

4.2 Product 表 单

 

4.4 应用@Autowired 和@Service 进行依赖注入

使用 Spring 框架的一个好处是容易进行依赖注入。毕竟,Spring 框架一开始就是一个依赖注入容器。将依赖注入到Spring MVC 控制器的最简单方法是,通过注解@Autowired 到字段或方法。Autowired 注解类型属于 org.springframework.beans.factory.annotation 包。

此外,为了能作为依赖注入,类必须要注明为@Service。该类型是 org.springframework.

stereotype 包的成员。Service 注解类型指示类是一个服务。此外,在配置文件中,还需要添加一个<component-scan/>元素来扫描依赖基本包。

<context:component-scan base-package="dependencyPackage"/>

下面以 annotated2 应用进一步说明 Spring MVC 如何应用依赖注入。在 annotated2 应用程序中,ProductController 类(见清单 4.6)已经不同于 annotated1 中的类。

清单 4.6 annotated2 中的 ProductController 

package controller;

import java.math.BigDecimal;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

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;

import org.springframework.web.servlet.mvc.support.RedirectAttributes; import domain.Product;

import form.ProductForm; import service.ProductService;

 

@Controller

public class ProductController {

private static final Log logger = LogFactory

.getLog(ProductController.class);

 

@Autowired

private ProductService productService;

 

@RequestMapping(value = "/input-product ") public String inputProduct() {

logger.info("inputProduct called"); return "ProductForm";

}

 

@RequestMapping(value = "/save-product ", method = RequestMethod.POST) public String saveProduct(ProductForm productForm,

RedirectAttributes redirectAttributes) { logger.info("saveProduct called");

// no need to create and instantiate a ProductForm

// create Product

Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try {

product.setPrice(new BigDecimal(productForm.getPrice()));

} catch (NumberFormatException e) {

}

 

// add product

Product savedProduct = productService.add(product);

 

redirectAttributes.addFlashAttribute("message", "The product was successfully added.");

return "redirect:/product-view/" + savedProduct.getId();

}

 

@RequestMapping(value = "/view-product/{id}")

public String viewProduct(@PathVariable Long id, Model model) { Product product = productService.get(id);


 

model.addAttribute("product", product); return "ProductView";

}

}

 

annotated1 中相比,annotated2 中的 ProductController 类做了一系列的调整。首先是在如下的私有字段上增加了@Autowired 注解:

@Autowired

private ProductService productService

ProductService 是一个提供各种处理产品的方法的接口。为 productService 字段添加

@Autowired 注解会使ProductService 的一个实例被注入到ProductController 实例中。

清单 4.7 和清单 4.8 分别显示了 ProductService 接口及其实现类 ProductServiceImpl。注意, 为了使类能被 Spring 扫描到,必须为其标注@Service

清单 4.7 ProductService 接口

package service

import domain.Product;

public interface ProductService { Product add(Product product); Product get(long id);

}

 

清单 4.8 ProductServiceImpl 

package service;

import java.math.BigDecimal; import java.util.HashMap; import java.util.Map;

import java.util.concurrent.atomic.AtomicLong; import org.springframework.stereotype.Service; import domain.Product;

 

@Service

public class ProductServiceImpl implements ProductService {

 

private Map<Long, Product> products = new HashMap<Long, Product>();

private AtomicLong generator = new AtomicLong();

 

public ProductServiceImpl() { Product product = new Product();


product.setName("JX1 Power Drill"); product.setDescription(

"Powerful hand drill, made to perfection"); product.setPrice(new BigDecimal(129.99));

 

add(product);

}

 

@Override

public Product add(Product product) {

long newId = generator.incrementAndGet(); product.setId(newId);

products.put(newId, product); return product;

}

 

@Override

public Product get(long id) { return products.get(id);

}

}

 

如清单 4.9 所示,annotated2 Spring MVC 配置文件中有两个<component-scan/>元素: 一个用于扫描控制器类,另一个用于扫描服务类。

清单 4.9 Spring MVC 配置文件

<?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:mvc="http://www.springframework.org/schema/mvc" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/springcontext.xsd">

 

<context:component-scan base-package="controller"/>

<context:component-scan base-package="service"/>

<mvc:annotation-driven/>

<mvc:resources mapping="/css/**" location="/css/"/>

<mvc:resources mapping="/*.html" location="/"/>

 

<bean id="viewResolver"


 

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/jsp/"/>

<property name="suffix" value=".jsp"/>

</bean>

</beans>



二:重要记录

一个带注解@Controller 的例子。
package com.example.controller;

  import org.springframework.stereotype;
...

 @Controller

public class CustomerController {

// request-handling methods here

}



Spring 使用扫描机制来找到应用程序中所有基于注解的控制器类。为了保证 Spring 能找到你的控制器,需要完成两件事情。首先,需要在 Spring MVC 的配置文件中声明 spring- context 然后,需要应用<component-scan/>元素

每个请求处理方法可以有多个不同类型的参数,以及一个多种类型的返回结果。例如, 如果在请求处理方法中需要访问HttpSession 对象,则可以添加的 HttpSession 作为参数,Spring会将对象正确地传递给方法。

org.springframework.ui.Model 类型。这不是一个 Servlet API 类型,而是一个包含 Map Spring MVC 类型。每次调用请求处理方法时,Spring MVC 都创建 Model 对象并将其 Map 注入到各种对象。

annotated1 和前面的应用程序间的主要区别在于,annotated1 的控制器类增加了注解 @Controller

annotated1 有两个配置文件。第一个为部署描述符(web.xml 文件)中注册 Spring MVC Dispatcher Servlet。第二个为 springmvc-config.xml,即 Spring MVC 的配置文件。

 annotated1 的目录结构







三:学习参考
spring mvc 学习指南 第二版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值