Tomcat集成SpringBoot容器

SpringBoot中使用了内嵌的Tomcat服务器,在SSM中是通过外部配置来将项目部署的。如何在SpringBoot中集成Tomcat容器

模拟启动Tomcat

public class SpringApp {

    public static ApplicationContext run(Class clazz){
        WebApplicationContext context = new AnnotationConfigServletWebApplicationContext(clazz);
        startServer(context);
        return context;
    }

    private static void startServer(WebApplicationContext applicationContext) {
        Tomcat tomcat = new Tomcat();
        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");
        Connector connector = new Connector();
        connector.setPort(8500);
        Engine engine = new StandardEngine();
        engine.setDefaultHost("localhost");
        String contextPath = "";//项目的context-path
        Context context = new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());
        Host host = new StandardHost();
        host.setName("localhost");
        host.addChild(context);
        engine.addChild(host);
        service.setContainer(engine);

        //为tomcat容器添加servlet映射
        tomcat.addServlet(contextPath,"dispatcherServlet",new DispatcherServlet(applicationContext));
        context.addServletMappingDecoded( "/*","dispatcherServlet");
        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}

如果是以嵌入式servlet容器,像SpringBoot的这种方式在启动服务器之前,需要为服务器添加Servlet,且指定这个Servlet能处理哪个请求,来代替web.xml的方式。在创建好整个容器以后,交给dispatcherServlet。这样整个web应用就创建好了。

主启动类

@SpringApp
public class App {

    public static void main(String[] args) {
        com.tomcat.run.SpringApp.run(App.class);
    }

}

在SpringBoot的启动过程中,如何根据引入的jar包启动不同的服务器?这就和SpringBoot的自动配置有关了,在SpringBoot启动过程中,会扫描所有classpath下的jar包的META-INF/spring.factories,其中这个文件通过org.springframework.boot.autoconfigure.EnableAutoConfiguration指定了一个配置类,但是这些配置类不是一般的配置类,是我们常说的自动配置类,通过加载这些类,往往又会加载这些类绑定的Properties配置文件,然后创建好Bean,返回到容器中。

在这里插入图片描述
但是呢,这些自动配置类并不是都会生效的,通常会搭配大量的@Condition注解使用。例如ServletWebServerFactoryAutoConfiguration

@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})
public class ServletWebServerFactoryAutoConfiguration {}

@ConditionalOnClass({ServletRequest.class}),当你的类路径下有ServletRequest这个类时,这个自动配置类才会生效,类路径是什么?其实就是判断当前项目是否引入了ServletRequest的jar包。而ServletRequest是存在于servlet包下的,也就是说需要引入servlet的相关依赖,这个自动配置类才会生效。点开jetty或undertow的依赖,发现这两个依赖中就引入了servlet的依赖。

在这里插入图片描述

也就是说当引入了jetty或undertow或tomcat的依赖时,这个自动配置类就生效了。

一旦自动配置类生效,就会导入

@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})

而在导入这些类时,依然有条件判断

   @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedUndertow {
        EmbeddedUndertow() {
        }

        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers, ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.getDeploymentInfoCustomizers().addAll((Collection)deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getBuilderCustomizers().addAll((Collection)builderCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

        @Bean
        UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
            return new UndertowServletWebServerFactoryCustomizer(serverProperties);
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedJetty {
        EmbeddedJetty() {
        }

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll((Collection)serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedTomcat {
        EmbeddedTomcat() {
        }

        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }

只有引入每个servlet容器自己的依赖时,相对应的配置类才会生效,例如拿jetty来说

 @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})

类路径下有以上四个类时才会生效,而Loader这个类是属于jetty包的,所以只有引入了jetty相关依赖这个配置类就生效的。一旦这个配置类生效,就会给容器中配置一个

      @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll((Collection)serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

Spring容器就可以获取到ServletWebServerFactory,从而启动服务器,将dispatherServlet交给服务器处理请求。

  • springbootjar包问题

在看springboot源码时,看到了大量的不属于springboot包的类,例如Undertow等,为何在启动项目时不会报错?因为引入的是已经编译好的class文件,而不是java文件,在启动项目时,只需将项目中的java文件编译即可,对于引入的第三方jar包,通常都是通过 -classpath参数指定jar包在磁盘上的路径。

  • springboot依赖问题

springboot在编写时,依赖了大量的其他第三方框架,但是在编译时,将这些第三方框架的依赖排除了,只保留了springboot的源码的class文件,而在编译后,class文件就是一些静态文件了,并不会再被编译一遍。

  • 如何判断类路径下有没有某个类?

当springboot加载到某个配置类时,如果这个配置类使用了条件注解,使用try … catch(ClassNotFoundException e);的方式来判断类路径下是否存在这个类,从而觉得该配置类是否生效。

  • asm解析class文件

在springboot中,默认是从主启动类所在的包开始扫描,但是大部分文件是没有标注@Component注解的,更多的还是普通的文件,如果是挨个用Class.forName的方式拿到一个class对象,然后再去判断一个class对象是否标注了@Component注解,那么这种方式无疑会造成大量的class被加载到内存中,造成内存资源浪费,所以Spring采取asm技术直接去取编译好的class文件,判断class文件中是否标注了相关注解,再来决定是否加载这个类。这样就不用所有类都加载一遍,节省了内存空间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值