Spring MVC-04循序渐进之基于注解的控制器(上)

简介: Spring MVC-04循序渐进之基于注解的控制器

概述


Spring MVC-03循序渐进之Spring MVC中我们介绍了传统的开发方式,其弊端Controller接口实现类只能处理一个单一动作,本篇博文我们来介绍下基于注解的控制器。


Spring MVC注解类型


基于注解的控制器优点如下:


一个控制器可以处理多个请求动作,而一个实现了Controller接口的控制器只能处理一个动作

基于注解的控制器的请求映射不需要存储在配置文件中,使用RequestMapping注解类型,可以对一个方法进行请求处理。


Controller和RequestMapping注解类型是SpringMVC API最重要的两个注释类型,当然了我们这里也会介绍其他一些注解类型


Controller注解类型


org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器。


下面是一个带有@Controller注解的例子

import org.springframework.stereotype.Controller;
@Controller
public class ArtisanController {
}

Spring使用注解扫描的方式来找到应用中所有基于注解的控制器类,为了确保Spring能扫描到你的控制器,需要完成两件事情

  1. 在Spring MVC配置文件中声明spring-context及指定schema
  2. 然后配置component-scan扫描路径
<?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: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.artisan.springmvc.controller"/>
   <!--......省略其他配置项--->
</beans>


确保所有控制器类都在基本包下,并且不要指定一个太宽泛的基本包,这样会使Spring扫描了无关的包。


RequestMapping注解类型


现在我们需要在控制器类内部为每一个动作开发相应的处理方法,要让Spring知道哪一种方法来处理它的动作,需要使用org.springframework.web.bind.annotation.RequestMapping注解类型映射的URL与方法。


RequestMapping注释类型的作用:映射一个请求和一种方法,可以使用@RequestMapping注释一种方法或者一个类


一个采用了@RequestMapping注解的方法将成为一个请求处理方法,并由调度程序在接收到对应的URL请求时调用


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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ArtisanController {
    @RequestMapping(value="/doSomething")
    public String doSomething() {
        // do some bussiness logic here
        return "custoomer";
    }
}


使用RequestMapping注解的value属性将URI映射到方法。在上面的例子中,我们将doSomething映射到doSomething方法,这样,就可以使用如下URL访问doSomething方法

http://domain/context/doSomething


value属性

由于value属性是RequestMapping注解的默认属性,如果只有唯一的属性,则可以省略属性名称。换句话说

    @RequestMapping(value="/doSomething")
    @RequestMapping("/doSomething")


效果是等同的,但是如果超过一个属性时,就必须要输入value属性名称。

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


其他属性


RequestMapping除了具有value属性,还有其他属性。比如method属性用来指示改方法仅处理哪些HTTP方法. 当method为多个值时,后面写为数组{method1, method2}

例如只有在HTTP POST或者PUT方法时才能访问到下面的方法

@Controller
public class ArtisanController {
    @RequestMapping(value="/doSomething",method={RequestMethod.POST,RequestMethod.PUT})
    public String doSomething() {
        // do some bussiness logic here
        return "customer";
    }
}


如果method属性仅有一个HTTP方法值,则不需要花括号

@RequestMapping(value="/doSomething",method=RequestMethod.POST)


如果没有指定method属性值,则请求处理方法可以处理任意HTTP方法。


此外RequestMapping注释类型也可以用来注释一个控制器类

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/artisan")
public class ArtisanController {
}


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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/artisan")
public class ArtisanController {
    @RequestMapping(value="/delete",method={RequestMethod.POST,RequestMethod.PUT})
    public String deleteArtisan() {
        // do delete opertaion
        return "artisanList";
    }
}


由于控制器类的映射使用了“/artisan” ,而deleteArtisan方法映射为/delete,则如下的URL将映射到该方法上

htpp://domain/context/artisan/delete


编写请求处理方法


每个请求处理方法可以有多个不同类型的参数,以及一个多钟类型的返回结果。


比如在请求处理方法中需要访问HttpSession对象,则可以添加HttpSession作为参数,Spring会将对象正确传递给方法

    @RequestMapping("/uri")
    public String method(HttpSession session){
        // do something
        session.setAttribute(key, value);
        return ...;
    }


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

@RequestMapping("/uri")
    public String method(HttpServletRequest request,Locale locale){
        // access HttpServletRequest or Locale here 
        return ...;
    }



每个请求处理方法可以有多个不同类型的参数,下面时可以在请求处理方法中出现的参数类型:


javax.servlet.ServletRequest 或 javax.servlet.HttpServletRequest

javax.servlet.ServletResponse

或 javax.servlet.httpHttpServletResponse

javax.servlet.http.HttpSession

org.springframework.web.context.request.WebRequest

或 org.springframework.web.context.request.nativeWebRequest

java.util.Locale

java.io.InputStream 或 java.io.Reader

java.io.OutputStream 或 java.io.Writer

java.security.Principal

HttpEntity<?>

java.util.Map 或 org.springframework.ui.Model

org.springframework.ui.ModelMap

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

