文 by / 林本托

Tips

做一个终身学习的人。

Tips

代码路径:https://github.com/iqcz/Springbootdemo/tree/master/code01/ch3

Web 框架行为调整

在此章节中,主要包括如下内容:

  • 配置路由匹配模式;
  • 配置自定义静态路径映射;
  • 通过EmbeddedServletContainerCustomizer调优Tomcat;
  • 选择嵌入式servlet容器;
  • 添加自定义连接。

一. 配置路由匹配模式

当我们构建Web应用程序时,并不总是使用一些默认的配置。 有时,我们要创建包含字符“.”的 RESTful 风格的 URL。“.”字符在Spring作为分隔符定义格式,例如path.xml中的点,或者我们可能不想识别路径尾部的斜杠,如/home/等。 Spring为我们提供了对这些问题提供了一种轻松的实现。

在前面的第二节中,我们介绍了WebConfiguration类,它继承了WebMvcConfigurerAdapter类。 通过继承,可以重写面向过滤器,格式化,还有其他格式的方法。 同样,还可以重写配置路径匹配的方法。

假设ISBN格式允许使用点来将图书编号与修订版本分开,看起来像“[isbn-number].[revision]”的格式。

我们将配置我们的应用程序不使用“.*”的后缀模式匹配,并且在解析参数时不忽略点之后的值。 我们执行以下步骤:

首先,需要在WebConfiguration类中,加入如下内容:

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false).
setUseTrailingSlashMatch(true);
}

同时,需要引入import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;类。

然后,执行./gradlew clean bootRun命令。

在浏览器中,输入 http://localhost:8080/books/978-1-78528-415-1.1 , 然后会看到如下结果:

如果输入正确的 ISBN,会看到如下的结果:

我们来看看具体做了什么事情。 configurePathMatch(PathMatchConfigurer configurer)方法有能力设置我们自己的行为,希望Spring如何将请求URL路径与控制器参数相匹配:

如果要进一步配置路径匹配的方式,可以提供自己的PathMatcherUrlPathHelper实现,但这些在最极端和自定义的情况下都是必需的,一般情况下不推荐使用。

二. 配置自定义静态路径映射

在前面的内容中,我们讲解了如何调整请求的URL路径映射,并将其转换为控制器方法。 除此而外,还可以控制、Web应用程序处理静态文件,这些文件可能存在与文件系统中,或可部署的归档文件中。

假设我们想通过应用程序的 http://localhost:8080/internal/application.properties 的静态网址公开我们的内部application.properties 文件。 要开始执行此操作,请继续执行下面的步骤。

