同上题记。总结:快速开始,方便搭建,开发web时并不需要Tomcat或者Jetty,甚至连插件都不用(因为自带Tomcat或自配置成Jetty)。
一个框架除了知道他的优点,肯定要知道他的缺点。
SpringBoot 缺点如下(暂时发现):
使用 @Import 引入配置Bean,有三种方式:
1) 直接引入 configuration.class
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
DelegatingWebMvcConfiguration 就是配置的Configuration类
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
//...
}
2) 引入 ImportSelector 接口的实现
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
只要实现该接口,返回需要加载的 Configuration 类名字符串即可,见例子:
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 接口的实现类
public interface ImportBeanDefinitionRegistrar {
void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2);
}
很好理解,手动将需要注入的bean的definition放入BeanDefinitionRegistry 中
所有的开始都是源于 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 远不止如此, 且学且记录吧!