SpringBoot系列十四:嵌入式Servlet容器 | 您所在的位置:网站首页 › servlet容器和spring容器tomcat › SpringBoot系列十四:嵌入式Servlet容器 |
一、配置嵌入式Servlet容器 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器; (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容器 2、Jetty(长连接): org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty org.springframework.boot3、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); }
4、嵌入式容器的配置修改生效原理 ServerProperties、EmbeddedServletContainerCustomizerEmbeddedServletContainerCustomizer:定制器帮我们修改了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创建好目录结构) 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 实验室设备网 版权所有 |