springboot的web应用支持

3.1 web应用配置

3.1.1 设置Http端口

一般是在配置文件中通过server.port进行设置,也可以通过环境变量SERVER_PORT设置端口,通过环境变量设置的端口优先级更高

3.1.2配置随机Http端口

使用随机、未分配的端口只需要设置 server.prot=0即可,如果不希望对外提供Http服务,只需要设置server.prot=-1即可

3.1.3获取运行时Http端口

此外,如果需要应用在运行时动态地获取HTTP端口,则可通过SpringBoot为获取HTTP端口提供的如下两种方式。

➢通过WebServerApplicationContext来获取WebServer对象( WebServer就代表了Spring Boot所使用的Web服务器),接下来即可通过WebServer来获取端口,启动或关闭Web服务

➢通过实现ApplicationListener<WebServerInitializedEvent>接口的Bean (监听器)来监听容器初始化事件,这样同样可获取WebServer对象。

下面通过例子来示范如何在运行时获取HTTP端口。首先创建一个Maven项目,然后修改该项目的pom.xml文件,即进行如下两项修改。

➢让项目的pom.xml文件继承最新版的spring-boot-starter-parent POM文件。
➢添加Spring Boot的springboot-starter web.jar依赖。

如果程序需要在普通组件中动态地获取HTTP端口,则可让该组件实现ApiationLitener<WeSeializedEvenD接口,这样该组件就变成了监听服务器事件的一个容器监听器。

如下是监听器类的代码。

@Component
public class ContainerEventListener 
implements ApplicationListener<WebServerInitializedEvent> {
	@Override
	public void onApplicationEvent(WebServerInitializedEvent evt) {
		System.out.println("动态HTTP端口为:"
				+ evt.getWebServer().getPort());
	}
}

从上面的粗体字代码可以看出,该Bean类实现了ApplicationListener<WebServerInitializedEvent接口,这样Spring容器就会自动将它注册为容器中的监听器。

当应用使用ApplicationContext作为Spring容器时,Spring 容器会自动将容器中所有实现了监听器接口的Bean注册为监听器;但如果使用BeanFactory作为Spring容器,则需要调用BeanFactory的方法来手动注册监听器。

上面的容器监听器还实现了onApplicationEvent( WebServerInitializedEvent evt)方法,该方法将会在Web服务器初始化完成时被自动触发,程序通过该事件方法的参数即可获取WebServer对象(应用所使用的Web服务器),进而获取HTTP端口。

程序获取到HTTP端口只是简单地打印,至于实际应用怎么使用该端口,则需要根据应用的实际业务来定。
此外,还可通过依赖注入来注入WebServerApplicationContext成员变量,这样即可通过该变量来获取WebServer 对象,进而获取HTTP端口。例如如下控制器代码。

@RestController
public class HelloController
{
	@Autowired
	private WebServerApplicationContext ctx;

	@GetMapping("/hello")
	public String hello()
	{
		return "HTTP端口为:" + ctx.getWebServer().getPort();
	}
}

上面程序中的粗体字代码使用@Autowired修饰了WerepplpationContext 类型的变量,
接下来即可通过该变量来获取WebServer对象,进而获取HTTP端口。
运行该应用,当应用的Web服务器初始化完成时,将可以在控制台看到如下输出:

动态HTTP端口为: 50828

上面这行输出即表明应用通过监听器动态获取了HTTP端口。
如果在测试阶段使用@SpringBootTest( webEnvironment=WebEnvironment.RANDOM_PORT指定随机端口进行测试,则可直接使用@LocalServerPort注解修饰成员变量来获取随机的HTTP端口.
例如如下测试用例的代码片段:

@SpringBootTest (webEnvironment=WebEnvironment.RANDOM_PORT)
class MvWebIntearat ionTests{
	@LocalServerPort
	int port;
}

上面测试用例中的粗体字代码即可通过依赖注入来获取HTTP端口。

**注意:@LocalServerPort获取端口

@LocalServerPort只是@Value("$S {local.server.port'")的元注解,千万不要在普通应用中使用该注解来注入HTTP端口。这是因为:该注解修饰的成员变量必须在Web 服务器初始化完成后才会被注入,这样可能导致应用代码去访问该成员变量时,该成员变量还没来得及被注入。**

3.1.4 启用HTTP响应压缩

当使用Tomcat、Jetty 或Undertow 作为Spring Boot 应用的Web 服务器时,可通过oplication.properties (或application.yaml)文件中设置如下属性来开启HTTP响应压缩。

server.compression.enabled=true

开启HTTP响应压缩后,可以减少网络传输的数据量,从而提高HTTP响应的速度。

在默认情况下,只有当HTTP响应的数据大于2048字节时才会开启HTTP响应压缩(道理很简单,如果响应数据本身就不多,压缩就失去意义了)。当然,Spring Boot也允许通过属性来改变该默认行为,例如,在applcation.properteies (或aplpcatoin,yaon))文件中设置如下属性:

server.compression.min- response-size=1024