首先,在WebConfiguration类中,重写addResourceHandlers方法:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/internal/**")
.addResourceLocations("classpath:/");
}

执行./gradlew clean bootRun。服务启动以后,在浏览器中输入:http://localhost:8080/internal/application.properties,在我的环境里,系统是 macOS,浏览器是 Chrome,会下载application.properties文件,可能会根据每个人的操作系统和浏览器的不同,行为会不一样。

我们重写的addResourceHandlers(ResourceHandlerRegistry registry)WebMvcConfigurer类的另一个配置方法,它能够为静态资源URL定义自定义映射,并将它们与文件系统或应用程序类路径上的资源进行连接。 在上面例子中,定义了一个通过“/ internal” URL可以访问的文件的映射,以便在我们的应用程序的“classpath:/”中查找。 (对于生产环境,可能不想将整个类路径暴露为静态资源)所以让我们来看看我们做了什么,如下所示:

  • registry.addResourceHandler("/internal/**") 方法,向ResourceHandlerRegistry类添加一个资源处理程序来处理我们的静态资源,并返回ResourceHandlerRegistration,这可以用于以链式方式进一步配置映射。“/internal/**”是一个路径模式,用于使用PathMatcher与请求URL进行匹配。 我们已经看到在上一个示例中如何配置PathMatcher,但是默认情况下使用AntPathMatcher实现。 可以配置多个URL模式以匹配特定的资源位置。

  • addResourceLocations("classpath:/")方法在新创建ResourceHandlerRegistration类实例时被调用,它定义了应该从中加载资源的目录。 这些应该是有效的文件系统或类路径目录,并且可以有多个输入。 如果提供了多个位置,将按照输入的顺序进行检查。

我们还可以使用setCachePeriod(Integer cachePeriod)方法为给定资源配置缓存间隔。

三. 通过EmbeddedServletContainerCustomizer调优Tomcat

Spring Boot公开了许多服务器属性,可以通过简单地设置application.properties中的值来配置诸如PORT,SSL和其他内容的服务器属性。 但是,如果需要进行更复杂的调优,Spring Boot提供了一个EmbeddedServletContainerCustomizer接口,以编程方式定义配置。

即使会话超时可以通过将application.properties中的server.session-timeout属性设置为我们所需的值(几秒钟)来轻松配置,但是我们仍然使用EmbeddedServletContainerCustomizer来演示该功能。

我们希望session保持一分钟。 为了实现这一点,在WebConfiguration类中添加一个EmbeddedServletContainerCustomizer,其中包含以下内容:

@Bean
public
EmbeddedServletContainerCustomizer
embeddedServletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void
customize(ConfigurableEmbeddedServletContainer
container) {
container.setSessionTimeout(1, TimeUnit.MINUTES);
}
};
}

出于演示的目的,通过调用getSession()方法来请求会话的请求对象,这将强制其创建。 为此,我们将添加一个新的请求映射到BookController类,代码如下:

@RequestMapping(value = "/session", method = RequestMethod.GET)
public String getSessionId(HttpServletRequest request) {
return request.getSession().getId();
}

启动 ./gradlew clean bootRun

在浏览器中输入 http://localhost:8080/books/session,看到如下结果:

如果我们等待一分钟以上,然后重新加载此页面,则session id将更改为新的 session id。

EmbeddedServletContainerCustomizer接口定义了自定义customize(ConfigurableEmbeddedServletContainer container)方法。 这实际上对于使用Java 8的人来说是一个很好的方便,因为返回一个lambda表达式,而不是创建该类的实现。 在这种情况下,它将如下所示:

public EmbeddedServletContainerCustomizer
embeddedServletContainerCustomizer() {
return (ConfigurableEmbeddedServletContainer container) -> {
container.setSessionTimeout(1, TimeUnit.MINUTES);
};
}

在应用程序启动期间,Spring Boot自动配置检测定制器的存在,并调用customize(...)方法,将引用传递给servlet容器。 在具体的情况下,实际上得到了一个TomcatEmbeddedServletContainerFactory实现的实例; 但是根据使用的servlet容器的种类不同,如Jetty或Undertow,实现方式将有所不同。

四. 选择嵌入式servlet容器

尽管Tomcat是Spring Boot中的默认嵌入式容器,但并不限于此。 Spring Boot提供Jetty和Undertow的等容器的支持,因此我们可以选择不同的容器。

如果决定使用Jetty作为servlet容器,需要在的构建文件中添加Jetty相关的模块。

首先,由于Tomcat已经作为Spring Boot的传递性依赖,所以我们需要通过将以下内容添加到build.gradle中,将其从构建依赖关系树中排除:

configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}

我们还需要添加Jetty的编译依赖关系:

compile("org.springframework.boot:spring-boot-starter-jetty")

因为WebConfiguration类中的RemoteIpFilterl类,是 Tomcat提供的类,所以,我们需要把这块代码注释掉。

 /*
@Bean
public RemoteIpFilter remoteIpFilter() {
return new RemoteIpFilter();
}
*/

运行 ./gradlew clean bootRun。这时查看控制台,出现如下信息,说明Jetty 已经运行了。

这就是Spring Boot的自动配置的强大。 我们必须从构建文件中删除Tomcat依赖关系,以防止Tomcat和Jetty之间的依赖冲突。 Spring Boot对类路径中的类进行条件扫描,并根据其检测到的内容,确定将使用哪个servlet容器。

如果我们查看EmbeddedServletContainerAutoConfiguration类的源代码,会看到以下条件判读,用于检查Jetty包中是否存在Servlet.classServer.classLoader.class中,以确定是否应使用JettyEmbeddedServletContainerFactory

/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class})
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty { @Bean
public JettyEmbeddedServletContainerFactory
jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}

@ConditionalOnClass注解告诉Spring Boot ,当 Jetty 的org.eclipse.jetty.server.Serverorg.eclipse.jetty.util.Loader类存在 classpath 中,则使用EmbeddedJetty配置。

五. 添加自定义连接

企业应用程序开发和部署中的另一个常见的情况是使用两个单独的HTTP端口连接器运行应用程序:一个用于HTTP,另一个用于HTTPS。

前面例子中,我们使用了 Jetty 容器,下面的例子,还是使用默认的 tomcat,所以,需要注释掉以前的所有 Jetty 的配置。

为了创建HTTPS连接,我们需要一些东西; 但最重要的是,需要生成用于加密和解密与浏览器的SSL通信的证书密钥库。

如果你使用的是Unix或Mac,可以运行以下命令:

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

如果是 Windows 系统,使用下面命令:

"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

在创建密钥库期间,你应该输入适合您的信息,包括密码,名称等。 我们将使用默认密码:changeit。 执行完成后,新生成的密钥库文件将在系统主目录(.keystore)出现。

Tips

可以在以下位置找到有关证书密钥库信息:

