同上题记。总结:快速开始,方便搭建,开发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 远不止如此, 且学且记录吧!