Quick Guide to Spring Controllers – Spring控制器快速指南

最后修改: 2016年 8月 2日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this article we’ll focus on a core concept in Spring MVC – Controllers.

在这篇文章中,我们将重点讨论Spring MVC的一个核心概念–控制器。

2. Overview

2.概述

Let’s start by taking a step back and having a look at the concept of the Front Controller in the typical Spring Model View Controller architecture.

让我们先退一步,看看典型的Spring Model View Controller架构中的Front Controller的概念

At a very high level, here are the main responsibilities we’re looking at:

在一个非常高的水平上,以下是我们正在关注的主要职责。

  • Intercepts incoming requests
  • Converts the payload of the request to the internal structure of the data
  • Sends the data to Model for further processing
  • Gets processed data from the Model and advances that data to the View for rendering

Here’s a quick diagram for the high level flow in Spring MVC:

下面是一张关于Spring MVC中高层流程的快速图表。

SpringMVC

As you can see, the DispatcherServlet plays the role of the Front Controller in the architecture.

正如你所看到的,DispatcherServlet在架构中扮演Front Controller的角色。

The diagram is applicable both to typical MVC controllers as well as RESTful controllers – with some small differences (described below).

该图既适用于典型的MVC控制器,也适用于RESTful控制器–有一些小的区别(如下所述)。

In the traditional approach, MVC applications are not service-oriented hence there is a View Resolver that renders final views based on data received from a Controller.

在传统方法中,MVC应用程序不是面向服务的,因此有一个View Resolver,它根据从Controller收到的数据渲染最终视图。

RESTful applications are designed to be service-oriented and return raw data (JSON/XML typically). Since these applications do not do any view rendering, there are no View Resolvers – the Controller is generally expected to send data directly via the HTTP response.

RESTful应用程序被设计为面向服务并返回原始数据(通常为JSON/XML)。由于这些应用程序不做任何视图渲染,因此没有视图解析器控制器通常要通过HTTP响应直接发送数据。

Let’s start with the MVC0-style controllers.

让我们从MVC0风格的控制器开始。

3. Maven Dependencies

3.Maven的依赖性

In order to be able to work with Spring MVC, let’s deal with the Maven dependencies first:

为了能够使用Spring MVC,让我们先处理一下Maven的依赖性。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.6.RELEASE</version>
<dependency>

To get the latest version of the library, have a look at spring-webmvc on Maven Central.

要获得该库的最新版本,请查看Maven中心的spring-webmvc

4. Project Web Config

4.项目网络配置

Now, before looking at the controllers themselves, we first need to set up a simple web project and do a quick Servlet configuration.

现在,在看控制器本身之前,我们首先需要建立一个简单的Web项目,并做一个快速的Servlet配置。

Lets first see how the DispatcherServlet can be set up without using web.xml – but instead using an initializer:

让我们先看看如何在不使用web.xml的情况下设置DispatcherServlet–而是使用一个初始化器。

public class StudentControllerConfig implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        AnnotationConfigWebApplicationContext root = 
          new AnnotationConfigWebApplicationContext();
        root.register(WebConfig.class);

        root.refresh();
        root.setServletContext(sc);

        sc.addListener(new ContextLoaderListener(root));

        DispatcherServlet dv = 
          new DispatcherServlet(new GenericWebApplicationContext());

        ServletRegistration.Dynamic appServlet = sc.addServlet("test-mvc", dv);
        appServlet.setLoadOnStartup(1);
        appServlet.addMapping("/test/*");
    }
}

To set things up with no XML, make sure to have servlet-api 3.1.0 on your classpath.

要在没有XML的情况下进行设置,确保你的classpath上有servlet-api 3.1.0。

Here’s how the web.xml would look like:

下面是web.xml的样子。

<servlet>
    <servlet-name>test-mvc</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/test-mvc.xml</param-value>
    </init-param>
</servlet>

We’re setting the contextConfigLocation property here – pointing to the XML file used to load the Spring context. If the property is not there, Spring will search for a file named {servlet_name}-servlet.xml.

我们在这里设置contextConfigLocation属性–指向用于加载Spring上下文的XML文件。如果该属性不在这里,Spring将搜索一个名为{servlet_name}-servlet.xml的文件。