https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html#Prepare_the_Certificate_Keystore

密钥库创建完成后,需要创建一个单独的属性文件,以便存储HTTPS连接的配置,例如端口等。 之后,创建一个配置属性绑定对象,并使用它来配置新的连接。 执行以下步骤:

首先,在src/main/resources目录下创建tomcat.https.properties文件,下面是具体的内容:

custom.tomcat.https.port=8443
custom.tomcat.https.secure=true
custom.tomcat.https.scheme=https
custom.tomcat.https.ssl=true
custom.tomcat.https.keystore=${user.home}/.keystore
custom.tomcat.https.keystore-password=changeit

接下来,在WebConfiguration类中,创建一个静态内部类TomcatSslConnectorProperties,代码如下:

@ConfigurationProperties(prefix = "custom.tomcat.https")
public static class TomcatSslConnectorProperties {
private Integer port;
private Boolean ssl= true;
private Boolean secure = true;
private String scheme = "https";
private File keystore;
private String keystorePassword; // 省略了getter 和 setter 方法 public void configureConnector(Connector connector) {
if (port != null)
connector.setPort(port);
if (secure != null)
connector.setSecure(secure);
if (scheme != null)
connector.setScheme(scheme);
if (ssl!= null)
connector.setProperty("SSLEnabled", ssl.toString());
if (keystore!= null && keystore.exists()) {
connector.setProperty("keystoreFile", keystore.getAbsolutePath());
connector.setProperty("keystorePassword", keystorePassword);
}
}
}

现在,需要将新创建的tomcat.http.properties文件添加为Spring Boot属性源,并启用TomcatSslConnectorProperties绑定。 这可以通过在WebConfiguration类的类声明之上添加以下代码来完成:

@Configuration
@PropertySource("classpath:/tomcat.https.properties")
@EnableConfigurationProperties(WebConfiguration.TomcatSslConnectorProperties.class)
public class WebConfiguration extends WebMvcConfigurerAdapter {...}

最后,需要创建一个EmbeddedServletContainerFactory类的bean,用于添加HTTPS连接。 通过将以下代码添加到WebConfiguration类来实现:

@Bean
public EmbeddedServletContainerFactory servletContainer(TomcatSslConnectorProperties properties) {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createSslConnector(properties)
);
return tomcat;
} private Connector createSslConnector(TomcatSslConnectorProperties properties) {
Connector connector = new Connector();
properties.configureConnector(connector);
return connector;
}

启动./gradlew clean bootRun

在浏览器中输入:https://localhost:8443/internal/tomcat.https.properties,

点击箭头所指部分,然后就会下载 tomcat.https.properties 文件。

上面的程序有一些改动,除了生成密钥库,还创建了tomcat.https.properties配置文件,和创建了TomcatSslConnectorProperties用于属性绑定。以前,在配置DataSource时,我们已经处理了对application.properties中各种设置的更改。 那时候,我们并不需要创建任何绑定对象,因为Spring Boot已经定义了它们。

如前所述,Spring Boot已经公开了许多属性来配置应用程序设置,包括服务器配置的一整套设置。 这些值绑定到内部Spring Boot类:ServerProperties。

Tips

常见应用程序属性的完整列表可以在Spring Boot参考文档中找到:http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

我们只是简单模仿了 Spring Boot 创建了一个配置文件,并绑定了文件中的属性。之所以没有使用已经存在的“server”前缀,而是选择了“custom.tomcat”的原因是由于ServerProperties在检测到未知配置字段时禁止重用命名空间并在属性绑定期间抛出异常,因为它将一直在我们这个例子。

@ConfigurationProperties(prefix = "custom.tomcat.https")对于TomcatSslConnectorProperties对象来说是一个非常重要的注解。它告诉 Spring Boot 自动绑定“custom.tomcat.https”前缀的属性与TomcatSslConnectorProperties类中声明的属性。为了进行绑定,除了定义类中的字段之外,定义getter和setter也是非常重要的。 还值得一提的是,在绑定过程中,Spring将自动尝试将属性值转换为适当的数据类型。 例如,custom.tomcat.https.keystore的值自动绑定到一个专用的文件密钥库字段对象。

Tips

我们之前了解的转换器,也可以转换自定义数据类型。

下一步是告诉Spring Boot在属性列表中包含在tomcat.https.properties中定义的属性。 这通过在WebConfiguration类中@PropertySource("classpath:/tomcat.https.properties")注解来实现。

导入属性值之后,需要告诉Spring Boot自动创建一个TomcatSslConnectorProperties的实例供我们使用。 这是通过添加以下注释来完成的:

@EnableConfigurationProperties(WebConfiguration.TomcatSslConnectorProperties.class)

