1
0
mirror of https://github.com/apachecn/spring-boot-doc-zh.git synced 2025-06-06 17:50:57 +00:00
wizardforcel cdd1ca7535 mkdocs
2018-11-26 15:20:07 +08:00

31 KiB
Raw Permalink Blame History

27. 开发Web应用程序

Spring Boot非常适合Web应用程序开发。 您可以使用嵌入式TomcatJetty或Undertow轻松创建自包含的HTTP服务器。 大多数Web应用程序将使用spring-boot-starter-web模块快速启动和运行。

如果您尚未开发Spring Boot Web应用程序则可以按照“Hello World”示例进行操作。 在“入门”部分中的示例。

27.1 “Spring Web MVC框架”

Spring Web MVC框架通常简称为“Spring MVC”是一个丰富的“模型视图控制器”Web框架。 Spring MVC允许您创建特殊的@Controller或@RestController bean来处理传入的HTTP请求。 您的控制器中的方法将使用@RequestMapping注释映射到HTTP。

以下是@RestController用于提供JSON数据的典型示例

@RestController
@RequestMapping(value="/users")
public class MyRestController {

    @RequestMapping(value="/{user}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
    List<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}", method=RequestMethod.DELETE)
    public User deleteUser(@PathVariable Long user) {
        // ...
    }

}

Spring MVC是Spring Framework的一部分详细信息可在参考文档中找到。 Spring.io/guide中还有几个指南可供Spring MVC使用。

27.1.1 Spring MVC自动配置

Spring Boot提供了适用于大多数应用程序的Spring MVC的自动配置。

自动配置在Spring的默认值之上添加以下功能

  • 包含ContentNegotiatingViewResolver和BeanNameViewResolver bean。
  • 支持提供静态资源包括对WebJars的支持见下文
  • ConverterGenericConverterFormatter beans的自动注册。
  • 支持HttpMessageConverters见下文
  • 自动注册MessageCodesResolver见下文
  • 静态index.html支持。
  • 自定义Favicon支持见下文
  • 自动使用ConfigurableWebBindingInitializer bean见下文

如果要保留Spring Boot MVC功能并且您只需要添加其他MVC配置interceptors, formatters, view, controllers等你可以添加自己的WebConfigurerAdapter类型的@Configuration类但不能使用@EnableWebMvc。 如果要提供自定义的RequestMappingHandlerMappingRequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver实例您可以声明一个提供此类组件的WebMvcRegistrationsAdapter实例。

如果要完全控制Spring MVC可以使用@EnableWebMvc添加您自己的@Configuration注释。

27.1.2 HttpMessageConverters

Spring MVC使用HttpMessageConverter接口转换HTTP请求和响应。 包括一些开箱即用的合理配置例如对象可以自动转换为JSON使用Jackson库或XML使用Jackson XML扩展如果可用否则使用JAXB。 字符串默认使用UTF-8进行编码。

如果需要添加或自定义转换器可以使用Spring Boot HttpMessageConverter类

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}

上下文中存在的任何HttpMessageConverter bean将被添加到转换器列表中。 您也可以以这种方式覆盖默认转换器。

27.1.3 自定义JSON序列化器和反序列化器

如果您使用Jackson序列化和反序列化JSON数据则可能需要编写自己的JsonSerializer和JsonDeserializer类。 自定义序列化程序通常通过一个模块注册到Jackson但是Spring Boot提供了一个备用的@JsonComponent注释可以更容易地直接注册Spring Bean。

您可以直接在JsonSerializer或JsonDeserializer实现中使用@JsonComponent。 您也可以将它用于包含序列化器/解串器的类作为内部类。 例如:

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

    public static class Serializer extends JsonSerializer<SomeObject> {
        // ...
    }

    public static class Deserializer extends JsonDeserializer<SomeObject> {
        // ...
    }

}

ApplicationContext中的所有@JsonComponent bean将自动注册到Jackson并且由于@JsonComponent是使用@Component进行元注解的所以常规的组件扫描规则适用。

Spring Boot还提供了JsonObjectSerializerJsonObjectDeserializer基类它们在序列化对象时为标准的Jackson版本提供了有用的替代方法。 有关详细信息请参阅Javadoc。

27.1.4 MessageCodesResolver

