Spring Boot自動配置源碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章