-
SpringBoot内嵌服务器----源码分析
2020-11-26 14:48:40本文主要针对内嵌服务器的原理以及如何切换不同内嵌服务器进行介绍。 下面针对springBoot2.3.4版本进行简单的介绍。springBoot主要内嵌的服务器从官方文档可以看到: 由此可知,主要集成了jetty,netty,tomcat以及...SpringBoot 集成的服务器源码分析
本文主要针对内嵌服务器的原理以及如何切换不同内嵌服务器进行介绍。
下面针对springBoot2.3.4版本进行简单的介绍。springBoot主要内嵌的服务器从官方文档可以看到:
由此可知,主要集成了jetty(适合交互式消息),tomcat以及undertow(不支持JSP,并发性能较高)三种,spring-boot-starter-web默认使用的是tomcat服务器。我们在启动springboot的时候就会发现,不需要导入tomcat组件就可以直接运行,这就是内嵌服务器。这与传统的打war包不同,内嵌服务器直接将项目打包成jar包,使用 java -jar + jar包名 来直接部署,而打war包需将war包放入tomcat目录下的webapps,启动Tomcat才可以。如何修改这些内嵌服务器的配置
查看ServerProperties.class类
public class ServerProperties { private Integer port; private InetAddress address; @NestedConfigurationProperty private final ErrorProperties error = new ErrorProperties(); private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy; private String serverHeader; private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L); private Shutdown shutdown; @NestedConfigurationProperty private Ssl ssl; @NestedConfigurationProperty private final Compression compression; @NestedConfigurationProperty private final Http2 http2; private final ServerProperties.Servlet servlet; private final ServerProperties.Tomcat tomcat; private final ServerProperties.Jetty jetty; private final ServerProperties.Netty netty; private final ServerProperties.Undertow undertow;
这些配置都可在application.yml/application-{profiles}.yml/application.properties等配置文件下配置,例如:
server: port: 8089 #开启压缩 compression: enable: true #压缩文件类型 # 触发压缩的最小内容长度 min-response-size: 2048 mime-types: /* servlet: # session 过期时间,注意单位是秒 session: timeout: 3600 undertow: threads: io: 8 worker: 256
如何切换内嵌服务器
由于我们在引入spring-boot-starter-web模块的时候,默认会将tomcat作为servlet容器。因此,在pom.xml中更改如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
内嵌服务器自动配置原理介绍(如何使用)
在SpringBoot的自动配置类路径下找到:org\springframework\boot\spring-boot-autoconfigure\2.3.4.RELEASE\spring-boot-autoconfigure-2.3.4.RELEASE.jar!\org\springframework\boot\autoconfigure\web\servlet\ServletWebServerFactoryAutoConfiguration.class,
@Configuration( proxyBeanMethods = false ) @AutoConfigureOrder(-2147483648) @ConditionalOnClass({ServletRequest.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) @EnableConfigurationProperties({ServerProperties.class}) @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
这里主要关注两个注解:
@EnableConfigurationProperties({ServerProperties.class})//加载可配置的属性
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})//导入内嵌服务器插件
下面对具体实现进行分析:public class ServletWebServerFactoryAutoConfiguration { public ServletWebServerFactoryAutoConfiguration() { } @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnClass( name = {"org.apache.catalina.startup.Tomcat"} ) public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnMissingFilterBean({ForwardedHeaderFilter.class}) @ConditionalOnProperty( value = {"server.forward-headers-strategy"}, havingValue = "framework" ) public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() { ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean(filter, new ServletRegistrationBean[0]); registration.setDispatcherTypes(DispatcherType.REQUEST, new DispatcherType[]{DispatcherType.ASYNC, DispatcherType.ERROR}); registration.setOrder(-2147483648); return registration; } //前面主要注册了三个Bean对象,分别为ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer和FilterRegistrationBean。 public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; public BeanPostProcessorsRegistrar() { } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory)beanFactory; } } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory != null) { this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } } private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } } } } //后续三个函数主要注册了WebServerFactoryCustomizerBeanPostProcessor.class和ErrorPageRegistrarBeanPostProcessor.class两个类。
分析:
WebServerFactoryCustomizerBeanPostProcessor.class:后置处理器。bean初始化前后(创建完对象,还没赋值),执行初始化工作。
//如果当前初始化的是一个webServerFactory对象,就执行postProcessBeforeInitialization
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
this.postProcessBeforeInitialization((WebServerFactory)bean);
}
return bean;
}
//获取所有的定制器,调用每一个定制器的customize方法为其赋值。这里的customize方法就是为其做赋值处理的。
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
} -
Spring MVC 使用 Jetty 作为内嵌服务器
2018-03-27 23:53:53在本文中,我们将使用 Spring Web MVC 技术来实现 REST 接口,并使用 使用 Jetty 作为内嵌服务器,方便测试。 接口设计 我们将会在系统中实现两个接口: GET http://localhost:8080/hello GET ...Jetty 是高性能的 Servlet 容器,经常会在开发环境中作为服务器来使用。在本文中,我们将使用 Spring Web MVC 技术来实现 REST 接口,并使用 使用 Jetty 作为内嵌服务器,方便测试。
接口设计
我们将会在系统中实现两个接口:
其中,第一个接口“/hello”将会返回“Hello World!” 的字符串;而第二个接口“/hello/way”则会返回一个包含用户信息的JSON字符串。
系统配置
我们需要在应用中添加如下依赖:
<properties> <spring.version>5.0.4.RELEASE</spring.version> <jetty.version>9.4.9.v20180320</jetty.version> <jackson.version>2.9.4</jackson.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>${jetty.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> </dependencies>
其中,
spring-webmvc
是为了使用 Spring MVC 的功能。jetty-servlet
是为了提供内嵌的 Servlet 容器,这样我们就无需依赖外部的容器,可以直接运行我们的应用。jackson-core
和jackson-databind
为我们的应用提供 JSON 序列化的功能。
后台编码实现
领域模型
创建一个 User 类,代表用户信息。
public class User { private String username; private Integer age; public User(String username, Integer age) { this.username = username; this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
控制器
创建 HelloController 用于处理用户的请求。
@RestController public class HelloController { @RequestMapping("/hello") public String hello() { return "Hello World! Welcome to visit waylau.com!"; } @RequestMapping("/hello/way") public User helloWay() { return new User("Way Lau", 30); } }
其中,映射到“/hello”的方法将会返回“Hello World!” 的字符串;而映射到“/hello/way”则会返回一个包含用户信息的JSON字符串。
应用配置
在本应用中,我们采用基于 Java 注解的配置。
AppConfiguration 是我们的主应用配置:
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @ComponentScan(basePackages = { "com.waylau.spring" }) @Import({ MvcConfiguration.class }) public class AppConfiguration { }
AppConfiguration 会扫描“com.waylau.spring”包下的文件,并自动将相关的 bean 进行注册。
AppConfiguration 同时又引入了 MVC 的配置类 MvcConfiguration:
@EnableWebMvc @Configuration public class MvcConfiguration implements WebMvcConfigurer { public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new MappingJackson2HttpMessageConverter()); } }
MvcConfiguration 配置类一方面启用了 MVC 的功能,另一方面添加了 Jackson JSON 的转换器。
最后,我们需要引入 Jetty 服务器 JettyServer:
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import com.waylau.spring.mvc.configuration.AppConfiguration; public class JettyServer { private static final int DEFAULT_PORT = 8080; private static final String CONTEXT_PATH = "/"; private static final String MAPPING_URL = "/*"; public void run() throws Exception { Server server = new Server(DEFAULT_PORT); server.setHandler(servletContextHandler(webApplicationContext())); server.start(); server.join(); } private ServletContextHandler servletContextHandler(WebApplicationContext context) { ServletContextHandler handler = new ServletContextHandler(); handler.setContextPath(CONTEXT_PATH); handler.addServlet(new ServletHolder(new DispatcherServlet(context)), MAPPING_URL); handler.addEventListener(new ContextLoaderListener(context)); return handler; } private WebApplicationContext webApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AppConfiguration.class); return context; } }
JettyServer 将会在 Application 类中进行启动:
public class Application { public static void main(String[] args) throws Exception { new JettyServer().run();; } }
11.13.6 运行
在编辑器中,直接运行 Application 类即可。启动之后,应能看到如下控制台信息:
2018-03-21 23:14:52.665:INFO::main: Logging initialized @203ms to org.eclipse.jetty.util.log.StdErrLog 2018-03-21 23:14:52.868:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T20:21:10+08:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 1.8.0_112-b15 2018-03-21 23:14:52.902:INFO:oejshC.ROOT:main: Initializing Spring root WebApplicationContext 三月 21, 2018 11:14:52 下午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization started 三月 21, 2018 11:14:52 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing Root WebApplicationContext: startup date [Wed Mar 21 23:14:52 CST 2018]; root of context hierarchy 三月 21, 2018 11:14:52 下午 org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions 信息: Registering annotated classes: [class com.waylau.spring.mvc.configuration.AppConfiguration] 三月 21, 2018 11:14:53 下午 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry register 信息: Mapped "{[/hello]}" onto public java.lang.String com.waylau.spring.mvc.controller.HelloController.hello() 三月 21, 2018 11:14:53 下午 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry register 信息: Mapped "{[/hello/way]}" onto public com.waylau.spring.mvc.vo.User com.waylau.spring.mvc.controller.HelloController.helloWay() 三月 21, 2018 11:14:53 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache 信息: Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Wed Mar 21 23:14:52 CST 2018]; root of context hierarchy 三月 21, 2018 11:14:53 下午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization completed in 983 ms 2018-03-21 23:14:53.893:INFO:oejshC.ROOT:main: Initializing Spring FrameworkServlet 'org.springframework.web.servlet.DispatcherServlet-6aaa5eb0' 三月 21, 2018 11:14:53 下午 org.springframework.web.servlet.FrameworkServlet initServletBean 信息: FrameworkServlet 'org.springframework.web.servlet.DispatcherServlet-6aaa5eb0': initialization started 三月 21, 2018 11:14:53 下午 org.springframework.web.servlet.FrameworkServlet initServletBean 信息: FrameworkServlet 'org.springframework.web.servlet.DispatcherServlet-6aaa5eb0': initialization completed in 15 ms 2018-03-21 23:14:53.910:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@2796aeae{/,null,AVAILABLE} 2018-03-21 23:14:54.037:INFO:oejs.AbstractConnector:main: Started ServerConnector@42054532{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} 2018-03-21 23:14:54.038:INFO:oejs.Server:main: Started @1578ms
分别在浏览器中访问 “http://localhost:8080/hello” 和 “http://localhost:8080/hello/way” 地址进行测试,能看到图1和图2的响应效果。
图1 “/hello”接口的返回内容
图2 “/hello/way”接口的返回内容
参考应用
- 源码:见《Spring 5 案例大全》(https://github.com/waylau/spring-5-book)的 “s5-ch11-mvc-rest”应用。
- 原文同步至:https://waylau.com/spring-mvc-use-jetty/
-
为什么Spring Boot要内嵌服务器?
2020-01-13 16:24:39为什么Sprint Boot要内嵌服务器呢? 想象一下如果要部署一个Java应用都需要做些什么呢? 安装JDK 安装Web/Application服务器(Tomcat/Jetty etc) 部署war包 如果我们想要简化这个流程,并且只要拥有Java运行环境...为什么Sprint Boot要内嵌服务器呢?
想象一下如果要部署一个Java应用都需要做些什么呢?
- 安装JDK
- 安装Web/Application服务器(Tomcat/Jetty etc)
- 部署war包
如果我们想要简化这个流程,并且只要拥有Java运行环境就能够运行部署应用,要做些什么呢?
在创建一个可部署的应用时,内嵌一个服务器,在Spring Boot创建一个应用时,会生产一个包含服务器的Jar包,这样就可以运行这个web application香java application 那样
嵌入式服务器意味着我们的可部署单元包含服务器的二进制文件(例如tomcat.jar.
-
Jetty内嵌服务器实例
2014-03-20 11:41:40jetty服务器内嵌实例,运行JettTest后即可访问 -
Tomcat7内嵌服务器
2019-04-21 01:36:20NULL 博文链接:https://liuguxing.iteye.com/blog/1928161 -
SpringBoot框架Day01之核心与内嵌服务器HelloWorld和开发向导
2021-01-26 19:43:20内嵌服务器?springboot优势初体验! 创建maven工厂,pom中添加依赖 编写main函数,类上标明@SpringBootApplication Controller层 使用@RestController注解 在resource目录下创建配置文件application.properties ...核心!
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项
开始
内嵌服务器?springboot优势初体验!
- 创建maven工厂,pom中添加依赖
- 编写main函数,类上标明@SpringBootApplication
- Controller层 使用@RestController注解
- 在resource目录下创建配置文件application.properties
- 使用maven进行打包,通过命令行手动部署在Tomcat服务器中。cmd命令窗口下输入:
java -jar springboot-1.0-SNAPSHOT.jar
springboot依赖管理与版本仲裁
之所以只引入一个spring-boot-starter-parent这个jar包,在spring-boot-dependencies中springboot当前版本自动为开发者实现jar包导入管理。若开发者向更改当前依赖jar包版本号,只需在pom文件中就近原则声明properties自定义修改版本号,实现版本仲裁。
包扫描
使用@SpringBootApplication(scanBasePackages=“com.lwt”)或者不使用前者(前者包含后者)使用@ComponentScan
自动配置原理
spring-boot-starter-web-2.3.4.RELEASE.pom到spring-boot-starter-2.3.4.RELEASE.pom到spring-boot-autoconfigure-2.3.4.RELEASE.pom
前期简单总结
- 引入依赖
- 查看自动配置 debug=true Negative/Positive
- application.properties配置参照
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties 官网。
开发技巧
- lombok @Data
- dev-tools
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
- spring-Initailizr 初始化向导,返璞归真。
-
SpringBoot内嵌服务器启动流程
2021-02-22 23:44:42我们知道spring 底层是servlet,而servlet必须运行在web容器中,而我们使用spring 并没有使用自己定义的tomcat,那为什么会有Tomcat给我们用呢? org.springframework.boot:spring-boot-starter-tomcat:2.4.3 ... -
jetty作为内嵌服务器自启动
2016-05-26 11:11:00为了完成web工程的测试,最近内嵌jetty也要搞起来.第一次搞还是挺焦头烂额的.直接上成果: package com.test.action; import java.io.File; import org.eclipse.jetty.server.Connector; import org.eclipse... -
Embedding Jetty 创建简单的内嵌服务器
2014-01-12 21:15:33创建内嵌的Jetty服务器主要有以下几个步骤: (1)创建一个Server实例 (2)添加或者配置一个Connectors (3)添加或者配置 Handler and/or Contexts and/or Servlet (4)启动Server (5)等待服务,或者... -
JavaWeb之Jetty和Tomcat内嵌服务器实现
2017-10-22 13:33:55在开发Java web项目时候,可以在项目中嵌入tomcat和jetty服务器的方式来运行web程序。 -
使用jetty作为内嵌服务器启动项目
2012-05-11 13:47:32需求:把jetty作为内嵌的一个服务器,直接启动,web项目不用部署在应用服务器中。在网上搜索了一些资料,参照后,都没有成功,经过3天的研究,终于搞定了,记录在此,以备查询。 最开始用的jetty8.1,没成功,后又...
-
java spring启动和终止_springBoot jar启动以停止脚本参数详解
-
FFmpeg4.3系列之16:WebRTC之小白入门与视频聊天的实战
-
租房协议书.docx
-
java sql merge_JDBC支持SqlServer里的Merge方法么?
-
BParkingNANO:制定停车NanoAOD的代码-源码
-
事件循环:事件循环桥接到并行事件-源码
-
java sql server 2016_SQL Server2016正式版安装配置方法图文教程
-
Android 7.0解决抓取不到https请求的问题
-
java sql server 2016_SQL server 2016 安装步骤
-
my-library:用Angular Framework编写的我的文学作品集-源码
-
华为1+X——网络系统建设与运维(中级)
-
java sql package_求大神给填个注释~~ package com.mypro.dao; import java.sql.Connection; import java...
-
react-curse:Udemy curse:React-完整指南-源码
-
java spring上传文件_Java Spring文件上传,Java文件上传,Java通用文件上传
-
京东VOP供应链JAVA_demo.zip
-
华为1+X认证——网络系统建设与运维(初级)
-
java spring定时器_Spring定时器的两种实现方式
-
SPFD5408A PVI 2.6 inch Application Note_20070706.pdf
-
C/C++反汇编解密
-
用Go语言来写区块链(一)