收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
由于SpringBoot
默认是以 jar包的方式启动嵌入的Servlet
容器来启动SpringBoot
的web
应用,没有web.xml
文件。注册三大组件的方式如下:
【1】通过 ServletRegistrationBean
注册自定义的Servlet
。
//首先创建一个Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello MyServlet");
super.doPost(req, resp);
}
}
//将创建的Servlet通过配置类注入到容器中,两个是不同的类。
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
return registrationBean;
}
【2】通过 FilterRegistrationBean 注册拦截器 Filter。
//自定义一个filter实现servlet.Filter接口
public class myFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.printf("myFilter");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
//通过配置类注入自定义的Filter
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new myFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myFilter"));
return registrationBean;
}
【3】通过`ServletListenerRegistrationBean`注册自定义的`Listener`。
```java
//创建自定义的Listener监听
public class myListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.printf("服务启动");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.printf("服务销毁");
}
}
//通过配置类注入自定义的listener
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return servletListenerRegistrationBean;
}
三、使用其他 Servlet容器:Jetty(长连接引用)、Undertow(不支持JSP)
【1】我们在定制嵌入式的Servlet
容器时,会传入ConfigurableEmbeddedServletContainer
类,我们通过Ctrl+T
查看此可配置嵌入式类容器中可以配置Tomcat
、Jetty
和Undertow
。
//ConfigurableEmbeddedServletContainer
@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8082);
}
};
}
【2】默认使用Tomcat
,因为starter-web
引入的是Tomcat
的starter
。我们排除Tomcat
的依赖,引入Jetty
的依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>spring-boot-starter-Jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
四、嵌入式 Servlet容器自动配置原理
【1】EmbeddedServletContainerAutoConfiguration
类主要用来自动配置嵌入式的Servlet
容器。
@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication
//导入BeanPostProcessorsRegistrar:后置处理器:在bean初始化前后,执行(刚创建完对象,还没属性赋值)初始化工作.
//给容器中导入一些组件,导入了embeddedServletContainerCustomizerBeanPostProcessor
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class})//判断当前Servlet中是否引入的Tomcat依赖
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)//判断当前容器中,没有用户自定义的EmbeddedServletContainerFactory嵌入式的Servlet容器工厂,
//作用:创建嵌入式的servlet容器。
public static class EmbeddedTomcat {
public EmbeddedTomcat() {
}
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedUndertow {
public EmbeddedUndertow() {
}
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedJetty {
public EmbeddedJetty() {
}
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
}
【2】嵌入式的容器工厂:EmbeddedServletContainerFactory
,用来创建嵌入式的Servlet
容器。
public interface EmbeddedServletContainerFactory {
//获取嵌入式的Servlet容器
EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... var1);
}
☛ SpringBoot 再带了三种嵌入式的容器工厂,如下:
【3】EmbeddedServletContainer:嵌入式的容器,SpringBoot 为我们提供了三种不同的嵌入式容器,与工厂相互对应,如下:
【4】我们进入工厂类 TomcatEmbeddedServletContainerFactory发现,其实也是创建一个 Tomcat并配置其基本属性。
public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
//创建Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环境
File baseDir = this.baseDirectory != null?this.baseDirectory:this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入,并启动Tomcat,Tomcat.start()
return this.getTomcatEmbeddedServletContainer(tomcat);
}
【5】用户自定义的Servlet
容器配置类和SpringBoot
默认的ServerProperties
配置类,都实现了EmbeddedServletContainerCustomizer
接口。到底是怎么实现的哪?其实是SpringBoot
自动配置类中引入了后置处理器,如下:
//与用户自定义的Servlet容器实现的接口名很类似,有一定的命名规则。
embeddedServletContainerCustomizerBeanPostProcessor
☛ 进入后置处理器类中,重点看如下代码:
//初始化之前执行
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果当前初始化的是当前ConfigurableEmbeddedServletContainer类型的组件
if(bean instanceof ConfigurableEmbeddedServletContainer) {
this.postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer)bean);
}
return bean;
}
//上面的postProcessBeforeInitialization方法:
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
Iterator var2 = this.getCustomizers().iterator();
while(var2.hasNext()) {
//获取所有的定制器,调用每一个定制器的customize方法来给servlet属性赋值。
EmbeddedServletContainerCustomizer customizer = (EmbeddedServletContainerCustomizer)var2.next();
customizer.customize(bean);
}
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if(this.customizers == null) {
//this.beanFactory.xx表示从容器中获取XXCustomizer自定义类型的组件
this.customizers = new ArrayList(this.beanFactory.getBeansOfType(EmbeddedServletContainerCustomizer.class, false, false).values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
整理下步骤:
【1】SpringBoot
根据pom.xml
中导入的依赖,给容器中添加其对应的嵌入式的服务容器工厂类,例如默认的Tomcat
工厂:EmbeddedServletContainerFactory
【TomcatEmbeddedServletContainerFactory】
【2】给容器中某个组件要创建对象就会触发后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor
,只要是嵌入式的Servlet
容器工厂,后置处理器就会工作(默认的ServerProperties
也是实现了此类接口的,所以肯定存在相关配置类)
【3】后置处理器从容器中获取所有的EmbeddedServletContainerCustomizer
,调用定制器的定制方法。
五、嵌入式Servlet容器启动原理
根据上述的流程,我们要研究Servlet容器的启动原理。其实就是研究什么时候创建嵌入式的容器工厂和何时获取嵌入式的容器并启动Tomcat。获取嵌入式的Servlet容器工厂的过程(在new TomcatEmbeddedServletContainerFactory()时打一个断电,查看过程):
【1】SpringBoot 应用启动运行 run() 方法。
【2】this.refreshContext(context) 方法:用来初始化 IOC容器,既创建 IOC容器对象并初始化IOC容器中的每一个组件。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if(contextClass == null) {
try {
//判断是不是web环境,是Web环境引入AnnotationConfigEmbeddedWebApplicationContext,否则引入AnnotationConfigApplicationContext
contextClass = Class.forName(this.webEnvironment
?"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"
:"org.springframework.context.annotation.AnnotationConfigApplicationContext");
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
【3】this.refresh(context)
:刷新刚才创建好的IOC
容器。
【4】this.onRefresh()
:web
的IoC
容器重写了onRefresh()
方法。
protected void onRefresh() {
super.onRefresh();
try {
//重点是创建了嵌入式的Servlet容器
this.createEmbeddedServletContainer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start embedded container", var2);
}
}
【5】this.createEmbeddedServletContainer()
:web
的IOC
容器会创建嵌入式的Servlet
容器。
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = this.getServletContext();
if(localContainer == null && localServletContext == null) {
// 1、获取嵌入式的Servlet嵌入式的工厂
EmbeddedServletContainerFactory containerFactory = this.getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(
new ServletContextInitializer[]{this.getSelfInitializer()});
}
}
【6】获取嵌入式工厂后,便可从容器中获取EmbeddedServletContainerFactory
的组件tomcatEmbeddedServletContainerFactory
来创建Tomcat
对象,后置处理器就会触发获取所有的定制器来确定Servlet
容器的相关配置。
【7】通过嵌入式工厂获取嵌入式容器,如下:
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(
new ServletContextInitializer[]{this.getSelfInitializer()});
● 嵌入式的Servlet容器创建并启动对象:
public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
//创建对象
Tomcat tomcat = new Tomcat();
//启动对象
this.tomcat.start();
● 先启动嵌入式的Servlet容器,再将IOC容器中剩下没有创建的对象进行初始化,如下:
this.onRefresh();
//启动完嵌入式容器后,后续还有其他对象的初始化工作
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
六、使用外置的Servlet容器
嵌入式Servlet容器的缺点: 默认不支持JSP
、优化和定制比较复杂。外置Servlet
容器:安装外部的Tomcat
,步骤如下:
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
嵌入式Servlet容器的缺点: 默认不支持JSP
、优化和定制比较复杂。外置Servlet
容器:安装外部的Tomcat
,步骤如下:
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-jYTsQv1H-1715905968533)]
[外链图片转存中…(img-aElPrjOi-1715905968533)]
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!