上面的属性设置表明:只要HTTP响应的数据大于1024字节就会开启HTTP响应压缩。

在默认情况下,只有当HTTP响应的内容类型(content type)是以下几种时才会开启HTTP响
应压缩。

text/xml

text/plain

text/css

text/javascript

application/javascript

➢application/json

➢application/xml

从上面这些内容类型可以看出,Spring Boot默认只会对常见的文本响应(javascript、 json响应依然是文本响应)执行压缩。如果希望应用对某些特定的响应类型进行压缩,则可通过application.properties ( 或application.yaml)文件中的**server.compression.mime-types 属性**进行配置。

server.compression.mime-types=text/html, text/ CSS

上面的属性配置意味着仅当HTTP响应的内容类型是text/html和text/css时才执行压缩。

3.1.5 Web服务器的编程式配置

如果希望使用编程式的方式对Web服务器进行配置,SpringBoot则提供了如下两种方式。

定义一个实现WebServerFactoryCustomizer接口的Bean实例。

直接在容器中配置一个 自定义的ConfigurableServletWebServerFactory,它负贵创建Web服务器。

实现WebServerFactoryCustomizer接口的Bean 实例可以访问ConfigurableServletWebServerFactory对象,调用ConfigurableServletWebServerFactory的方法即可对Web服务器进行配置。

如下例子示范了通过实现WebServerFactoryCustomizer接口来配置Web服务器。首先创建一个Maven项目,然后修改该项目的pom.xml文件,即进行如下两项修改。

➢让项目的pom.xml文件继承最新版的spring-boot-starter-parent POM文件。
➢添加Spring Boot的spring-boot-starter-web.jar依赖。
接下来为应用添加一个控制器来生成RESTful响应用于测试,并通过如下Bean类对Web服务器进行配置。

@Component
public class CustomizationBean 
implements ** WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>**
{
	@Override
	public void customize(ConfigurableServletWebServerFactory server)
	{
		// 设置端口
		server.setPort(8181);
		// 设置该服务器的context Path
		server.setContextPath("/mytest");
		Compression compression = new Compression();
		compression.setMinResponseSize(DataSize.ofBytes(1024));
		// 设置启用HTTP响应压缩
		server.setCompression(compression);
	}
}

正如从上面代码所看到的,该Bean类实现了WebServerFactoryCustomizer接口,该Bean就必须实现public void customize()方法,通过该方法的参数即可访问ConfigurableServletWebServerFactory参数,该参数就代表了本应用所使用的Web服务器,剩下的就是调用该参数的方法对Web服务器进行配置。

上面示例将应用的HTTP端口设为8181,并将应用的context Path 修改为“/mytest",还设置了开启HTTP响应压缩一只要响应数据大于1024字节就开启HTTP响应压缩。

运行该应用的主类来启动应用,将可以在控制台看到如下日志输出:

疯狂springboot终极PDF下载 疯狂springboot终极讲义_疯狂springboot终极PDF下载


正如从该示例所看到的,使用编程式配置完全可以获得对Web服务器的全部控制权。换句话说,编程式配置才是Web服务器配置的本质,使用application.properties ( 或application.yaml)配置只是一种快捷方式。

提示:
有些开发者往往对aplcationproperties配置感到困惑:觉得该文件支持的配置选:项太多了,好像除了查文档或死记,全无任何规律可循。其实这都是因为没有掌握Spring Boot本质的缘故,一旦明白了 applcation.propertes配置只是编程式配置的快捷方式,以及application.properties 文件中所有的配置选项通常完全对应于编程式配置的setter方法,就不会对application.properties文件感到困惑了。

除可通过实现WebServerFactoryCustomizer接口对Web服务器进行配置之外,还可直接在容器中配置**一个自定义的ConfigurableServletWebServerFactory来对Web服务器进行配置**,正如前面所介绍的,开发者配置了自定义的ConfigurableServletWebServerFactory之后,它就取代了Spring Boot自动配置的ConfigurableServletWebServerFactory,而ConfigurableServletWebServerFactory则负责创建Web服器。
由于ConfigurableServletWebServerFactory 只是一个接口, 因此通常会使用它的如下三个实现
类来进行具体设置。

JettyServletWebServerFactory: 代表Jetty服务器。

TomcatServletWebServerFactory: 代表Tomcat服务器。

UndertowServletW ebServerFactory:代表Undertow服务器。

下面的例子示范了使用自定义的ConfigurableServletWebServerFactory代替自动配置的该Bean.首先创建一个Maven项目,然后让其pom.xml 文件继承spring-boot-starter-parent, 并添加spring-boot-starter-web.jar依赖。
接下来使用Java配置方式创建并返回一个ConfigurableServletWebServerFactory Bean,它就会
代替Spring Boot自动配置的ConfigurableServletWebServerFactory Bean。

