本文基于Spring Boot1.4.x所写,最新源代码可到https://github.com/spring-projects/spring-boot上去下载。Spring Boot伴随Spring4.x发布,可以说是Java Web开发近几年来最有影响力的项目之一,极大的提高了开发效率。据我说知,很多公司新起的项目当中都用起了SpringBoot框架。
SpringBoot项目启动非常简单,调用SpringApplication类的静态run方法,并将Application类对象和main方法的参数args作为参数传递进去。在这个静态方法中,创建SpringApplication对象,SpringApplication对象的构造函数中调用initialize方法,初始化SpringApplication对象的成员变量sources,webEnvironment,initializers,listeners,mainApplicationClass。
1、sources的赋值比较简单,就是我们传给SpringApplication.run方法的参数。
2、webEnvironment是一个boolean,该成员变量用来表示当前应用程序是不是一个Web应用程序。那么怎么决定当前应用程序是否Web应用程序呢,是通过在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES这个数组中所包含的类(javax.servlet.Servlet、ConfigurableWebApplicationContext),如果存在那么当前程序即是一个Web应用程序,反之则不然。
3、initializers成员变量,是一个ApplicationContextInitializer类型对象的集合。ApplicationContextInitializer是一个用来初始化ApplicationContext的接口,代码中调用getSpringFactoriesInstances从一个名字叫spring.factories的资源文件中读取对应配置。
4、listeners成员变量,是一个ApplicationListener类型对象的集合。获取的方式和initializers一样。
5、mainApplicationClass即是我们自己编写的Application类。通过获取当前调用栈,找到入口方法main所在的类。
上述创建好SpringApplication对象后并调用该对象的run方法,当中做了很多启动配置,代码如下所示:
1、getRunListeners方法初始化SpringApplicationRunListeners,对象中包含SpringApplicationRunListener的集合,集合中的数据是调用getSpringFactoriesInstances从spring.factories中加载。默认的会加载EventPublishingRunListener,这个RunListener是在SpringApplication对象的run方法执行到不同的阶段时,发布相应的event给SpringApplication对象的成员变量listeners中记录的事件监听器。
2、createApplicationContext创建ApplicationContext,如果是服务端程序则是AnnotationConfigEmbeddedWebApplicationContext。默认的BeanFactory为DefaultListableBeanFactory同时实现了ResourceLoader接口。同时会创建两个对象AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。
创建AnnotatedBeanDefinitionReader的时候会调用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry),该方法会向BeanFactory注册一些注解实现类,如下所示:
ConfigurationClassPostProcessor:@Configuration
AutowiredAnnotationBeanPostProcessor:@Autowired、@Value、@Inject、@Lookup
RequiredAnnotationBeanPostProcessor:@Required
CommonAnnotationBeanPostProcessor:@PostConstruct、@PreDestroy、@Resource、@WebServiceRef、@EJB
PersistenceAnnotationBeanPostProcessor:@PersistenceContext、@PersistenceUnit
EventListenerMethodProcessor:@EventListener
DefaultEventListenerFactory:@EventListener
AnnotationAwareOrderComparator:@Order
ContextAnnotationAutowireCandidateResolver:@Lazy、@Qualifier、@Value
创建ClassPathBeanDefinitionScanner的时候会调用registerDefaultFilters。registerDefaultFilters用于初始化includeFilters筛选@ManagedBean、@Named、@Component(此注解包含如:@Service、@Controller等)
3、prepareContext调用applyInitializers方法设置上下文,调用load方法创建BeanDefinitionLoader并调用其load把用户的Application定义注册进容器,用户自定的Application会包装为BeanDefinitionHolder,后续流程会用的到
4、refreshContext,它最后调用的是AbstractApplicationContext.refresh(),该方法正是Spring启动的核心流程。由此也可以看出SpringBoot并没有发明什么新的技术,只是把已有技术做整合
5、afterRefresh,容器初始化完成后会回调用户定义的ApplicationRunner及CommandLineRunner
下面将是本文的重点分析,其中会略过一些代码步骤只分析重要的地方,对着代码相互印证才会真正明白,不然只是知道而已。
AbstractApplicationContext.refresh负责整个Spring的加载、配置、初始化等工作,本文也会这个为节点做分析,代码如下所示:
invokeBeanFactoryPostProcessors
该方法主要代码为PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,当中会多次调用invokeBeanDefinitionRegistryPostProcessors及invokeBeanFactoryPostProcessors方法,其中主要的调用如下:
invokeBeanDefinitionRegistryPostProcessors最重要的是调用上面注册的ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry。注册ImportAwareBeanPostProcessor(@ImportRegistry)及EnhancedConfigurationBeanPostProcessor类。进入processConfigBeanDefinitions方法,该方法重点从创建ConfigurationClassParser开始。
应用程序首先从用户自定义的Application开始解析,调用parser.parse(candidates),该方法包含很多内容,主要是解析@ComponentScans、@Import、@ImportResource、@Bean、@SpringBootApplication等。
@ComponentScans主要是使用ComponentScanAnnotationParser调用ClassPathBeanDefinitionScanner.doScan来扫描项目及Jar中的符合要求的类转换成BeanDefinition注册到容器当中。实现方式是基于ResourceLoader来扫描项目及jar中的候选class路径列表,ResourceLoader则是基于ClassLoader来做的,扫描出的列表进行过滤并抽象成Resource。然后循环调用MetadataReaderFactory.getMetadataReader来解析Resource对应的class,此处解析class并没有使用反射来做,而是使用ASM,使用ASM方式比反射方式的好处是速度快以及实现lazyload,启动的时候不需要装载全部实例。
在parser方法中最后会调用processDeferredImportSelectors方法,解析Application上的@EnableAutoConfiguration注解(@SpringBootApplication包含此注解),此注解定义如下:
其中引入了EnableAutoConfigurationImportSelector,此类会调用SpringFactoriesLoader从spring.factories文件中加载EnableAutoConfiguration对应的xxxAutoConfiguration配置类。然后创建ConfigurationClassBeanDefinitionReader并调用其loadBeanDefinitions方法循环解析ConfigurationClass并注册BeanDefinition到容器中。
invokeBeanFactoryPostProcessors比较重要的是调用上面注册的ConfigurationClassPostProcessor.enhanceConfigurationClasses,其它的暂未研究。创建ConfigurationClassEnhancer并循环调用其enhance方法使用cglib为ConfigurationClass创建代理类。主要作用是代理@Bean方法,把获取Bean改为从BeanFactory中获取。
initMessageSource
该方法所执行的工作就是初始化容器中的国际化信息资源,Bean在上面的步骤中已经注册到容器(MessageSourceAutoConfiguration),实现类为ResourceBundleMessageSource。Spring的国际化本质上是基于JDK的ResourceBundle以及MessageFormat。在初始化的时候是不会去真正的去读取国际化文件,只有在第一次使用的时候才会去读取,调用的核心代码为ResourceBundleMessageSource.resolveCode。看过里面的源码发现虽然Spring对ResourceBundle以及MessageFormat都有使用HashMap做缓存,但是因为HashMap以及MessageFormat都不是线程安全的以及这里使用了懒加载的方式,里面多次使用到synchronized做同步控制。想来在极高的并发情况下这里也是会有瓶颈出现的。
onRefresh
该方法会初始化ThemeSource及应用容器(默认为Tomcat),在这个前后端分离的大背景下,ThemeSource可以不用管了。我们来看看Spring是怎样初始化Tomcat吧。核心方法为EmbeddedWebApplicationContext.createEmbeddedServletContainer:
getEmbeddedServletContainerFactory从BeanFactory中获取容器ContainerFactory,Bean在上面的步骤已经注册到容器(EmbeddedServletContainerAutoConfiguration),默认的实现类为TomcatEmbeddedServletContainerFactory。创建Tomcat的核心方法为TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer,有兴趣可以跟进去分析下,代码如下:
finishBeanFactoryInitialization
该方法会实例化容器中注册的singleton且非Lazy的beans,即@Scope为空或为singleton且没有用@Lazy修饰,看到这里想来大多数Bean都会被实例化。里面的核心调用为beanFactory.preInstantiateSingletons(),里面会调用beanFactory.getBean来实例化,看到这个方法大家可能很熟悉,这不是用来获取Bean实例的吗,怎么成实例化Bean的方法呢?跟进去看就会发现原来这个方法大有玄机,如果Bean在容器中不存在就会去实例化,上面那些向容器中注册的Bean其实都是Bean的定义,并不是真实的实例。
继续往下看,会调用AbstractAutowireCapableBeanFactory.createBean,然后调用doCreateBean,然后是createBeanInstance,然后是instantiateBean,然后是SimpleInstantiationStrategy.instantiate,调了这么多层终于到了实例化的代码instantiate,其它的都是Spring的扩展。实例化方式很简单,通过clazz.getDeclaredConstructor获取构造函数,然后调用Constructor.newInstance创建对象。由此看来spring默认创建对象是使用空构造函数来实现的,所以如果需要自定义构造函数,则必须定义一个空构造函数的说法就是由此得来。
实例化对象后会做一些回调及事件处理,这些才是Spring的金矿,就等你去挖了。后续有时间还会对文中具体的技术点做详细分析。