完成所有属性的设置和完成后,我们将继续执行代码创建第二个连接。 EmbeddedServletContainerFactory bean的创建为Spring Boot提供了一个工厂类来创建EmbeddedServletContainer。 添加静态内部类TomcatSslConnectorProperties中configureConnector(Connector connector)方法提供了一个很好的地方来封装和整合配置新创建的Connector实例所需的所有设置。

Spring Boot 学习(3)的更多相关文章

  1. Spring Boot学习大全(入门)

    Spring Boot学习(入门) 1.了解Spring boot Spring boot的官网(https://spring.io),我们需要的一些jar包,配置文件都可以在下载.添置书签后,我自己 ...

  2. Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客

    ==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...

  3. Spring boot学习1 构建微服务:Spring boot 入门篇

    Spring boot学习1 构建微服务:Spring boot 入门篇 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

  4. spring boot 学习资料

    spring boot 学习资料: 学习资料 网址 Spring Boot Cookbook-极客学院 http://wiki.jikexueyuan.com/project/spring-boot- ...

  5. Spring Boot学习笔记2——基本使用之最佳实践[z]

    前言 在上一篇文章Spring Boot 学习笔记1——初体验之3分钟启动你的Web应用已经对Spring Boot的基本体系与基本使用进行了学习,本文主要目的是更加进一步的来说明对于Spring B ...

  6. spring boot 学习(十四)SpringBoot+Redis+SpringSession缓存之实战

    SpringBoot + Redis +SpringSession 缓存之实战 前言 前几天,从师兄那儿了解到EhCache是进程内的缓存框架,虽然它已经提供了集群环境下的缓存同步策略,这种同步仍然需 ...

  7. 转载:spring boot学习

    Spring Boot学习 Spring Boot是为了简化Spring应用的创建.运行.调试.部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置. 简单来说,它提 ...

  8. Spring Boot学习路线

    Spring Boot 学习路线,本文计划根据作者近几年的工作.学习经验,来分析和制定一个学习使用 Spring Boot技术的步骤路线图. SpringBoot是伴随着Spring4.0诞生的: S ...

  9. 我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题

    Spring Boot版本: 2.0.0.RELEASE 这里需要引入依赖 spring-boot-starter-web 这里有可能有个人的误解,请抱着怀疑态度看. 建议: 感觉自己也会被绕晕,所以 ...

  10. 15 个优秀开源的 Spring Boot 学习项目,一网打尽!

    Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受 ...

随机推荐

  1. 【Shell】使用Shell脚本发布项目

    第一次写Shell脚本,没经验,是直接写呢,还是要走流程( ̄▽ ̄)~* ---------------------------------------------------------------- ...

  2. 浅析JS中的模块规范AMD和CMD

    一.AMD AMD就只有一个接口:define(id?,dependencies?,factory); 它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样: d ...

  3. SQL生成一年每一天的时间列表的几种方法

    工作好几年了,一直没有写博客,准备捡起来...   以下脚本适用环境:SQL SERVER (starting with 2012)   1.构建序列:   /*1-1:利用交叉连接,推荐下列这种写法 ...

  4. 清北Day 2

    清北第二天,感受到了来自这个世界的不友善,大概把没听过不会的"名词"记录下来就已经一面了,然后被大佬说这都是最基础的东西,就很皮,那就趁别人练习字符串的题的时候,来写波博客了,倒不 ...

  5. 【BZOJ1001】[BeiJing2006]狼抓兔子

    挺简单一个题,最小割模板 我的感觉就是可能建图的时候会比较麻烦吧,毕竟三个方向. #include <cctype> #include <climits> #include & ...

  6. angularjs jsonp跨域

    <script> (function(angular){ "use strict" var app= angular.module('appController',[] ...

  7. angularjs ng-class

    ng-class指令可以设置一个键值对,用于决定是否添加一个特定的类名,键为class名,值为bool类型表示是否添加该类名 <style> .red { color: red; } .g ...

  8. session与cookie-----2017-05-08

    会话控制:目的是记录不同用户身份. 1.session:有实效性 特点: (1)存在在服务器 (2)每个用户都会存一份 (3)可以存储任意类型的数据 优点:安全性高 缺点:服务器压力过大 2.cook ...

  9. 吕鑫VC6.0-VS2015 全套C/C++、MFC新手实战入门教程、Linux视频教程 最好的基础入门教程没有之一

    本课程包括:[1]C语言(1个月)[2]C++语法与数据结构(1个月)) [3]MFC项目开发(1个月)[4]Linux项目开发(1个月)往届的授课视频都已经上传到百度网盘,请同学们按照视频教程提前掌 ...

  10. Java内存使用量测试

    JVM内存使用量测试测试各种不同的数据结构在JVM中的内存使用量 import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import java.lang. ...