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

时间: 2024-09-14 11:05:32

Spring Boot自动配置源码的相关文章

Spring Boot自动配置

在上篇文章如何运行Spring Boot应用中,已经熟悉了如何通过maven或者gradle创建一个Spring Boot应用,这篇文章主要学习Spring Boot的自动配置,包括注解的使用以及一些配置约束等等. 关于Spring Boot的特性介绍,可以参考Spring Boot特性. 主应用类 在Spring Boot应用中,我们通常将主应用类放置于应用的根包中,例如,com.javachen.example.主应用类有main方法,并且使用了@EnableAutoConfiguratio

求一套简单的spring restful+cxf的源码

问题描述 求一套简单的spring restful+cxf的源码 json格式发送接收,不需要数据访问层,只要http能访问就行 解决方案 此条提问取消,另外有个悬赏高的请大家移步那里解答http://ask.csdn.net/questions/186986 解决方案二: 此条提问取消,另外有个悬赏高的请大家移步那里解答http://ask.csdn.net/questions/186986

spring cloud eureka部分源码分析及微服务管理功能

eureka原生的管理页面只有查看服务节点和一些信息,没有动态启用停用服务节点的功能 一. EurekaClient获取所有注册的服务 eureka客户端会加载一个定时任务去获取注册中心的服务,任务的配置在:com.netflix.discovery.DiscoveryClient,刷新的线程是:CacheRefreshThread.获取的注册中心服务的时候,会把所有服务都拉取下来,但是默认会过滤掉状态不是UP的服务.获取服务的具体代码在:DiscoveryClient.getAndStoreF

Spring boot将配置属性注入到bean 专题

@ConfigurationProperties和@value都是将外部属性注入到对象 @ConfigurationProperties很方便使用. 比用@value注解好吗? 在特定的方案中是的,这只是一个选择问题 @EnableConfigurationProperties //开启属性注入,有此注解就可以通过@autowired注入, 是配合@ConfigurationProperties使用的.如果没有@EnableConfigurationProperties,则使用@Configur

Spring Boot 自动加载指定包下的拦截器

Spring Boot 在我们需要对程序添加拦截器需要使用 WebMvcConfigurerAdapter 中的 addInterceptors方法去注册拦截器,这样我们如果在程序里面有多个拦截或者我们在项目结构为maven 关系存在父子级关系时候.WebMvcConfigurerAdapter 类我们写在父类 这样就无法获取到子类存在哪些拦截器了.这个我们就需要在父级和子级都写对应的 WebMvcConfigurerAdapter 方法.这里我根据前面我的一篇博客为 获取某个包下面的所有类来实

Spring Boot下配置MyBatis多数据源

Spring Boot最大的特点是简化开发,因此使用Java Config实现去xml配置,本文将使用这种方式完成对阿里云ADS和RDS两个产品的多数据源配置.同时,会用到阿里巴巴的开源数据源Druid. 依赖 因为ADS和RDS都兼容mysql的JDBC实现,因此工程只需要依赖mysql的driver. Spring Boot易用性的特点是通过Starter实现的,我们只需依赖mybatis的spring-boot-starter即可. Druid是数据源. <dependency> <

WinForm天猫双11自动抢红包源码分享_C#教程

本文实例为大家分享了抢红包源码,供大家参考,具体内容如下 1. 正确获取红包流程 2. 软件介绍 2.1 效果图: 2.2 功能介绍 2.2.1 账号登录 页面开始时,会载入这个网站:https://login.taobao.com/member/login.jhtml?redirect_url=http%3A%2F%2F1111.tmall.com%2F 登录成功后,会自动跳转到1111.tmall.com活动页面. 2.2.2 [去抽奖]按钮 这按钮是手动跳转到1111.tmall.com页

有木有Struts2+hibernate+spring+mysql的项目源码?求!!!

问题描述 如题!!!要求项目能运行无误...数据库有创建数据库文件...如果谁有请联系我..QQ:506846716:先谢谢了!

spring boot druid mybatis 多数据源 配置

spring boot 在配置时做了很多简化配置的设置,但是简化的配置往往已牺牲一定的定制化,比如在数据源的配置时,spring boot 只提供4种数据库连接池的配置,其中并不支持常用的druid 阅读spring boot DataSourceBuilder 的源码可以发现 spring boot 提供的4种数据源类型并不是我们想要的 private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { "org.apac