SpringBoot系列十四:嵌入式Servlet容器 您所在的位置:网站首页 servlet容器和spring容器tomcat SpringBoot系列十四:嵌入式Servlet容器

SpringBoot系列十四:嵌入式Servlet容器

2024-01-25 02:59| 来源: 网络整理| 查看: 265

一、配置嵌入式Servlet容器 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器; 在这里插入图片描述 1、定制和修改Servlet容器的相关配置 (1)、修改和server有关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】)。

server.port=8081 server.context-path=/crud server.tomcat.uri-encoding=UTF-8 //通用的Servlet容器设置 server.xxx //Tomcat的设置 server.tomcat.xxx

(2)、编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器;来修改Servlet容器的配置。

@Bean //一定要将这个定制器加入到容器中 public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new EmbeddedServletContainerCustomizer() { //定制嵌入式的Servlet容器相关的规则 @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setPort(8083); } } }

2、注册Servlet三大组件【Servlet、Filter、Listener】  由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。 注册三大组件的方式: (1)、ServletRegistrationBean

public class MyServlet extends HttpServlet { //处理get请求 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("Hello MyServlet"); } } //注册三大组件 @Bean public ServletRegistrationBean myServlet(){ ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet"); return registrationBean; }

(2)、FilterRegistrationBean

public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter process..."); chain.doFilter(request,response); } @Override public void destroy() { } } @Bean public FilterRegistrationBean myFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new MyFilter()); registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet")); return registrationBean; }

(3)、ServletListenerRegistrationBean

public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("contextInitialized...web应用启动"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed...当前web项目销毁"); } } @Bean public ServletListenerRegistrationBean myListener(){ ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean(new MyListener()); return registrationBean; }

SpringBoot 整合 SpringMVC 的时候,自动的注册了 SpringMVC 的前端控制DIspatcherServlet。 DispatcherServletAutoConfiguration中:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public ServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet, this.serverProperties.getServletMapping()); //默认拦截: / 所有请求;包静态资源,但是不拦截jsp请求; /* 会拦截jsp //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径 registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; }

二、配置其他嵌入式Servlet容器 在这里插入图片描述 默认支持: 1、Tomcat(默认使用):

org.springframework.boot spring-boot-starter-web

2、Jetty(长连接):

org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty org.springframework.boot

3、Undertow(不支持JSP):

org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-undertow org.springframework.boot

三、嵌入式Servlet容器自动配置原理 EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(BeanPostProcessorsRegistrar.class) //导入BeanPostProcessorsRegistrar:Spring注解版;给容器中导入一些组件 //导入了EmbeddedServletContainerCustomizerBeanPostProcessor: //后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作 public class EmbeddedServletContainerAutoConfiguration { @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class }) //判断当前是否引入了Tomcat依赖; @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) //判断当前容器没有用户自己定义EmbeddedServletContainerFactory:嵌入式的Servlet容器工厂;作用:创建嵌入式的Servlet容器 public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { return new JettyEmbeddedServletContainerFactory(); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { return new UndertowEmbeddedServletContainerFactory(); } } }

1、EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)

public interface EmbeddedServletContainerFactory { //获取嵌入式的Servlet容器 EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers); }

在这里插入图片描述 2、EmbeddedServletContainer:(嵌入式的Servlet容器) 在这里插入图片描述 3、以 TomcatEmbeddedServletContainerFactory 为例

@Override public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { //创建一个Tomcat Tomcat tomcat = new Tomcat(); //配置Tomcat的基本环节 File baseDir = (this.baseDirectory != null ? this.baseDirectory : createTempDir("tomcat")); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); //将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器 return getTomcatEmbeddedServletContainer(tomcat); }

4、嵌入式容器的配置修改生效原理

ServerProperties、EmbeddedServletContainerCustomizer

EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器的配置。ServerProperties也是定制器。 5、容器中导入了 EmbeddedServletContainerCustomizerBeanPostProcessor

//初始化之前 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //如果当前初始化的是一个ConfigurableEmbeddedServletContainer类型的组件 if (bean instanceof ConfigurableEmbeddedServletContainer) { // postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean); } return bean; } private void postProcessBeforeInitialization( ConfigurableEmbeddedServletContainer bean) { //获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值; for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) { customizer.customize(bean); } } private Collection getCustomizers() { if (this.customizers == null) { // Look up does not include the parent context this.customizers = new ArrayList( this.beanFactory //从容器中获取所有这葛类型的组件:EmbeddedServletContainerCustomizer //定制Servlet容器,给容器中可以添加一个EmbeddedServletContainerCustomizer类型的组件 .getBeansOfType(EmbeddedServletContainerCustomizer.class, false, false) .values()); Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers; }

步骤:  1、SpringBoot根据导入的依赖情况,给容器中添加相应的EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】  2、容器中某个组件要创建对象就会惊动后置处理器;只要是嵌入式的Servlet容器工厂,后置处理器就工作;EmbeddedServletContainerCustomizerBeanPostProcessor;  3、后置处理器,从容器中获取所有的 EmbeddedServletContainerCustomizer,调用定制器的定制方法。

四、嵌入式Servlet容器启动原理 获取嵌入式的Servlet容器工厂: 1、SpringBoot应用启动运行run方法。 2、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建 AnnotationConfigEmbeddedWebApplicationContext,否则:AnnotationConfigApplicationContext 3、refresh(context);刷新刚才创建好的ioc容器。

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

4、 onRefresh(); web的ioc容器重写了onRefresh方法。 5、webioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer()。 6、获取嵌入式的Servlet容器工厂:

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory()

 从ioc容器中获取EmbeddedServletContainerFactory 组件;TomcatEmbeddedServletContainerFactory 创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置。 7、使用容器工厂获取嵌入式的Servlet容器:

this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer())

8、嵌入式的Servlet容器创建对象并启动Servlet容器。  先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;IOC容器启动创建嵌入式的Servlet容器。

五、使用外部的Servlet容器  嵌入式Servlet容器:应用打成可执行的jar。优点:简单、便携;缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂【EmbeddedServletContainerFactory】)。  外置的Servlet容器:外面安装Tomcat,然后将应用以war包的方式打包。

步骤: 1、必须创建一个war项目;(利用idea创建好目录结构) 在这里插入图片描述 2、将嵌入式的 Tomcat 指定为 provided

org.springframework.boot spring-boot-starter-tomcat provided

3、必须编写一个 SpringBootServletInitializer 的子类,并调用configure方法。

public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //传入SpringBoot应用的主程序 return application.sources(SpringBoot04WebJspApplication.class); } }

4、启动服务器就可以使用。

六、外部的Servlet容器启动原理 jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器; war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器; servlet3.0规则:  1、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面 ServletContainerInitializer 实例。  2、ServletContainerInitializer 的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是 ServletContainerInitializer 的实现类的全类名。  3、可以使用 @HandlesTypes,在应用启动的时候加载我们感兴趣的类。

流程: 1、启动Tomcat。 2、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer: Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer 3、SpringServletContainerInitializer 将 @HandlesTypes(WebApplicationInitializer.class) 标注的所有这个类型的类都传入到onStartup方法的Set



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有