Spring MVC有一个生成错误代码的策略用于从绑定错误中提取错误消息MessageCodesResolver。 Spring Boot将为您创建一个错误代码如果您设置spring.mvc.message-codes-resolver.format属性PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE请参阅DefaultMessageCodesResolver.Format中的枚举

27.1.5 静态内容

默认情况下Spring Boot将从类路径或ServletContext的根目录中的名为/static或/ public或/resources或/META-INF/resources的目录提供静态内容。 它使用Spring MVC中的ResourceHttpRequestHandler因此您可以通过添加自己的WebMvcConfigurerAdapter并覆盖addResourceHandlers方法来修改该行为。

在独立的Web应用程序中来自容器的默认servlet也被启用并且作为后备如果Spring决定不处理它则从ServletContext的根目录提供内容。 大多数情况下这不会发生除非您修改默认的MVC配置因为Spring将始终能够通过DispatcherServlet处理请求。

默认情况下,资源映射到/** 但可以通过spring.mvc.static-path-pattern调整。 例如,将所有资源重定位到 /resources/**可以配置如下:

spring.mvc.static-path-pattern=/resources/**

您还可以使用spring.resources.static-locations使用目录位置列表替换默认值来自定义静态资源位置。 如果这样做默认欢迎页面检测将切换到您的自定义位置因此如果在启动时任何位置都有一个index.html它将是应用程序的主页。

除了上述“标准”静态资源位置之外,还提供了一个特殊情况,用于Webjars内容。 任何具有/ webjars / **中路径的资源都将从jar文件中提供如果它们以Webjars格式打包。

如果您的应用程序将被打包为jar请不要使用 src/main/webapp 目录。 虽然这个目录是一个通用的标准但它只适用于war包如果生成一个jar它将被大多数构建工具忽略。

Spring Boot还支持Spring MVC提供的高级资源处理功能允许使用例如缓存静态资源或使用Webjars的版本无关的URL。

要为Webjars使用版本无关的URL只需添加webjars-locator依赖关系即可。然后声明您的Webjar以jQuery为例如“/webjars/jquery/dist/jquery.min.js”这将产生“/webjars/jquery/xyz/dist/jquery.min.js”其中xyz是Webjar版本 。

如果您使用JBoss则需要声明webjars-locator-jboss-vfs依赖关系而不是webjars-locator; 否则所有Webjars都将解析为404。

要使用缓存清除功能以下配置将为所有静态资源配置缓存清除解决方案从而有效地在URL中添加内容哈希值例如<link href=“/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

链接资源在运行时在模板中被重写这归功于自动配置为Thymeleaf和FreeMarker的ResourceUrlEncodingFilter。 使用JSP时应手动声明此过滤器。 其他模板引擎现在不会自动支持,但可以使用自定义模板宏/帮助程序和使用ResourceUrlProvider

当使用例如JavaScript模块加载器动态加载资源时重命名文件不是一个选项。这就是为什么其他策略也得到支持并可以合并的原因。 “固定(fixed)”策略将在URL中添加静态版本字符串而不更改文件名

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

使用此配置,位于“/js/lib/”下的JavaScript模块将使用固定版本策略“/v12/js/lib/mymodule.js”而其他资源仍将使用内容<link href =“/css/“spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>。

有关更多支持的选项,请参阅ResourceProperties

此功能已在专门的博客文章和Spring Framework参考文档中进行了详细描述。

27.1.6 自定义图标

Spring Boot在配置的静态内容位置和类路径的根目录按顺序中查找favicon.ico。 如果文件存在,它将被自动用作应用程序的图标。

27.1.7 ConfigurableWebBindingInitializer

Spring MVC使用WebBindingInitializer为特定请求初始化WebDataBinder。 如果您用@Bean创建自己的ConfigurableWebBindingInitializer @BeanSpring Boot将自动配置Spring MVC以使用它。

27.1.8 模板引擎

除了REST Web服务您还可以使用Spring MVC来提供动态HTML内容。 Spring MVC支持各种模板技术包括ThymeleafFreeMarker和JSP。 许多其他模板引擎也运行自己的Spring MVC集成。

Spring Boot包括对以下模板引擎的自动配置支持

如果可能应避免使用JSP当使用嵌入式servlet容器时JSP有几个已知的限制

当您使用默认配置的模板引擎之一时,您的模板将从 src/main/resources/templates 自动获取。

IntelliJ IDEA根据运行应用程序的方式对类路径进行不同的排序。 通过main方法在IDE中运行应用程序将导致使用Maven或Gradle打包的jar运行应用程序时的不同顺序。这可能会导致Spring Boot找不到类路径上的模板。 如果您受此问题的影响您可以重新排序IDE中的类路径以放置模块的类和资源。 或者,您可以配置模板前缀以搜索类路径上的每个模板目录:classpath*:/templates/

27.1.9 错误处理

默认情况下Spring Boot提供 /error 映射以合理的方式处理所有错误并在servlet容器中注册为“global”错误页面。 对于机器客户端它将产生JSON响应其中包含错误HTTP状态和异常消息的详细信息。 对于浏览器客户端,有一个'whitelabel'错误视图以HTML格式呈现相同的数据定制它只需添加一个解析“error”的视图。 要完全替换默认行为您可以实现ErrorController并注册该类型的bean定义或者简单地添加一个类型为ErrorAttributes的bean来使用现有机制但只是替换内容。

BasicErrorController可以用作自定义ErrorController的基类。 如果要添加新内容类型的处理程序默认情况下是专门处理text/html并为其他内容提供备选这一点尤其有用。 要做到这一点只需扩展BasicErrorController并添加一个带有@RequestMapping的公共方法并创建一个新类型的bean。

您还可以定义一个@ControllerAdvice来自定义为特定控制器 and/or 异常类型返回的JSON文档。

@ControllerAdvice(basePackageClasses = FooController.class)
public class FooControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

在上面的示例中如果由FooController在同一个包中定义的控件抛出了YourException则将使用CustomerErrorType POJO的json表示法而不是ErrorAttributes表示形式。

自定义错误页面

如果要显示给定状态代码的自定义HTML错误页面请将文件添加到/error文件夹。 错误页面可以是静态HTML即添加在任何静态资源文件夹下或使用模板构建。 该文件的名称应该是确切的状态代码或一个序列掩码。

例如要将404映射到静态HTML文件您的文件夹结构将如下所示

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用FreeMarker模板映射所有5xx错误使用如下结构

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftl
             +- <other templates>

对于更复杂的映射您还可以添加实现ErrorViewResolver接口的bean。

public class MyErrorViewResolver implements ErrorViewResolver {

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        // Use the request or status to optionally return a ModelAndView
        return ...
    }

}

您还可以使用常规的Spring MVC功能@ExceptionHandler方法@ControllerAdvice。 然后ErrorController将接收任何未处理的异常。

映射Spring MVC之外的错误页面

对于不使用Spring MVC的应用程序可以使用ErrorPageRegistrar接口来直接注册ErrorPages。这个抽象直接与底层的嵌入式servlet容器一起工作即使没有Spring MVC DispatcherServlet也可以工作。

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
    return new MyErrorPageRegistrar();
}

// ...

private static class MyErrorPageRegistrar implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }

}

N.B. 如果您注册一个最终由Filter过滤的路径的ErrorPage例如像一些非Spring Web框架例如Jersey和Wicket一样则必须将Filter显式注册为ERROR dispatcher例如。

@Bean
public FilterRegistrationBean myFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    ...
    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
    return registration;
}

默认的FilterRegistrationBean不包括ERROR dispatcher 类型)。

WebSphere Application Server上的错误处理

当部署到servlet容器时Spring Boot会使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。 如果响应尚未提交,则该请求只能转发到正确的错误页面。 默认情况下WebSphere Application Server 8.0及更高版本在成功完成servlet的服务方法后提交响应。 您应该通过将com.ibm.ws.webcontainer.invokeFlushAfterService设置为false来禁用此行为

27.1.10 Spring HATEOAS

如果您正在开发一种利用超媒体的RESTful APISpring Boot可以为Spring HATEOAS提供自动配置适用于大多数应用程序。 自动配置取代了使用@EnableHypermediaSupport的需求并注册了一些Bean以便轻松构建基于超媒体的应用程序包括LinkDiscoverers用于客户端支持和配置为将响应正确地组织到所需表示中的ObjectMapper。 ObjectMapper将根据spring.jackson。*属性或Jackson2ObjectMapperBuilder bean如果存在进行自定义。

您可以使用@EnableHypermediaSupport控制Spring HATEOAS配置。 请注意这将禁用上述ObjectMapper定制。

27.1.11 CORS 支持

跨原始资源共享CORS大多数浏览器实现的W3C规范允许您以灵活的方式指定什么样的跨域请求被授权而不是使用一些不太安全和不太强大的方法如IFRAME或JSONP。

从版本4.2起Spring MVC支持CORS开箱即用。 在Spring Boot应用程序中的controller方法使用@CrossOrigin注解的CORS配置不需要任何特定的配置。 可以通过使用自定义的addCorsMappings(CorsRegistry)方法注册WebMvcConfigurer bean来定义全局CORS配置

@Configuration
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}

27.2 JAX-RS 和 Jersey

如果您喜欢JAX-RS编程模型的REST endpoints 您可以使用一个可用的实现而不是Spring MVC。 如果您刚刚在应用程序上下文中注册了一个@Bean的Servlet或Filter那么Jersey 1.x和Apache CXF的功能非常出色。 Jersey2.x有一些本地Spring支持所以我们也提供自动配置支持它在Spring Boot与启动器。

要开始使用Jersey 2.x只需将spring-boot-starter-jersey作为依赖项然后您需要一个@Bean类型ResourceConfig您可以在其中注册所有端点(endpoints)

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(Endpoint.class);
    }

}

Jersey对扫描可执行档案的包是相当有限的。 例如当运行可执行的war文件时它无法扫描在WEB-INF/classes中找到的包中的端点(endpoints)。 为了避免这种限制,不应使packages方法,并且应使用上述寄存器方法单独注册(register)端点。

您还可以注册任意数量的ResourceConfigCustomizer的实现bean以实现更高级的自定义。

所有注册的端点都应为具有HTTP资源注解@GET等的@Components例如。

@Component
@Path("/hello")
public class Endpoint {

    @GET
    public String message() {
        return "Hello";
    }

}

由于Endpoint是一个Spring @Component所以Spring的生命周期由Spring管理您可以使用@Autowired依赖关系并使用@Value注入外部配置。 默认情况下Jersey servlet将被注册并映射到/ *。 您可以通过将@ApplicationPath添加到ResourceConfig来更改映射。

默认情况下Jersey将通过@Bean以名为jerseyServletRegistration的ServletRegistrationBean类型在Servlet进行设置。 默认情况下servlet将被初始化但是您可以使用spring.jersey.servlet.load-on-startup进行自定义。您可以通过创建一个自己的同名文件来禁用或覆盖该bean。 您也可以通过设置spring.jersey.type = filter在这种情况下@Bean来替换或替换为jerseyFilterRegistration使用Filter而不是Servlet。 servlet有一个@Order您可以使用spring.jersey.filter.order设置。 可以使用spring.jersey.init.* 给出Servlet和过滤器注册的init参数以指定属性的映射。

有一个Jersey示例,所以你可以看到如何设置。 还有一个Jersey1.x示例。 请注意在Jersey1.x示例中spring-boot maven插件已经被配置为打开一些Jersey jar以便它们可以被JAX-RS实现扫描因为示例要求它们在Filter注册中进行扫描 。 如果您的任何JAX-RS资源作为嵌套的jar打包您可能需要执行相同操作。

27.3 嵌入式servlet容器支持

Spring Boot包括对嵌入式TomcatJetty和Undertow服务器的支持。 大多数开发人员将简单地使用适当的“Starter”来获取完全配置的实例。 默认情况下嵌入式服务器将监听端口8080上的HTTP请求。

如果您选择在CentOS上使用Tomcat请注意默认情况下临时目录用于存储已编译的JSP文件上传等。当您的应用程序正在运行导致故障时该目录可能会被tmpwatch删除。 为了避免这种情况您可能需要自定义tmpwatch配置以便tomcat.*目录不被删除或配置server.tomcat.basedir以便嵌入式Tomcat使用不同的位置

27.3.1 Servlets, Filters 和 listeners

当使用嵌入式servlet容器时可以使用Spring bean或通过扫描Servlet组件例如HttpSessionListener注册Servlet规范中的Servlet过滤器和所有监听器。

将Servlets过滤器和监听器注册为Spring bean

任何ServletFilter或Servlet Listener 实例都会作为Spring bean注册到嵌入式容器中。 可以非常方便地在配置过程中引用您的application.properties中的值。

默认情况下如果容器中只包含一个Servlet它将映射到/。 在多个Servlet bean的情况下bean名称将作为路径前缀。 过滤器(Filters)将映射到/*,默认过滤所有请求。

如果基于惯例的映射不够灵活可以使用ServletRegistrationBeanFilterRegistrationBean和ServletListenerRegistrationBean类来完成控制。

27.3.2 Servlet Context 初始化

嵌入式servlet容器不会直接执行Servlet 3.0+ javax.servlet.ServletContainerInitializer接口或Spring的org.springframework.web.WebApplicationInitializer接口。 这样设计的目的旨在降低在war中运行的第三方库破坏Spring Boot应用程序的风险。

如果您需要在Spring Boot应用程序中执行servlet context 初始化则应注册一个实现org.springframework.boot.context.embedded.ServletContextInitializer接口的bean。 单个onStartup方法提供对ServletContext的访问并且如果需要可以轻松地用作现有WebApplicationInitializer的适配器。

扫描Servlet过滤器和监听器

使用嵌入式容器时,可以使用@ServletComponentScan启用@WebServlet@WebFilter和@WebListener注解类的自动注册。

@ServletComponentScan在独立容器中不起作用在该容器中将使用容器的内置发现机制。

27.3.3 EmbeddedWebApplicationContext

在Spring Boot引导下将会使用一种新类型的ApplicationContext来支持嵌入式的servlet容器。 EmbeddedWebApplicationContext是一种特殊类型的WebApplicationContext它通过搜索单个EmbeddedServletContainerFactory bean来引导自身。 通常TomcatEmbeddedServletContainerFactoryJettyEmbeddedServletContainerFactory或UndertowEmbeddedServletContainerFactory将被自动配置。

您通常不需要知道这些实现类。 大多数应用程序将被自动配置并将代表您创建适当的ApplicationContext和EmbeddedServletContainerFactory。

27.3.4 定制嵌入式servlet容器

可以使用Spring Environment属性配置常见的servlet容器设置。 通常您可以在application.properties文件中定义属性。

常用服务器设置包括:

  • 网络设置侦听端口的HTTP请求server.port接口地址绑定到server.address等。
  • 会话设置会话是否持久化server.session.persistence会话超时server.session.timeout会话数据的位置server.session.store-dir和session-cookie配置server.session.cookie.*)。
  • 错误管理错误页面的位置server.error.path
  • SSL
  • HTTP压缩

Spring Boot尽可能地尝试公开常见设置但并不总是可能的。 对于这些情况专用命名空间提供服务器特定的定制请参阅server.tomcat和server.undertow。 例如可以使用嵌入式servlet容器的特定功能来配置访问日志

有关完整列表,请参阅 ServerProperties 类。

用程序定制

如果需要以编程方式配置嵌入式servlet容器您可以注册一个实现EmbeddedServletContainerCustomizer接口的Spring bean。 EmbeddedServletContainerCustomizer提供对ConfigurableEmbeddedServletContainer的访问其中包含许多自定义设置方法。

import org.springframework.boot.context.embedded.*;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setPort(9000);
    }

}

直接自定义ConfigurableEmbeddedServletContainer

如果上述定制技术有太多限制您可以自己注册TomcatEmbeddedServletContainerFactoryJettyEmbeddedServletContainerFactory或UndertowEmbeddedServletContainerFactory bean。

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
    factory.setPort(9000);
    factory.setSessionTimeout(10, TimeUnit.MINUTES);
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
    return factory;
}

setter方法提供了许多配置选项。 如果您需要做更多的自定义,还会提供几种保护方法“钩子”。 有关详细信息,请参阅源代码文档。

27.3.5 JSP限制

当运行使用嵌入式servlet容器并打包为可执行文档的Spring Boot应用程序时对JSP支持有一些限制。

  • 可以使用Tomcat和war包即可执行的war将会起作用并且也可以部署到标准容器不限于但包括Tomcat中。 由于Tomcat中的硬编码文件模式可执行的jar将无法正常工作。
  • 可以使用Jetty和war包即可执行的war将会起作用,并且也可以部署到任何标准的容器,它应该可以工作。
  • Undertow不支持JSP。
  • 创建自定义的error.jsp页面将不会覆盖默认视图以进行错误处理,而应使用自定义错误页面