同上題記。總結:快速開始,方便搭建,開發web時並不須要Tomcat或者Jetty,甚至連插件都不用(由於自帶Tomcat或自配置成Jetty)。java
一個框架除了知道他的優勢,確定要知道他的缺點。react
SpringBoot 缺點以下(暫時發現):web
使用 @Import 引入配置Bean,有三種方式:redis
1) 直接引入 configuration.classspring
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { }
DelegatingWebMvcConfiguration 就是配置的Configuration類apache
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { //... }
2) 引入 ImportSelector 接口的實現tomcat
public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); }
只要實現該接口,返回須要加載的 Configuration 類名字符串便可,見例子:websocket
static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } }
3) 引入 ImportBeanDefinitionRegistrar 接口的實現類session
public interface ImportBeanDefinitionRegistrar { void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2); }
很好理解,手動將須要注入的bean的definition放入BeanDefinitionRegistry 中app
全部的開始都是源於 DataSourceAutoConfiguration 這個類,就是說若是你想自動化生成 DataSrouce 你只要在你的配置類引入該類:
@Configuration @Import({DataSourceAutoConfiguration.class}) public class Application { // ... }
DataSourceAutoConfiguration 中引入了其餘配置類,而且使用 @Bean 來生成須要的組件:
@Configuration @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}) @EnableConfigurationProperties({DataSourceProperties.class}) @Import({Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class}) public class DataSourceAutoConfiguration { }
引入了 Registrar 和 DataSourcePoolMetadataProvidersCofniguration , 這裏以 Registrar 爲例介紹:
static class Registrar implements ImportBeanDefinitionRegistrar { private static final String BEAN_NAME = "dataSourceInitializerPostProcessor"; Registrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if(!registry.containsBeanDefinition("dataSourceInitializerPostProcessor")) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class); beanDefinition.setRole(2); beanDefinition.setSynthetic(true); registry.registerBeanDefinition("dataSourceInitializerPostProcessor", beanDefinition); } } }
這是符合上面的第3種方式,目的就是註冊一個PostProcessor 來處理註冊進來的 DataSource, 凡有DataSource實例,就實例化 DataSourceInitializer (用於預跑一些初始化的SQL腳步)。
重點是下面
@Configuration @Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class}) @ConditionalOnMissingBean({DataSource.class, XADataSource.class}) @Import({Tomcat.class, Hikari.class, Dbcp.class, Dbcp2.class}) protected static class PooledDataSourceConfiguration { protected PooledDataSourceConfiguration() { } }
引入三個配置類,Tomcat、Hikari、Dbcp、Dbcp2,處理邏輯基本一致,都是判斷是否有對於的類和配置,以Dbcp爲例:
@ConditionalOnClass({org.apache.commons.dbcp.BasicDataSource.class}) @ConditionalOnProperty( name = {"spring.datasource.type"}, havingValue = "org.apache.commons.dbcp.BasicDataSource", matchIfMissing = true ) static class Dbcp extends DataSourceConfiguration { Dbcp() { } @Bean @ConfigurationProperties("spring.datasource.dbcp") public org.apache.commons.dbcp.BasicDataSource dataSource(DataSourceProperties properties) { org.apache.commons.dbcp.BasicDataSource dataSource = (org.apache.commons.dbcp.BasicDataSource)this.createDataSource(properties, org.apache.commons.dbcp.BasicDataSource.class); DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl()); String validationQuery = databaseDriver.getValidationQuery(); if(validationQuery != null) { dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(validationQuery); } return dataSource; } }
最終 @Bean 生成 dataSource 實例。
這個祕密就隱藏在 @SpringBootApplication 中
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} )} ) public @interface SpringBootApplication
從源碼中可見,@SpringBootApplication = @EnableAutoConfiguration + @SpringBootConfiguration + @ComponentScan
很明顯,EnableAutoConfiguration 是自動化配置的關鍵
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration
EnableAutoConfigurationImportSelector implements DeferredImportSelector
符合上面第3中引入方式,EnableAutoConfigurationImportSelector 的主要功能就是將spring.factories 配置的config獲取,返回出來
spring.factories 中都是些什麼:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
前面分析的 DataSourceAutoConfiguration 就在上面,因而可知,這些AutoConfig都被自動引入。
這麼多配置可能因爲的就一半都不到,因此若是優化啓動速度,那麼就 手動@Import 便可,不過有點麻煩。
public static void main(String[] args) throws LifecycleException, InterruptedException { SpringApplication.run(Application.class, args); }
一切的開始都是從這段代碼,因此 SpringApplication.run 是分析入口,最終追蹤到源碼:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return (new SpringApplication(sources)).run(args); }
實例化,而後run。
實例化中,還調用判斷了是否Web環境,原理是判斷是否存在兩個class:
private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"}; private boolean deduceWebEnvironment() { String[] var1 = WEB_ENVIRONMENT_CLASSES; int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { String className = var1[var3]; if(!ClassUtils.isPresent(className, (ClassLoader)null)) { return false; } } return true; }
run的源碼:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.started(); try { DefaultApplicationArguments ex = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex); // env配置處理, application.properties等 Banner printedBanner = this.printBanner(environment); // 打印 Banner context = this.createApplicationContext(); // 關鍵(1) this.prepareContext(context, environment, listeners, ex, printedBanner);// 一些賦值,及調用initer this.refreshContext(context); // refresh ctx, 視爲啓動ctx 關鍵(2) this.afterRefresh(context, ex); listeners.finished(context, (Throwable)null); stopWatch.stop(); if(this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } return context; } catch (Throwable var8) { this.handleRunFailure(context, listeners, var8); throw new IllegalStateException(var8); } }
分析關鍵(1)的源碼
protected ConfigurableApplicationContext createApplicationContext() { Class contextClass = this.applicationContextClass; if(contextClass == null) { try { contextClass = Class.forName(this.webEnvironment?"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext":"org.springframework.context.annotation.AnnotationConfigApplicationContext"); } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass); }
很簡單,判斷是否 web環境,若是是就是要使用 AnnotationConfigEmbeddedWebApplicationContext 這個Ctx類實例化Context。
關鍵(2)的源碼
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext)applicationContext).refresh(); }
那麼 AnnotationConfigEmbeddedWebApplicationContext 的 onRefresh 和普通的 context的區別在於:
protected void onRefresh() { super.onRefresh(); try { this.createEmbeddedServletContainer(); // 建立自帶的web容器 } catch (Throwable var2) { throw new ApplicationContextException("Unable to start embedded container", var2); } } private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = this.getServletContext(); if(localContainer == null && localServletContext == null) { EmbeddedServletContainerFactory ex = this.getEmbeddedServletContainerFactory(); // 獲取ContainerFactory this.embeddedServletContainer = ex.getEmbeddedServletContainer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if(localServletContext != null) { //... } this.initPropertySources(); }
那麼 ContainerFactory 確定是自動引入的配置咯!
EmbeddedServletContainerAutoConfiguration 中:
@Configuration @ConditionalOnClass({Servlet.class, Tomcat.class}) @ConditionalOnMissingBean( value = {EmbeddedServletContainerFactory.class}, search = SearchStrategy.CURRENT ) public static class EmbeddedTomcat { public EmbeddedTomcat() { } @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } }
固然,該類中還有其餘web容器的引入配置,形同上面 Tomcat的,邏輯也相似就是判斷是否存在一些關鍵類:
@ConditionalOnClass({Servlet.class, Tomcat.class})
其餘的,refresh 過程跟通常的spring context一致,不做分析。
SpringBoot 遠不止如此, 且學且記錄吧!