Spring Boot自动配置源码
2015-11-27 12:00
357 查看
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.htmlhttp://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-troubleshoot-auto-configuration
http://blog.csdn.net/liaokailin/article/category/5765237
相关文章推荐
- java.lang.NoClassDefFoundError: javax/mail/Address解决方法
- Java编写掷骰子游戏
- 关于Java JDK中 URLDecoder.decode 方法
- 关于Android工程从eclipse迁移至android studio的过程
- java中的数学计算函数
- Java中回调函数个人理解
- java实现走迷宫算法
- LeetCode 172 : Factorial Trailing Zeroes (Java)
- Java NIO (九) ServerSocketChannel
- java处理数字格式的几种方式
- 关于Spring事务<tx:annotation-driven/>的理解(Controller可以使用@Transactional)
- 【leetcode】【73】Set Matrix Zeroes
- Java正则表达式匹配电话格式
- java.net.BindException: Address already in use: JVM_Bind:8080
- JAVA基础代码分享--求圆面积
- java 的文件操作方法记录
- Eclipse - subclipse svn
- java中i = i++问题
- 使Eclipse下支持编写HTML/JS/CSS/JSP页面的自动提示
- JAVA基础代码分享--DVD管理