Spring Boot自动配置源码

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

SpringBoot初始化上下文环境

SpringBoot会从META-INF/spring.factories文件中加载Initializers,Auto Configure

Initializers用于加载配置(Environment)
Auto Configure用于自动配置类

如果是web类型的工程,SpringBoot会创建EmbeddedWebApplicationContext上下文 -> 使用createEmbeddedServletContainer方法创建内嵌的servlet服务容器( 由工厂类EmbeddedServletContainerFactory -> getEmbeddedServletContainer()创建Servlet容器, ->initialize() 同时进行容器初始化及运行 )

容器类EmbeddedServletContainer控制着内嵌服务器的生命周期以及配置.

加载AutoConfiguration

AutoConfiguration初始化对应的实例

我们来看下Mongo的AutoConfiguration,如下:

    @Configuration
    @ConditionalOnClass(MongoClient.class)
    @EnableConfigurationProperties(MongoProperties.class)
    @ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDbFactory")
    public class MongoAutoConfiguration {

        @Autowired
        private MongoProperties properties;

        @Autowired(required = false)
        private MongoClientOptions options;

        @Autowired
        private Environment environment;

        private MongoClient mongo;

        @PreDestroy
        public void close() {
            if (this.mongo != null) {
                this.mongo.close();
            }
        }

        @Bean
        @ConditionalOnMissingBean
        public MongoClient mongo() throws UnknownHostException {
            this.mongo = this.properties.createMongoClient(this.options, this.environment);
            return this.mongo;
        }

    }

Mongo的AutoConfiguration将会在用户引入Mongo相关包时,并且没有自定义MongoDbFactory时被激活,同时配置文件(application.properties之类的)将注入到MongoProperties中.MongoProperties类由@ConfigurationProperties标注:

    @ConfigurationProperties(prefix = "spring.data.mongodb")
    public class MongoProperties {

        /** * Default port used when the configured port is {@code null}. */
        public static final int DEFAULT_PORT = 27017;

        /** * Mongo server host. */
        private String host;
        //...省略...
        //根据配置创建MongoClient
            public MongoClient createMongoClient(MongoClientOptions options,
                Environment environment) throws UnknownHostException {
            try {
                if (hasCustomAddress() || hasCustomCredentials()) {
                    if (options == null) {
                        options = MongoClientOptions.builder().build();
                    }
                    List<MongoCredential> credentials = null;
                    if (hasCustomCredentials()) {
                        String database = this.authenticationDatabase == null
                                ? getMongoClientDatabase() : this.authenticationDatabase;
                        credentials = Arrays.asList(MongoCredential.createMongoCRCredential(
                                this.username, database, this.password));
                    }
                    String host = this.host == null ? "localhost" : this.host;
                    int port = determinePort(environment);
                    return new MongoClient(Arrays.asList(new ServerAddress(host, port)),
                            credentials, options);
                }
                // The options and credentials are in the URI
                return new MongoClient(new MongoClientURI(this.uri, builder(options)));
            }
            finally {
                clearPassword();
            }
        }

可以看到MongoClient最终由MongoAutoConfiguration调用MongoProperties的createMongoClient()方法创建.通过标注@Bean将MongoClient发布到Spring容器中.

如果用户已经用@Bean自定义了一个MongoClient,那么Mongo AutoConfig就不会做去初始化MongoClient,配置文件中的配置也就不生效了.

Embedded Tomcat初始化过程

内嵌式Tomcat通过Tomcat类创建并配置的,我们可以看看Spring是如何包装的,使用工厂类TomcatEmbeddedServletContainerFactory -> getEmbeddedServletContainer() :

    @Override
        public EmbeddedServletContainer getEmbeddedServletContainer(
                ServletContextInitializer... initializers) {
            //通常创建内嵌Tomcat时的流程,使用Tomcat类
            Tomcat tomcat = new 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);
            tomcat.getEngine().setBackgroundProcessorDelay(-1);
            for (Connector additionalConnector : this.additionalTomcatConnectors) {
                tomcat.getService().addConnector(additionalConnector);
            }
            prepareContext(tomcat.getHost(), initializers);
            return getTomcatEmbeddedServletContainer(tomcat);
        }

Spring使用EmbeddedServletContainer包装了Tomcat,封装了内嵌容器的生命周期.

所有用户通过工厂类EmbeddedServletContainerFactory配置容器,例如:application.properties中的server.port=8099,
带有@ConfigurationProperties注解的ServerProperties,自动注入了application.properties中关于server.*的配置.

由于ServerProperties实现了EmbeddedServletContainerCustomizer接口,ServerProperties通过该接口的方法,对EmbeddedServletContainerFactory进行配置:

            if (getPort() != null) {
                container.setPort(getPort());
            }
            if (getAddress() != null) {
                container.setAddress(getAddress());
            }
            if (getContextPath() != null) {
                container.setContextPath(getContextPath());
            }
            if (getDisplayName() != null) {
                container.setDisplayName(getDisplayName());
            }
            if (getSession().getTimeout() != null) {
                container.setSessionTimeout(getSession().getTimeout());
            }
            if (getSsl() != null) {
                container.setSsl(getSsl());
            }
            if (getJspServlet() != null) {
                container.setJspServlet(getJspServlet());
            }
            if (getCompression() != null) {
                container.setCompression(getCompression());
            }

除了配置文件方式,我们还可以:

        @Bean
        public EmbeddedServletContainerFactory servletContainer() {
            TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
            factory.addConnectorCustomizers(connector -> {
                Http11NioProtocol protocol = ((Http11NioProtocol) connector.getProtocolHandler());
                connector.setPort(8989);
                protocol.setConnectionTimeout(10000);
            });
            return factory;
        }

直接自己创建工厂类,并实现addConnectorCustomizers接口中的customizer.这部分会覆盖配置文件的配置,在TomcatEmbeddedServletContainerFactory的getEmbeddedServletContainer() -> customizeConnector() 中会调用我们自定义的customizer:

            for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
                customizer.customize(connector);
            }

如果用户没有自定义EmbeddedServletContainerFactory的话,EmbeddedServletContainerAutoConfiguration就默认初始化一个.

        @Configuration
        @ConditionalOnClass({ Servlet.class, Tomcat.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedTomcat {

            @Bean
            public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
                return new TomcatEmbeddedServletContainerFactory();
            }

        }

Reference

http://geowarin.github.io/understanding-spring-boot.html
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-troubleshoot-auto-configuration
http://blog.csdn.net/liaokailin/article/category/5765237


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Boot自动配置源码

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