@Configuration
public class AppConfig
{
	@Bean
	public ConfigurableServletWebServerFactory webServerFactory()
	{
		**TomcatServletWebServerFactory factory = 
		new TomcatServletWebServerFactory();**
		// 设置端口
		factory.setPort(8181);
		Session session = new Session();
		// 设置服务器session的超时时长为10分钟
		session.setTimeout(Duration.ofMinutes(10));
		factory.setSession(session);
		// 设置404的错误页面
		factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
		// 设置该服务器的context Path
		factory.setContextPath("/newtest");
		Compression compression = new Compression();
		compression.setMinResponseSize(DataSize.ofBytes(1024));
		// 设置启用HTTP响应压缩
		factory.setCompression(compression);
		return factory;
	}
}

上面AppConfig 类使用了@Configurntion注解修饰,这意味着该Java 类其实就相当于一份Spring配置文件,可以近似地把该注解当成XML配置文件的<beans…/>根元素,

上面AppConfig类中被@Bean注解修饰的方法就相当于配置了一个Bean实例(其作用大致类似于XML配置文件中的ban…>.元素),这意味着上面的webServerFactory()方法其实就是在Spring容器中配置了一个ConfigurableServletWebServerFactory对象,它就代替了Spring Boot自动配置的该Bean,而该Bean将会负责创建Web服务器。

上面程序中的粗体字代码创建了一个TomeatServletWebServerFactory对象,这表明本应用将会使用Tomcat作为Web服务器,接下米的代码就是调用该对象的setter方法来对Web服务器进行配置了。

上面示例将应用的HTTP端口设为8181,将服务器sossion的超时时长设为10分钟,并将应用的context Path修改为“/newtest",还设置了开启HTTP响应压缩一-只 要响应数据大于1024字节就开启HTTTP响应压缩。
运行该应用的上类来启动应用,将可以在控制台看到如下日志输出:

可见,直接在容鼎申定义ConfgurableServetWebServerFactory来配置Web服务器不仅可以设置一些通用行为, 还可以针对特定的Web服好牌(例如该示例中的Tomeat)进行设置。

事实上,使用applicaion.properties文件同样可针对特定的Web服务器进行特定的设置、例如,server.tomcat.*.代表专门针对Tonneat进行设理: server.jetty 代表专门针对Jetty进行设置。

3.2为应用添 加Servlet、Fiter. Listener

对于传统Web应用而言, Servlet、Filter和Listener是最重要的“三大金刚”,但自从Spring MVC(Spring Boot 自动整合了Spring MVC)流行之后,很多开发者甚至忘记了这三种最重要的组件,
实际上,即使使用Spring MVC,在某些极端场景下,开发者也依然需要使用传统的Servlet、Filter和Listener组件。
Spring Boo允许开发者在Web应用中使用传统的Srvlet、Filter 、Listener 组件,为Spring Boot应用添加Servlet. Filter Listener 有如下三种方式:

使用Spring Bean添加Servlet、Filter 或Listener

>使用XxzRegistrationBean手动添加Servlet. Filter 或Listener.

使用Classpath扫描添加Servlet、Filter 或Listener.

3.2.1使 用Spring Bean添加Servlet、Filter 或Listener