In our case the servlet_name is test-mvc and so, in this example the DispatcherServlet would search for a file called test-mvc-servlet.xml.

在我们的例子中,servlet_nametest-mvc,因此,在这个例子中,DispatcherServlet将搜索一个叫做test-mvc-servlet.xml的文件。

Finally, let’s set the DispatcherServlet up and map it to a particular URL – to finish our Front Controller based system here:

最后,让我们设置DispatcherServlet,并将其映射到一个特定的URL–以完成我们基于Front Controller的系统。

<servlet-mapping>
    <servlet-name>test-mvc</servlet-name>
    <url-pattern>/test/*</url-pattern>
</servlet-mapping>

Thus in this case the DispatcherServlet would intercept all requests within the pattern /test/* .

因此,在这种情况下,DispatcherServlet将拦截所有模式/test/*内的请求。

5. Spring MVC Web Config

5.Spring MVC Web配置

Lets now look at how the Dispatcher Servlet can be setup using Spring Config:

现在让我们来看看如何使用Spring Config来设置Dispatcher Servlet

@Configuration
@EnableWebMvc
@ComponentScan(basePackages= {
  "com.baeldung.controller.controller",
  "com.baeldung.controller.config" }) 
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
 
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver bean = 
          new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/");
        bean.setSuffix(".jsp");
        return bean;
    }
}

Let’s now look at setting up the Dispatcher Servlet using XML . A snapshot of the DispatcherServlet XML file – the XML file which the DispatcherServlet uses for loading custom controllers and other Spring entities is shown below:

现在让我们看看如何使用XML设置Dispatcher Servlet。下面是DispatcherServlet XML文件的快照–DispatcherServlet用于加载自定义controller和其他Spring实体XML文件。

<context:component-scan base-package="com.baledung.controller" />
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

Based on this simple configuration, the framework will of course initialize any controller bean that it will find on the classpath.

基于这个简单的配置,框架当然会初始化它在classpath上找到的任何控制器bean。

Notice that we’re also defining the View Resolver, responsible for view rendering – we’ll be using Spring’s InternalResourceViewResolver here. This expects a name of a view to be resolved, which means finding a corresponding page by using prefix and suffix (both defined in the XML configuration).

请注意,我们还定义了负责视图渲染的视图解析器 – 我们将在此使用Spring的InternalResourceViewResolver。这期望一个视图的名字被解析,这意味着通过使用前缀和后缀(都在 XML配置中定义)找到一个相应的页面。

So for example if the Controller returns a view named “welcome”, the view resolver will try to resolve a page called “welcome.jsp” in the WEB-INF folder.

因此,例如,如果Controller返回一个名为”welcome “的viewviewresolver将尝试解析WEB-INF文件夹中一个名为“welcome.jsp “的页面。

6. The MVC Controller

6.MVC控制器

Let’s now finally implement the MVC style controller.

现在让我们最终实现MVC风格的控制器。

Notice how we’re returning a ModelAndView object – which contains a model map and a view object; both will be used by the View Resolver for data rendering:

注意我们是如何返回一个ModelAndView对象的–它包含一个model map和一个view object;两者都将被View Resolver用于数据渲染。

@Controller
@RequestMapping(value = "/test")
public class TestController {

    @GetMapping
    public ModelAndView getTestData() {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("welcome");
        mv.getModel().put("data", "Welcome home man");

        return mv;
    }
}

So, what exactly did we set up here.

那么,我们在这里到底设置了什么。

First, we created a controller called TestController and mapped it to the “/test” path. In the class we have created a method which returns a ModelAndView object and is mapped to a GET request thus any URL call ending with “test” would be routed by the DispatcherServlet to the getTestData method in the TestController.

首先,我们创建了一个名为TestController的控制器,并将其映射到“/test”路径。在这个类中,我们创建了一个方法,它返回一个ModelAndView对象,并被映射到一个GET请求,因此任何以”test“结尾的URL调用将被DispatcherServlet路由到TestControllergetTestData方法。

And of course we’re returning the ModelAndView object with some model data for good measure.

当然,我们将返回带有一些模型数据的ModelAndView对象,以利于衡量。

The view object has a name set to “welcome“. As discussed above, the View Resolver will search for a page in the WEB-INF folder called “welcome.jsp“.

视图对象的名称设置为”welcome“。如上所述,视图解析器将在WEB-INF文件夹中搜索一个名为”welcome.jsp“的页面。

Below you can see the result of an example GET operation:

下面你可以看到一个例子GET操作的结果。

result_final

Note that the URL ends with “test”. The pattern of the URL is “/test/test“.

请注意,URL“test”结束。URL的模式是“/test/test“。

The first “/test” comes from the Servlet, and the second one comes from the mapping of the controller.

第一个“/test”来自Servlet,第二个来自控制器的映射。

7. More Spring Dependencies for REST

7.REST的更多Spring依赖性

Let’s now start looking at a RESTful controller. Of course, a good place to start is the extra Maven dependencies we need for it:

现在让我们开始研究RESTful控制器。当然,一个好的开始是我们需要的额外的Maven依赖项。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>
</dependencies>

Please refer to jackson-core, spring-webmvc and spring-web links for the newest versions of those dependencies.

请参考jackson-corespring-webmvcspring-web链接了解这些依赖的最新版本。

Jackson is of course not mandatory here, but it’s certainly a good way to enable JSON support. If you’re interested to dive deeper into that support, have a look at the message converters article here.

Jackson在这里当然不是强制性的,但它肯定是启用JSON支持的一个好方法。如果您有兴趣深入了解这种支持,请看消息转换器文章

8. The REST Controller

8.REST控制器

The setup for a Spring RESTful application is the same as the one for the MVC application with the only difference being that there is no View Resolvers and no model map.

Spring RESTful应用程序的设置与MVC应用程序的设置相同,唯一的区别是没有View Resolversmodel map。

The API will generally simply return raw data back to the client – XML and JSON representations usually – and so the DispatcherServlet bypasses the view resolvers and returns the data right in the HTTP response body.

API通常会简单地将原始数据返回给客户端–XMLJSON表示,因此DispatcherServlet绕过视图解析器在HTTP响应体中直接返回数据

Let’s have a look at a simple RESTful controller implementation:

让我们来看看一个简单的RESTful控制器实现。

@Controller
public class RestController {

    @GetMapping(value = "/student/{studentId}")
    public @ResponseBody Student getTestData(@PathVariable Integer studentId) {
        Student student = new Student();
        student.setName("Peter");
        student.setId(studentId);

        return student;
    }
}

Note the @ResponseBody annotation on the method – which instructs Spring to bypass the view resolver and essentially write out the output directly to the body of the HTTP response.

请注意方法上的@ResponseBody注解–它指示Spring绕过视图解析器基本上直接将输出写入HTTP响应的主体中

A quick snapshot of the output is displayed below:

下面显示的是输出的一个快速快照。

16th_july

The above output is a result of sending the GET request to the API with the student id of 1.

上面的输出是向API发送GET请求的结果,学生id1

One quick note here is – the @RequestMapping annotation is one of those central annotations that you’ll really have to explore in order to use to its full potential.

这里有一个简短的说明–@RequestMapping 注释是那些你必须真正探索才能充分使用其潜力的核心注释之一。

9. Spring Boot and the @RestController Annotation

9.Spring Boot和@RestController注解

The @RestController annotation from Spring Boot is basically a quick shortcut that saves us from always having to define @ResponseBody.

Spring Boot的@RestController注解基本上是一个快速捷径,使我们不必总是定义@ResponseBody

Here’s the previous example controller using this new annotation:

下面是之前使用这个新注解的控制器例子。

@RestController
public class RestAnnotatedController {
    @GetMapping(value = "/annotated/student/{studentId}")
    public Student getData(@PathVariable Integer studentId) {
        Student student = new Student();
        student.setName("Peter");
        student.setId(studentId);

        return student;
    }
}

10. Conclusion

10.结论

In this guide, we explore the basics of using controllers in Spring, both from the point of view of a typical MVC application as well as a RESTful API.

在本指南中,我们将从典型的MVC应用以及RESTful API的角度,探讨Spring中使用控制器的基本知识。

Of course all the code in the article is available over on GitHub.

当然,文章中的所有代码都可以在GitHub上找到