文 by / 林本托html
Tips
作一个终身学习的人。git
Tips
代码路径:https://github.com/iqcz/Springbootdemo/tree/master/code01/ch3github
在此章节中,主要包括以下内容:web
当咱们构建Web应用程序时,并不老是使用一些默认的配置。 有时,咱们要建立包含字符“.”的 RESTful 风格的 URL。“.”字符在Spring做为分隔符定义格式,例如path.xml中的点,或者咱们可能不想识别路径尾部的斜杠,如/home/
等。 Spring为咱们提供了对这些问题提供了一种轻松的实现。spring
在前面的第二节中,咱们介绍了WebConfiguration
类,它继承了WebMvcConfigurerAdapter
类。 经过继承,能够重写面向过滤器,格式化,还有其余格式的方法。 一样,还能够重写配置路径匹配的方法。apache
假设ISBN格式容许使用点来将图书编号与修订版本分开,看起来像“[isbn-number].[revision]”的格式。编程
咱们将配置咱们的应用程序不使用“.*”的后缀模式匹配,而且在解析参数时不忽略点以后的值。 咱们执行如下步骤:浏览器
首先,须要在WebConfiguration
类中,加入以下内容:缓存
@Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.setUseSuffixPatternMatch(false). setUseTrailingSlashMatch(true); }
同时,须要引入import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
类。tomcat
而后,执行./gradlew clean bootRun
命令。
在浏览器中,输入 http://localhost:8080/books/978-1-78528-415-1.1 , 而后会看到以下结果:
若是输入正确的 ISBN,会看到以下的结果:
咱们来看看具体作了什么事情。 configurePathMatch(PathMatchConfigurer configurer)
方法有能力设置咱们本身的行为,但愿Spring如何将请求URL路径与控制器参数相匹配:
configurer.setUseSuffixPatternMatch(false)
方法表示不想使用“.*”后缀,不忽略最后一个点号后面的字符。这转换为Spring解析整个978-1-78528-415-1.1做为BookControlle
r的{isbn}参数。因此, “http://localhost:8080/books/978-1-78528-415-1.1” 和 http://localhost:8080/books/978-1-78528-415-1” 是两个不一样的 URL。
configurer.setUseTrailingSlashMatch(true)
方法表示咱们想使用“/”在URL中做为匹配,即便 URL 中不存在“/”。 因此,“http://localhost:8080/books/978-1-78528-415-1” 和 “http://localhost:8080/books/978-1-78528-415-1/” 效果是同样的。
若是要进一步配置路径匹配的方式,能够提供本身的PathMatcher
和UrlPathHelper
实现,但这些在最极端和自定义的状况下都是必需的,通常状况下不推荐使用。
在前面的内容中,咱们讲解了如何调整请求的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)
方法为给定资源配置缓存间隔。
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,实现方式将有所不一样。
尽管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.class
,Server.class
和Loader.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.Server
和org.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实例所需的全部设置。