SpringBoot的启动类代码
@SpringBootApplicationpublic class MyblogApplication { public static void main(String[] args) { SpringApplication.run(MyblogApplication.class, args); }}
首先在启动类这段代码中,我们点进 @SpringBootApplication
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {}
除了元注解外,最核心的注解有:
@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan
首先我们来说 @SpringBootConfiguration @EnableAutoConfiguration这两个注解
@SpringBootConfiguration 点进去之后
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@documented@Configurationpublic @interface SpringBootConfiguration {}
除了元注解外,就只有一个 @Configuration,也就是说或这个注解相当于@Configuration,所以这两个注解作用是一样的,什么作用呢?就是能够注册一些额外的Bean,并且导入一些额外的配置。@Configuration还有一个作用就是把该类变成一个配置类,不需要进行额外的XML配置。所以@SpringBootConfiguration相当于@Configuration。
继续看 @EnableAutoConfiguration,官网说这个注解是 让Spring自动去进行一些配置
点进去看
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@documented@Inherited@AutoConfigurationPackage@import(AutoConfigurationimportSelector.class)public @interface EnableAutoConfiguration {}
可以看到它是由@AutoConfigurationPackage 和 @import(AutoConfigurationimportSelector.class) 组成,@AutoConfigurationPackage,作用是:让包中类以及子包中的类能够被自动扫描到Spring容器中。自动配置?它是如何配置的呢?和@import(AutoConfigurationimportSelector.class)相关
从@import(AutoConfigurationimportSelector.class) ,我们点进去看
public class AutoConfigurationimportSelector implements DeferredimportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectimports(Annotationmetadata annotationmetadata) {if (!isEnabled(annotationmetadata)) {return NO_importS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationmetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protected List
这个AutoConfigurationimportSelector 类中有一个方法 selectimports(Annotationmetadata annotationmetadata) ,它可以帮助扫描那些类自动去添加到程序当中。
里面还有一个方法是 getCandidateConfigurations(),它的作用是引入系统已经加载好的一些类,到底是哪些类呢?
"No auto configuration classes found in meta-INF/spring.factories、If you " + "are using a custom packaging, make sure that file is correct."
从这句话中,可以看到 这个类会去找一个目录为 meta-INF/spring.factories,也就是说它会帮你加载让你去使用,也就是在meta-INF/spring.factories目录装配的,它在哪呢?
点进去看
可以发现它搬我们配置了很多类的全路径,比如WebMvc
可以看到它已经帮我们引入了进来,拿几个过来看
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,
@EnableAutoConfiguration主要作用就是帮你自动去配置,但并不是所有都是创建好的,是根据你的程序去进行决定。那我们继续来看
接着看@ComponentScan注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
扫描包,放入Spring容器,它在SpringBoot做了什么呢?
它帮助我们做了一个排除策略,在这里它结合@SpringBootConfiguration去使用,为什么是排除?因为不可能一上来就全部加载,内存有限。
总结@SpringBootApplication:就是说 它已经把很多东西准备好,具体是否使用取决于我们的程序或者说配置,那我们用不用?继续看这一行代码
public class MyblogApplication { public static void main(String[] args) { SpringApplication.run(MyblogApplication.class, args); }}
来看run方法有没有用到哪些自动配置的东西,比如说内置tomcat,
我们点进去 SpringApplication
class SpringApplication{ public ConfigurableApplicationContext run(String..、args) { //计时器StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection
我们关注的就是 refreshContext(context); 刷新context,点进去看
private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}refresh((ApplicationContext) context);}
追踪 refresh((ApplicationContext) context);
public class SpringApplication { @Deprecatedprotected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);refresh((ConfigurableApplicationContext) applicationContext);} protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();}}
它调用了 refresh();点进去这个方法看
protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();}
追踪refresh()
来到了 ConfigurableApplicationContext 接口
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { void refresh() throws BeansException, IllegalStateException;}
实现该接口,重写该方法的几个类中,
点进去第一个看看
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}
发现,这个代码很熟悉,就是一个spring的bean的加载过程,解析SpringIOC加载过程的时候也含有这些方法,如果你看过Spring源码的话 ,应该知道这些方法都是做什么的。现在我们不关心其他的,我们来看一个方法叫做 onRefresh();方法
protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.}
找它的实现
我们既然要找Tomcat那就肯定跟web有关,我们可以看到有个ServletWebServerApplicationContext
@Overrideprotected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); }}
可以看到 有一个 createWebServer() 方法,它是用来创建web容器的,而tomcat不是web容器,那它是怎么创建的呢?继续看 该方法
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();}
中间一行中 factory.getWebServer(getSelfInitializer());他是通过工厂的方式创建的
追踪进去
@FunctionalInterfacepublic interface ServletWebServerFactory {WebServer getWebServer(ServletContextInitializer..、initializers);}
找它的实现类,要找tomcat如何创建,找tomcat相关的
进去之后,找到创建tomcat的方法
@Overridepublic WebServer getWebServer(ServletContextInitializer..、initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setbaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);connector.setThrowonFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}
该过程,就是我们要找的内置tomcat以及创建tomcat的过程。