对于Spring Boot 而言,它会自动把Spring容器中的Servlet、Filter、 Listener 实例注册为Web服务器中对应的组件。
通过这种方式添加的Servlet、Filter 或Listener,由于它们都是Spring容器中的Bean,因此可以方便地访问application.properties中配置的属性值,也可以利用依赖注入将Spring容器中的其他Bean注入这些Servlet、Filter 或Listener.
当Spring容器中只有一个Servlet时,它默认被映射到应用的根路径为“/”当Spring容器中包含多个Servlet时,它们的映射地址就是其Bean名称(name属性值)而Filter的映射地址则默认为“/*”。

下面的例子将会示范通过Spring Bean来添加Servlet、Filter 或Listener。首先创建一个 Maven项目,然后让其pom.xml文件继承springobot-tarter-parent,并添加sringot-strterwebjar依赖。

接下来为该应用添加如下Servlet。

public class FirstServlet extends HttpServlet
{
	@Value("${crazyit.greeting}")
	private String greeting;
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		log("----FirstServlet---");
		resp.setContentType("text/html");
		resp.setCharacterEncoding("utf-8");
		PrintWriter out = resp.getWriter();
		out.println("为Spring Boot添加的第一个Servlet,信息:" + greeting);
	}
}

上面的FirstServlet类继承了HttpServlet, 表明它就是一个标准的Servlet; 但由于在本例中会通过把它配置成Spring Bean的方式来注册它,因此它可以很方便地访问application properties中配置的属性值,如上面程序中的粗体字注解@Value(“S rcrtyitgeeting”),它的作用就是将application.properties中的crazyit.greeting属性值赋值给greeting 成员变量。

由于该Servlet是Spring容器中的Bean,因此也可以将容器中的其他Bean注入该Servlet

本例还提供了另一个Servlet-- SecondSerlvet, 其代码也比较简单,

public class SecondServlet extends HttpServlet
{
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		resp.setContentType("text/html");
		resp.setCharacterEncoding("utf-8");
		PrintWriter out = resp.getWriter();
		out.println("为Spring Boot添加的第二个Servlet");
	}
}

下面是本例的Filter类代码。

public class CrazyitFilter implements Filter
{
	private static final Logger LOG = LoggerFactory.getLogger(CrazyitFilter.class);
	@Override
	public void doFilter(ServletRequest requ, ServletResponse resp,
			FilterChain filterChain) throws IOException, ServletException
	{
		LOG.info("处理请求之前的过滤处理");
		// 放行请求,继续让目标Servlet(或其他Web组件)处理用户请求
		filterChain.doFilter(requ, resp);
		LOG.info("处理请求之后的过滤处理");
	}
}

上面的CrazyitFilter类实现了Filter 接口,因此它就是-个标准的Filter.

下面是本例的Listener类代码。

上面的CrazyitListener类实现了ServletContextListener 接口,因此它就是一个可用 于监听Web应用初始化和应用销毁两个事件的监听器。

根据监听目标的不同,Servlet 规范提供了如下监听器接口供开发者使用,分别用于实现监听不同目标的监听器。

ServletContextAttributeListener: 监听ServletContext ( application)范围内属性变化事件的监听器接口。

ServletRequestListener: 监听ServletRequest (用户请求)创建或关闭事件的监听器接口。

ServletRequsetAttributeListener : 监听ServletRequet (用户请求)范围内属性变化事件的监听器接口。

HttpSessionAttributeLitener监听HttpSession (用户会话)范围内属性变化事件的监听器接口。

HttpSessionListener:监听HtpSession (用户会话)创建或关闭事件的监听器接口。

ServletContextListener: 监听ServletContext (application) 启动或关闭事件的监听器接口。

不管是上面哪一种监听器,只要将其部署成Spring容器中的Bean,SpringBoot就会自动把它
注册成Web应用中的监听器。

接下来同样使用Java配置方式将这些Servlet、Filter和Listener部署成Spring容器中的Beano
下面是本例的AppConfig类代码。

@Configuration
public class AppConfig
{
	@Bean("first")
	public HttpServlet createServlet1()
	{
		FirstServlet firstServlet = new FirstServlet();
		return firstServlet;
	}

	@Bean("second")
	public HttpServlet createServlet2()
	{
		SecondServlet secondServlet = new SecondServlet();
		return secondServlet;
	}

	@Bean
	public ServletContextListener createListener()
	{
		CrazyitListener listener = new CrazyitListener();
		return listener;
	}
	@Bean
	public Filter createFilter()
	{
		CrazyitFilter filter = new CrazyitFilter();
		return filter;
	}
}

上面@Configuration修饰的AppConfig 类定义了4个@Bean修饰的方法,这些方法用于将FirstServlet、SecondServlet、CrazyitListener、CrazyitFilter部署成Spring容器中的Bean,而Spring Boot会自动把它们注册成Web容器中的Servlet、Listener、 Filter。

运行该示例的主类来启动应用,将可以在控制台看到如下日志输出:

---- Web应用初始化完成----

上面的输出即表明CrazyitListener 已经开始起作用了一当Web 应用初始化时,该监听器监听到了该事件。

使用浏览器向http:/主机名:8080/first/" (不要忘记了first 后面的斜杠)地址发送请求,即可在控制台看到如下输出:

疯狂springboot终极PDF下载 疯狂springboot终极讲义_疯狂springboot终极PDF下载_02


上面的输出就是CrazyitFilter和FirstServlet的日志输出结果。

上面示例的Spring容器中配置了两个Servlet, 因此它们的映射地址就是其Bean名称(name属性值)。例如,上面配置FirstServlet的name属性值为“first",因此它的映射地址就是“ first/";配置SecondServlet的name属性值为“second",因此它的映射地址就是“ second/".

注意:Spring容器中只有一个Servlet

当Spring容器中只有一个Servlet时, 该Servlet的name属性不会起作用,它的映射地址总是“/ ”

3.2.2 使用XxxRegistrationBean注册Servlet、 Filter或Listener

前面介绍的方式是Servlet、Filter 总是使用默认的映射地址,如果嫌这种约定的映射方式不够灵活,则可使用ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean 来注册Servlet、Filter 和Listener,这样开发者就可以获得**全部控制权**

从它们的名字不难看出,ServletRegistrationBean 专门用于注册Servlet; FilterRegistrationBean用于注册Filter; ServletListenerRegistrationBean则用于注册Listener,它同样可以注册这些不同类型的Listener: ServletContextAttributeListener 、ServletRequestListener 、ServletRequestAttributeListener、
HttpSessionAttributeListener、HttpSessionListener、 ServletContextListener。
本例使用与前面例子相同的Servlet、 Listener 和Filter,只是改为使用XxxRegistrationBean来

注册它们。下面是本例的AppConfig类代码。

@Configuration
public class AppConfig {
    @Bean
    public ServletRegistrationBean<FirstServlet> createServlet1() {
        FirstServlet firstServlet = new FirstServlet();
        // 注册Servlet
      **  ServletRegistrationBean<FirstServlet> registrationBean =
                new ServletRegistrationBean<>(firstServlet, "/first");**
        return registrationBean;
    }

    @Bean
    public ServletRegistrationBean<SecondServlet> createServlet2() {
        SecondServlet secondServlet = new SecondServlet();
        // 注册Servlet
        ServletRegistrationBean<SecondServlet> registrationBean =
                new ServletRegistrationBean<>(secondServlet, "/second");
        return registrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean<CrazyitListener> createListener() {
        CrazyitListener listener = new CrazyitListener();
        // 注册Listener
        ServletListenerRegistrationBean<CrazyitListener> registrationBean =
                new ServletListenerRegistrationBean<>(listener);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<CrazyitFilter> createFilter() {
        CrazyitFilter filter = new CrazyitFilter();
        // 注册Filter
        FilterRegistrationBean<CrazyitFilter> registrationBean =
                new FilterRegistrationBean<>(filter);
        return registrationBean;
    }
}

正如从上面的粗体字代码所看到的,使用XxxRegistrationBean 注册Servlet、 Listener、Filter的方法非常简单,程序只要在Spring容器中部署包装Servlet、Listener Filter的XxxRegistrationBea对象,Spring Boot就会自动注册被包装的Servlet、Listener、 Filter。
在使用XxxRegistrationBean注册Servlet 和Filter 时,可以直接指定它们的映射地址,如上面
的粗体字代码所示。
运行该示例的主类来启动应用,然后使用浏览器向“http://主机名:8080/first”(注意first 后面没有斜杠,与程序中注册的映射地址保持一致)地址发送请求,依然可以在控制台看到如下输出:

处理请求之前的过滤处理
----FirstServlet---
处理请求之后的过滤处理

此时可以在浏览器中看到如图3.2所示的输出。

疯狂springboot终极PDF下载 疯狂springboot终极讲义_疯狂springboot终极PDF下载_03


从图3.2所示的结果可以看到,此时FirstServlet的greeting成员变量的值为null,这意味着Spring Boot没有将crazyit.greeting属性值注入FirstServlet- 一原因也很简单:此时的FirstServlet 并不是Spring容器中的Bean, 而是由开发者自行创建的对象,开发者获得了该对象的全部控制权,开发者需要自行对其进行设置。

3.2.3使用ClassPath扫描添加Servlet、Filter 或Listener

使用ClassPath扫描添加Servlet、Filter 或Listener的方式更加简单,开发者只要在Servlet类Filter类和Listener 类上分别添加@WebServlet、@WebFilter 和@WebListener注解,再通过@ServletComponentScan注解告诉Spring Boot自动扫描这些Web组件即可。
通过这种方式来添加Servlet、 Filter 和Listener 时,只要简单地添加几个注解即可,基本无须
手动使用代码进行注册。下面是本例的Servlet类代码。

@WebServlet("/first")
public class FirstServlet extends HttpServlet
{
	@Value("${crazyit.greeting}")
	private String greeting;
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		log("----FirstServlet---");
		resp.setContentType("text/html");
		resp.setCharacterEncoding("utf-8");
		PrintWriter out = resp.getWriter();
		out.println("为Spring Boot添加的第一个Servlet,信息:" + greeting);
	}
}
@WebServlet("/second")
public class SecondServlet extends HttpServlet
{
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		resp.setContentType("text/html");
		resp.setCharacterEncoding("utf-8");
		PrintWriter out = resp.getWriter();
		out.println("为Spring Boot添加的第二个Servlet");
	}
}

Listener代码

@WebListener
public class CrazyitListener implements ServletContextListener
{
	private static final Logger LOG = LoggerFactory.getLogger(CrazyitFilter.class);
	@Override
	public void contextInitialized(ServletContextEvent sce)
	{
		LOG.info("----Web应用初始化完成----");
	}
	@Override
	public void contextDestroyed(ServletContextEvent sce)
	{
		LOG.info("----Web应用销毁之前----");
	}
}

Filter代码

@WebFilter("/*")
public class CrazyitFilter implements Filter
{
	private static final Logger LOG = LoggerFactory.getLogger(CrazyitFilter.class);
	@Override
	public void doFilter(ServletRequest requ, ServletResponse resp,
			FilterChain filterChain) throws IOException, ServletException
	{
		LOG.info("处理请求之前的过滤处理");
		// 放行请求,继续让目标Servlet(或其他Web组件)处理用户请求
		filterChain.doFilter(requ, resp);
		LOG.info("处理请求之后的过滤处理");
	}
}

,使用@ServletComponentScan注解让@WebServlet、@WebFilter 和@WebListener注解生效

@Configuration
// 通过该注解设置到指定包中扫描Servlet、Filter、Listener
@ServletComponentScan("org.crazyit.app.web")
public class AppConfig {

}

启动主启动类

输入http://主机名:8080/first”

疯狂springboot终极PDF下载 疯狂springboot终极讲义_spring boot_04


从图3.3所示的结果可以看到,此时FirstServlet的greeting成员变量有值( 接受了Spring 容器的依赖注入),这表明通过这种方式添加的FirstServlet同样可以接受Spring容器的依赖注入一原因也很简单:此时的FirstServlet依然是由Spring Boot扫描并添加到Spring容器中的Bean(它的Beanid就是其全限定类名),因此它可以接受Spring容器的依赖注入

3.3配置内嵌Web服务器

前面示例使用的都是默认的Tomcat服务器,本节将会详细介绍关于内嵌Web服务器的

3.3.1切换到其他Web服务器

很多Spring Boot的Starter都包含了默认的内嵌服务器。其默认的服务器规则如下:

对于基于Servlet 的应用,spring- boot-starter- web.jart默认依赖spring- boot-starter- tomcar. jar因此它默认包含Tomat 作为内嵌服务器。如果项目需要,则也可改为依赖spring-boot-starter-jetty.jar 或spring-boot-starter-undertow.jar,,这将意味着使用Jetty或Undertow作为内嵌服务器。

对于反应式应用,spring- boot-starter-webflux.jar默认依赖spring- boot-starter-reactor-netty.jar,因此它默认包含Reactor Netty 作为内嵌服务器。如果项目需要,则也可改为依赖spring-boot-starter-tomcat.jar、spring-boot-starter-jetty.jar 或spring-boot-starter-undertow.jar,这将意味着使用Tomcat、Jetty或Undertow作为内嵌Reactor服务器。

下面以一个基于Servlet的应用为例来示范如何切换内嵌服务器。依然是先创建一个Maven项目,然后让其pom.xml文件继承pring- boot-starter-parent,并添加pring- boot-starter-web.jar依赖。由于spring-boot-starter-web.jar默认依赖spring- boot-starter- tomcar. jar(这代表使用Tomcat作为内嵌服务器),为了切换到其他Web服务器,需要对pom.xml文件进行如下两步修改:

在spring-boot starter-web.jar依赖配置内使用<exclusios…>元素排除spring-boot-starter-tomcat.jar依赖。

显式添加spring-boot-starter-jetty.jar或spring-boot-starter-undertow.jar依赖。

如果打算使用Undertow作为内嵌服务器,则将pom.xml文件的依赖管理改为如下形式。

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!-- 排除对Tomcat的依赖 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 显式添加对Undertow的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

上面配置中第一段粗体 字代码为spring-boot starter-web;jar依赖排除了springoo-tarter-tomcat.jar依赖,这表示不再使用默认的Tomcat 作为内嵌服务器;第二段粗体字代码显式添加了spring- boot-starter -undertow.jar依赖,这意味着使用Undertow 作为内嵌服务器。

运行该示例的主类来启动应用,将可以在控制台看到如下输出:

疯狂springboot终极PDF下载 疯狂springboot终极讲义_疯狂springboot终极PDF下载_05


通过上面的输出即可看出,本应用不再使用Tomcat 作为内嵌服务器,而是使用Undertow-2.1 .4.Final作为内嵌服务器。

如果想使用Jetty作为内嵌服务器,则将pom.xml文件改为如下形式。

<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>11</java.version>
		<servlet-api.version>3.1.0</servlet-api.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<!-- 排除对Tomcat的依赖 -->
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 显式添加对Jetty的依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jetty</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

上面第二段粗体字代码同样是为springboot-starter-web.jar 依赖排除了默认的spring-boot-starter-tomcatjar依赖;第三段粗体字代码同样是显式添加了springboot-starterjetty.jar 依赖。但由于Jetty9.4系列还不支持Servlet 4.0规范,因此上面配置中第一行粗体字代码显式指定使用Serlet 3.1规范,

然后运行该示例的主类来启动应用,将可以在控制台看到如下输出:

Server initialized with port: 8080
jetty-9.4.31.v20200723;
......
Jetty started on port(s) 8080 (http/1.1) with context path '/

通过上面的输出即可看出,本应用不再使用Tomcat作为内嵌服务器,而是使用Jetty9.4.31作为内嵌服务器。

3.3.2 配置SSL

出于安全考虑,现在都推荐基于SSL访问主流的应用,只要稍微留意一下就不难发现,在大部分网站的网址中都使用了https:// (注意多了一个s),而不是传统的http://,这就意味着该网站是基于SSL的。

为Spring Boot应用配置SSL (Secure Sockets Layer,安全套接字层)非常简单,只需如下两步即可。

➊生成或购买SSL证书。

开发者自己生成的SSL证书通常只是用于测试,如果要部署实际运行的项目,浏览器会提示该SSL证书是不可信任的证书。对于实际运行的项目,应该购买CA机构颁发的SSL证书,只有CA机构颁发的SSL证书才会被浏览器信任,这样浏览器才不会提示证书不可信任。

❷在application.properties (或application.yaml)文件中通过serer.s.*属性进行配置。

需要说明的是,一 旦在applcation.properties (或application.yam)文件中配置了基于SSL的HTTPS连接器,传统的HTTP连接器就被自动关闭了。

提示:
Web服务器接收用户请求、对外提供服务的组件叫作连接器(Connector),不同的连接器在不同的端口对外提供服务。一个Web服务器可配置多个连接器,这样该Web服务器即可在多个端口接收请求、提供服务。打开Tomeat的conf/目录下的server.xml .文件,可以看到有多个<Connector…/>元素,它们就用于为Tomcat配置连接器。打开Jetty的etc/目录下的jty-http.xml文件,可以看到其中有一个
<Call name-"addConector…>. 元素,该元素用于添加传统的HTTP连接器。

Spring Boot不支持在application.properties (或application.yaml)文件中同时配置HTTPS连接器和HTTP连接器。如果希望应用能同时支持HTTPS连接器和HTTP连接器,则推荐使用application.properties ( 或application.yaml) 文件配置HTTPS连接器,然后使用编程式的方式添加HTTP连接器,因为使用编程式的方式添加HTTP连接器比较容易。

下面先创建一个Maven项目,让其pom.xml文件继承spring bo-stater-parent,并添加spring -boot-starter-web.jar依赖。然后为该项目添加一个简单的控制器,并对主类进行简单修改,使之成为一个Spring Boot应用。
接下来为Spring Boot应用配置SSL,只要如下两步即可。

➊生成SSL证书(如果打算购买CA机构颁发的SSL证书,这一步可以省略)。

启动命令行窗口,在该窗口中输入如下命令:

keytool -genkey -v -alias spring -keyalg RSA -keystore D:/spring.keystore -validity 36500

上面命令所使用的keytool是JDK提供的一个工具,如果读者运行该命令时提示找不到该工具,那么一定是JDK还没有配置好。

keytool命令的-genkey是它的子命令,用于生成key。该子命令支持如下常用选项。

-alias: 指定证书别名。
-keyalg: 指定算法。
-kevetore.指定证书存储在哪里
-validity: 指定证书的有效时间,此处指定36500, 这意味着有效期是100年。

接下来会要求输入证书的密码,该密码在后面配置的时候要用到,因此必须记牢。接下来该命令会提示输入姓名、组织、城市、省份、国家信息,逐项完成输入并确认之后,就会在D:/盘下生成一个spring.keystore文件。

生成SSL证书的详细过程如图3.4所示。

疯狂springboot终极PDF下载 疯狂springboot终极讲义_HTTP_06

❷为SpringBoot配置SSL连接器。

将第1步生成的SSL证书复制到应用的resources目录下(与application.properties位于相同目录下),然后在应用的apliatio.properties文件中添加如下配置:

# 配置SSL的服务端口
server.port=8443
# 指定SSL证书的存储位置
server.ssl.key-store=classpath:spring.keystore
# 指定SSL证书的密码
server.ssl.key-store-password=123456

上面配置中的seressl.keystore passord用于指定SSL证书的密码,该密码就是第1步在包建该证书时所输入的密钥库口令密码。

运行该示例的主类来启动应用,将可以在控制台看到如下输出:

Tomcat started on port(s): 8443 (https) with context path ''

从上面的输出可以看到,该应用改为使用8443端口对外提供HTPS服务。

使用浏览器向https://主机名:8444/hello”(注意是htps:/:不再是htptp:/)/ 地址发送请求,此时可以看到如图35所示的输出:

疯狂springboot终极PDF下载 疯狂springboot终极讲义_疯狂springboot终极PDF下载_07

从图35可以看出,此时Spring Boot 应用已改为在8443端口对外提供HTTPS服务。只是游览器会提示“警告:面临潜在的安全风险”,这是由于该应用所使用的SSL证书是我们自己创建的(没得到认证),如果改为使用从CA购买的SSL安全证书,浏览器将不再提示该警告。

3.3.3配置HTTP/2

通过为Spring Boot应用配置serer.http2.enabled属性可以开启HTTP/2支持,但这种支持依在Web服务器和JDK上略有不同。对于JDK 8, HTTP/2 支持不是开箱即用的,建议大家升级JDK 11 (目前Java最新的长期支持版)。
提示:
HTTP/2是为了解决现有的HTTP/1.1 性能不好、安全性不足的问题才出现的,它是目前主流的HTTP协议。

需要说明的是,Spring Boot不支持传统HTTP的HTTP/2,它只支持基于HTTPS的HTTP/2,因此在配置HTTP/2之前必须先配置SSL.
下面针对不同的Web服务器介绍HTTP/2配置。

1. Undertow的HTTP/2支持

从Undertow1.4.0+开始,即使使用早期的JDK8,Undertow也可以很好地支持HTTP/2。

2. Tomcat的HTTP/2支持

从Tomcat9.0.x开始,如果使用JDK 9及更新版本的JDK,Tomcat 默认就可以支持HTTP/2。

如果一定要使用JDK 8让Tomcat 9.0.x 支持HTTP/2,则必须为JDK 8安装libtcnative库,并在操作系统中为libtcnative 库安装它的依赖。

如果没有安装JDK 8所依赖的native库,直接使用Tomcat 9.0.x来开启HTTP/2支持,将会在控制台看到如下错误信息:

The upgrade handler [org . apache . coyote . http2. Http2Protocol] for [h2] only supports
upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"1 connector that
does not support ALPN 。

该错误并不是致命的,但它会导致该应用依然使用HTTP/1.1的SSL支持。

要开启HTTP/2支持,只要将前一一个示例中的application.properties文件改为如下形式即可。

# 配置SSL的服务端口
server.port=8443
# 指定SSL证书的存储位置
server.ssl.key-store=classpath:spring.keystore
# 指定SSL证书的密码
server.ssl.key-store-password=123456

# 开启HTTP/2支持
**server.http2.enabled=true**

上面粗体字代码为Spring Boot应用开启了HTTP/2 支持。由于本书使用JDK 11环境,因此无须为Tomcat添加其他额外的库。

运行该示例的主类来启动应用,,然后使用浏览器向“https://主机名:8443/hello"地址发送请求,接下来可以在控制台看到如图3.6所示的结果。

疯狂springboot终极PDF下载 疯狂springboot终极讲义_spring boot_08

3. Jetty的HTTP/2支持

为了支持HTTP/2, Jetty 需要额外的org.eclipse.jetty.http2的http2-server.jar 依赖支持。此外,根据配置环境的不同,可能还需要添加以下额外的不同依赖。

如果使用 JDK9及更新版本的JDK,则需要添加org.eclipse.jetty的jetty-alpn-java-server.jar依赖。

如果使用JDK 8u252+及更新版本的JDK 8,则需要添加org.eclipse.jetty的jety-alpn-openjdk8-server.jar依赖。

对于其他JDK,则需要添加org.eclipse.jetty的jetty-alpn-conscrypt-server.jar依赖及Conscrypt库。

4. Reactor Netty的HTTP/2支持

spring-boot-webflux- starter默认使用Reactor Netty作为内嵌服务器,如果使用JDK9及更新版本的JDK, Reactor Netty完全可以支持HTTP/2;如果使用JDK 8环境,则需要额外的原生库来支持HTTP/2。

3.3.4 配置访客日志

Web服务器可以将所有访问记录以日志形式记录下来,Spring Boot同样为这种访客日志提了支持 ,Spring Boot为Tomcat、Jetty、 Undertow 分别提供了相应的属性来配置访客日志。依然使用前一个示例,只要在它的application.properties文件中添加如下内容即可。

# 配置SSL的服务端口
server.port=8443
# 指定SSL证书的存储位置
server.ssl.key-store=classpath:spring.keystore
# 指定SSL证书的密码
server.ssl.key-store-password=123456

# 开启HTTP/2支持
server.http2.enabled=true

# 配置Tomcat的基路径
server.tomcat.basedir=my-tomcat
# 开启Tomcat的访客日志记录
server.tomcat.accesslog.enabled=true
# 指定访客日志的记录格式
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)

正如从上面配置所看到的,为Tomeat配置访客日志时配置了三个属性。但为Jetty、Undertow配置访客日志时,通常只需要配置两个属性。

➢sever.xxxx. acesslog enable:用于开启日志。其中xxx可被更换为tomcat、jetty或undertow.

➢senver.xxx. acsslog.pattern: 指定日志的记录格式。其中xx可被更换为tomcat或Undertow

server.jetty. accesslog.file-date-format: 指定日志的记录格式 用于jetty

Tomcat往往需要额外配置一个属性,这是由于Tomcat的访客日志默认总保存在Tomcat基路径下的logs目录(该目录可通过server.tomcat.accesslog.directory属性修改)中,因此为Tomcat配置访客日志时需要额外配置-、个servertomeat.basedir属性,用于指定Tomcat的基路径。

运行该示例的主类来启动应用,然后使用浏览器向“https://主机名:8443/hello"地址多次发送请求,接下来即可在该应用的路径下发现一个my-tomcat目录,该目录下包含了一个logs目录,该目录中保存了Tomcat 的访客日志文件。

疯狂springboot终极PDF下载 疯狂springboot终极讲义_HTTP_09

如果使用Undertow作为服务器,则需要在application.properties文件中添加如下配置:

#开启Undertow的访客日志记录
server.undertow.accesslog.enabled=true
#推定访客日志的记录格式
serverundertow、accesslog.pattern=%t %a "%r" %s (%D ms)

添加上面配置之后,Spring Boot会自动将Undertow的访客日志记录在应用路径下的logs目录(该目录可通过server.undertow.accesslog. directory属性修改)中。

如果使用Jetty作为服务器,则需要在application.properties文件中添加如下配置:

#开启Jetty的访客日志记录
server.jetty.accesslog.enabled=true
#指定日志文件的存储路径
server.jetty.accesslog.filename=/var/log/ jetty-access.log

3.4 管理Spring MVC

SpringBoot的真正功能只是自动配置和快速整合,在SpringBoot应用中前端MVC由Spring MVC充当。因此,前面示例中用到的@Controller、@RestController @RequestMapping

Restful服务支持

访问sql数据库