org.springframework.validation.Errors

orgspringframework.validation.BindingResult

命令或表单对象

org.springframework.web.util.UriCompontsBuilder

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的对象模型


应用基于注解的控制器


该处的示例是对前面几篇博文的重写,区别于前几篇博文中的示例在于

  • 控制器类中增加了@Controller注解
  • Spring配置文件增加了部分元素,下面详解


目录结构


20180120204514700.jpg


maven工程结构如上,在这里,只有一个控制器类,而不是之前示例中的两个。

同时增加了一个名为index.html的静态文件,以便Spring MVC Servlet的URL模式设置为”/”时,依然可以访问静态资源


配置文件


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


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.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>



在部署描述符中servlet-mapping元素中url-pattern设置为 / ,而不是之前实例中的action。 实际上映射动作不必一定是要用某种URL扩展。


当然,当URL设置为/,意味着所有的请求( 包括那些静态资源)都被映射到DispatcherServlet, 为了正确的处理静态资源,就必须要在Spring MVC的配置文件中添加一些 resouce元素。


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/spring-context.xsd">
    <context:component-scan base-package="com.artisan.springmvc.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>


Spring MVC配置文件中最重要的是context:component-scan元素,这是要告诉SpringMVC扫描目标包中的类。


接下来是一个mvc:annotation-driven和两个mvc:resources。


mvc:annotation-driven元素做的事情内包括注册用于支持基于注解的控制器的请求处理方法的bean对象


mvc:resources元素用于指示Spring MVC 哪些静态资源需要单独处理,即不通过Dispatcher Servlet


在这个示例中,第一个resources元素确保/css目录下的所有文件可见

第二个允许显示所有的.html文件


注意:如果没有annotation-driven,resources元素会阻止任意控制器被调用,如果不需要使用resources,则不需要annotation-driven元素


Controller类


使用Controller注释类型的一个优点在于:一个控制器类可以包含多个请求处理方法

package com.artisan.springmvc.controller;
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 com.artisan.springmvc.domain.Product;
import com.artisan.springmvc.form.ProductForm;
@Controller
public class ProductController {
    private static final Log logger = LogFactory.getLog(ProductController.class);
    @RequestMapping(value="/product_input")
    public String inputProduct() {
        logger.info("inputProduct called");
        return "ProductForm";
    }
    @RequestMapping(value="/product_save")
    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(Float.parseFloat(
                    productForm.getPrice()));
        } catch (NumberFormatException e) {
        }
        // add product
        model.addAttribute("product", product);
        return "ProductDetails";
    }
}


其中,ProductController#saveProduct()方法的第二个入参

public String saveProduct(ProductForm productForm, Model model)


无论是否会使用,SpringMVC都会在每一个请求处理方法被调用时创建一个Model实例,用于增加需要显示在视图中的属性,例如通过调用model.addAttribute("product", product);来添加Product实例。这样Product实例就可以被添加到HttpServletRequestt中那样访问了。


View

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="product_save" 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>


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>



测试应用


http://localhost:8080/chapter04a/product_input

输入对应的表格


20180120211133731.jpg


提交后

http://localhost:8080/chapter04a/product_save

调用saveProduct方法


20180120211245415.jpg


相关文章
|
10月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
1537 128
|
10月前
|
Java 测试技术 数据库
使用Spring的@Retryable注解进行自动重试
在现代软件开发中,容错性和弹性至关重要。Spring框架提供的`@Retryable`注解为处理瞬时故障提供了一种声明式、可配置的重试机制,使开发者能够以简洁的方式增强应用的自我恢复能力。本文深入解析了`@Retryable`的使用方法及其参数配置,并结合`@Recover`实现失败回退策略,帮助构建更健壮、可靠的应用程序。
1111 1
使用Spring的@Retryable注解进行自动重试
|
9月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
787 2
|
10月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
662 12
|
10月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
558 0
探索Spring Boot的@Conditional注解的上下文配置
|
10月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
391 0
Spring中最大化@Lazy注解,实现资源高效利用
|
10月前
|
Java 测试技术 编译器
@GrpcService使用注解在 Spring Boot 中开始使用 gRPC
本文介绍了如何在Spring Boot应用中集成gRPC框架,使用`@GrpcService`注解实现高效、可扩展的服务间通信。内容涵盖gRPC与Protocol Buffers的原理、环境配置、服务定义与实现、测试方法等,帮助开发者快速构建高性能的微服务系统。
1852 0
|
设计模式 前端开发 JavaScript
Spring MVC(一)【什么是Spring MVC】
Spring MVC(一)【什么是Spring MVC】
|
前端开发 Java 关系型数据库
基于ssm框架旅游网旅游社交平台前后台管理系统(spring+springmvc+mybatis+maven+tomcat+html)
基于ssm框架旅游网旅游社交平台前后台管理系统(spring+springmvc+mybatis+maven+tomcat+html)
337 0
|
前端开发 Java Go
Spring MVC 和 Spring Boot 的区别
Spring MVC 和 Spring Boot 的区别
688 0

热门文章

最新文章