27. 开发web应用
Spring Boot非常适合Web应用程序开发。 您可以使用嵌入式Tomcat,Jetty或Undertow轻松创建自包含的HTTP服务器。 大多数Web应用程序将使用spring-boot-starter-web模块快速启动和运行。
如果你还没有开发Spring Boot web应用程序,你可以按照“Hello World!” 示例在入门部分。
27.1 The ‘Spring Web MVC 框架’
Spring Web MVC框架(通常简称"Spring MVC")是一个带有浓厚"model view controller"模式的web框架。Spring MVC允许你创建特殊的@Controller或者@RestController bean来处理传入的HTTP请求.你的Controller方法使用@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是Core Spring Framework的一部分,详细信息可以在参考文档中找到。 在spring.io/guides还有几个指南,涵盖Spring MVC。
27.1.1 Spring MVC自动配置
Spring Boot为Spring MVC提供了自动配置,适用于大多数应用程序。 自动配置在Spring的默认值之上添加了以下功能:
- 包含ContentNegotiatingViewResolver和BeanNameViewResolver bean。
- 支持服务静态资源,包括对WebJars的支持(见下文)。
- 自动注册Converter,GenericConverter,Formatter beans。
- 支持HttpMessageConverters(见下文)。
- 自动注册MessageCodesResolver(见下文)。
- 静态index.html支持。
- 自定义Favicon支持。
- 自动使用ConfigurableWebBindingInitializer bean(见下文)。
如果你想保留Spring Boot MVC的功能,而切你只是想添加额外的【MVC配置】(http://docs.spring.io/spring/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle#mvc)(interceptors,formatters,view controllers等),你可以添加自己的WebMvcConfigurerAdapter类型的@Configuration类,但没有@EnableWebMvc.如果你希望提供RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,你可以声明一个提供此类组件的WebMvcRegistrationsAdapter实例。 如果你想完全控制Spring MVC,你可以添加你自己的@Configuration注解@EnableWebMvc。
27.1.2 HttpMessageConverters
Spring MVC使用HttpMessageConverter接口来转换HTTP请求和响应。 敏感的默认值包括开箱即用,例如对象可以自动转换为JSON(使用Jackson库)或XML(使用Jackson XML扩展(如果可用,否则使用JAXB))。 默认情况下,字符串使用UTF-8编码。
如果你需要添加或自定义转换器,你可以使用Spring Boot的HttpMessageConverters类:
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类。自定义序列化器通常是通过Module来注册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还提供了JsonObjectSerializer和JsonObjectDeserializer基类,它们在序列化对象时为标准Jackson版本提供了有用的替代方法。 有关详细信息,请参阅Javadoc。
27.1.4 MessageCodesResolver
Spring MVC有一个策略用于生成错误代码,用于从绑定错误中呈现错误消息:MessageCodesResolver。 如果设置了spring.mvc.message-codes-resolver.format属性PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE(参考DefaultMessageCodesResolver.Format中的枚举),Spring Boot将为您创建一个。
27.1.5 静态内容
默认情况下,Spring Boot将从classpath中的/ static(或/ public或/ resources或/ META-INF / resources)目录或从ServletContext的根目录提供静态内容。 它使用Spring MVC中的ResourceHttpRequestHandler,因此您可以通过添加您自己的WebMvcConfigurerAdapter并覆盖addResourceHandlers方法来修改该行为。
在独立的Web应用程序中,容器中的默认servlet会被启用,并且作为后备,如果Spring决定不处理它,则从ServletContext的根服务内容。 大多数时候这不会发生(除非你修改默认的MVC配置),因为Spring总是能够通过DispatcherServlet处理请求。
您可以使用spring.resources.staticLocations(用目录位置列表替换默认值)自定义静态资源位置。 如果这样做,默认的欢迎页面检测将切换到您的自定义位置,因此如果在启动时在任何位置有一个index.html,它将是应用程序的主页。
除了上面的“标准”静态资源位置之外,还有一个特殊情况是Webjars内容。 如果以Webjars格式打包,那么任何具有/ webjars / **中路径的资源都将从jar文件提供。
如果您的应用程序将打包为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”就是他的版本未知的URL其中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=/**
link资源由ResourceUrlEncodingFilter在运行时在模板中重写,自动配置为Thymeleaf和FreeMarker。 您应该在使用JSP时手动声明此过滤器。 其他模板引擎现在不是自动支持的,但可以使用自定义模macros/helpers和使用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 ConfigurableWebBindingInitializer
Spring MVC使用WebBindingInitializer来初始化特定请求的WebDataBinder。 如果你创建自己的ConfigurableWebBindingInitializer @Bean,Spring Boot会自动配置Spring MVC来使用它。
27.1.7 模板引擎
除了REST Web服务,您还可以使用Spring MVC来提供动态HTML内容。 Spring MVC支持各种模板技术,包括Thymeleaf,FreeMarker和JSPs。 许多其他模板引擎也发布自己的Spring MVC集成。 Spring Boot包括对以下模板引擎的自动配置支持:
如果可能,应该避免JSP,当使用嵌入式servlet容器时,存在几个已知的限制。
当您使用其中一个模板引擎与默认配置,您的模板将被自动从src/main/resources/template加载。
IntelliJ IDEA根据运行应用程序的方式对类路径进行不同的排序。 在IDE中通过其main方法运行应用程序将导致使用Maven或Gradle或从其打包的jar运行应用程序时的顺序不同。 这可能导致Spring Boot无法在类路径上找到模板。 如果受此问题的影响,您可以在IDE中重新排序类路径,以首先放置模块的类和资源。 或者,您可以配置template前缀以搜索类路径上的每个模板目录:classpath *:/ templates /。
27.1.8 处理ERROR
Spring Boot默认提供了一个/error映射,以一种明智的方式处理所有的错误,并且在servlet容器中被注册为一个"全局"的错误页面.对于机器客户端,它将生成一个JSON响应,其中包含error,http状态和异常信息的详细信息.对于浏览器客户端,有一个“whitelabel”错误视图,以HTML格式呈现相同的数据(要自定义它只需要添加一个views,解析为“error”)。 要完全替换默认行为,您可以实现ErrorController并注册该类型的bean定义,或者只是添加一个类型为ErrorAttributes的bean来使用现有机制,但替换内容。
BasicErrorController可以用作自定义ErrorController的基类。 这是特别有用的,如果你想添加一个新的内容类型的处理程序(默认是处理文本/ html专门和提供回退的一切)。 为此,只需扩展BasicErrorController并添加一个带有generate属性的@RequestMapping的公共方法,并创建一个新类型的bean。
您还可以定义@ControllerAdvice以自定义为特定controller异常类型返回的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);
}
}
在上面的示例中,如果YourException由与FooController相同的包中定义的controller抛出,将使用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"));
}
}
如果你注册一个错误页面,其路径将被过滤器处理(例如像一些非Spring web框架,像Jersey和Wicket一样常见),那么过滤器必须显式地注册为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.9 Spring HATEOAS
如果你正在开发一个使用超媒体的RESTful API,Spring Boot为Spring HATEOAS提供了自动配置,可以与大多数应用程序一起使用。 自动配置取代了使用@EnableHypermediaSupport的需要,并注册了一些bean来简化构建基于超媒体的应用程序,包括LinkDiscoverers(用于客户端支持)和ObjectMapper,配置为将响应正确地封送到所需的表示中。 ObjectMapper将基于spring.jackson。*属性或Jackson2ObjectMapperBuilder bean(如果存在)进行自定义。
你可以通过使用@EnableHypermediaSupport来控制Spring HATEOAS的配置。 注意,这将禁用上述的ObjectMapper定制。
27.1.10 CORS支持
跨源资源共享(CORS)是一种由大多数浏览器实现的W3C规范,允许您以灵活的方式指定授权哪种跨域请求,而不是使用一些安全性较低且功能较低的方法,如IFRAME或JSONP。
从4.2版本开始,Spring MVC支持CORS开箱即用。 在Spring Boot应用程序中使用控制器方法使@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端点,你可以使用一个可用的实现,而不是Spring MVC。 Jersey 1.x和Apache CXF工作得很好,如果你只是注册他们的Servlet或过滤器作为@Bean在你的应用程序上下文。 Jersey 2.x有一些原生的Spring支持,所以我们还为Spring Boot和启动器提供了自动配置支持。
要开始使用Jersey 2.x,只需要包含spring-boot-starter-jersey作为依赖项,然后你需要一个ResourceConfig类型的@Bean,其中注册所有的端点:
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Endpoint.class);
}
}
Jersey对扫描可执行存档的支持相当有限。 例如,当运行可执行war文件时,它无法扫描在WEB-INF / classes中找到的包中的端点。 为了避免这种限制,不应该使用包方法,并且应该使用如上所示的寄存器方法单独注册endpoint。
由于Endpoint是一个Spring @Component,它的生命周期由Spring管理,你可以使用@Autowired依赖项,并使用@Value注入外部配置。 Jersey servlet将被注册并且默认映射到/ *。您可以通过向ResourceConfig添加@ApplicationPath来更改映射。
默认情况下,Jersey将在名为jerseyServletRegistration的ServletRegistrationBean类型的@Bean中设置为Servlet。默认情况下,servlet将被初始化,但您可以通过spring.jersey.servlet.load-on-startup自定义它。您可以通过使用相同的名称创建自己的bean来禁用或覆盖该bean。你也可以使用Filter而不是Servlet,方法是设置spring.jersey.type = filter(在这种情况下,要替换或覆盖的@Bean是jerseyFilterRegistration)。 servlet有一个@Order,你可以使用spring.jersey.filter.order设置。可以使用spring.jersey.init。*来指定Servlet和Filter注册的init参数以指定属性的映射。
有一个Jersey例子,所以你可以看到如何设置的东西。 还有一个Jersey 1.x样本。 请注意,在Jersey 1.x示例中,spring-boot maven插件已配置为解压缩一些Jersey jar,以便可以通过JAX-RS实现扫描它们(因为样本要求在Filter注册中扫描它们) 。 如果任何JAX-RS资源打包为嵌套JAR,您可能需要执行相同操作。
27.3嵌入式servlet容器支持
Spring Boot包括对嵌入式Tomcat,Jetty和Undertow服务器的支持。 大多数开发人员只需使用适当的“Starter”来获取完全配置的实例。 默认情况下,嵌入式服务器将在端口8080上侦听HTTP请求。
27.3.1 Servlets, Filters, and listeners
当使用嵌入式servlet容器时,您可以通过使用Spring bean或扫描Servlet组件,从Servlet规范(例如HttpSessionListener)注册Servlet,Filters和所有listeners。
将Servlet,Filters和listeners注册为Spring bean
任何Servlet,Filter或者Servlet * Listener都会作为Spring bean的实例向内嵌容器注册.如果想要在配置期间引用来自application.properties的值,这样会非常方便.
在默认情况下,如果上下文中止包含一个Servlet,它将映射到 /。在多个Servlet bean的情况下,bean名称将用作路劲前缀.过滤将映射到/*。
如果基于约定的映射不够灵活,您可以使用ServletRegistrationBean,FilterRegistrationBean和ServletListenerRegistrationBean类进行完全控制。
27.3.2 Servlet context初始化
嵌入式servlet容器不会直接执行Servlet 3.0+ javax.servlet.ServletContainerInitializer接口,或者Spring的org.springframework.web.WebApplicationInitializer接口。 这是一个有意的设计决定,旨在降低在war中运行的第三方库将破坏Spring Boot应用程序的风险。
如果您需要在Spring Boot应用程序中执行servlet上下文初始化,则应注册一个实现org.springframework.boot.context.embedded.ServletContextInitializer接口的bean。 单个onStartup方法提供对ServletContext的访问,并且如果需要可以轻松地用作现有WebApplicationInitializer的适配器。
扫描Servlet,Filter和Lisenners
当使用嵌入式容器时,可以使用@ServletComponentScan启用@WebServlet,@WebFilter和@WebListener注解类的自动注册。
@ServletComponentScan在独立容器中没有效果,而是使用内置容器的发现机制。
EmbeddedWebApplicationContext
在引擎罩下Spring Boot使用一种新的ApplicationContext类型用于嵌入式servlet容器支持。 EmbeddedWebApplicationContext是一种特殊类型的WebApplicationContext,它通过搜索单个EmbeddedServletContainerFactory bean来引导自身。 通常,TomcatEmbeddedServletContainerFactory,JettyEmbeddedServletContainerFactory或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)和会话cookie配置(server.session.cookie 。*)。
- 错误管理:错误页面的位置(server.error.path)等。
- SSL
- HTTP compression
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
如果上面的定制技术太有限,你可以自己注册TomcatEmbeddedServletContainerFactory,JettyEmbeddedServletContainerFactory或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;
}
为许多配置选项提供了设置器。 如果你需要做一些更奇特的事情,也提供了几个受保护的方法“钩子”。 有关详细信息,请参阅源代码文档。
27.3.5 JSP限制
当运行使用嵌入式servlet容器(并打包为可执行文件)的Spring Boot应用程序时,JSP支持有一些限制。
使用Tomcat,如果你使用war包装,即可执行war可以工作,并且也可以部署到标准容器(不限于,但包括Tomcat)。 由于Tomcat中的硬编码文件模式,可执行jar将无法工作。 如果你使用war包装,即使可执行的war可以工作,它也可以部署到任何标准容器。 Undertow不支持JSP。 创建自定义error.jsp页面不会覆盖错误处理的默认视图,应改用自定义错误页面。 有一个JSP示例,所以你可以看到如何设置的东西。