Spring体系的各种启动流程详解
⽬录
基本组件
基础流程
Springframework
1、容器类
2、注解定义bean读取器
3、BeanFactoryPostProcessor
4、refresh
SpringMVC
1、配置
2、启动过程
SpringBoot
总结
在介绍spring的启动之前,先来说下启动过程中使⽤到的⼏个类
基本组件
1、BeanFactory:spring底层容器,定义了最基本的容器功能,注意区分FactoryBean
2、ApplicationContext:扩展于BeanFactory,拥有更丰富的功能。例如:添加事件发布机制、⽗⼦级容器,⼀般都是直接使⽤ApplicationContext。
3、Resource:bean配置⽂件,⼀般为xml⽂件。可以理解为保存bean信息的⽂件。
4、BeanDefinition:beandifinition定义了bean的基本信息,根据它来创造bean
基础流程
不管是哪种系列的spring(springframework、springmvc、springboot、springcloud),Spring的启动过程主要可以分为两部分:
第⼀步:解析成BeanDefinition:将bean定义信息解析为BeanDefinition类,不管bean信息是定义在xml中,还是通过@Bean注解标注,都能通过不同的BeanDefinitionReader转为BeanDefinition类,将BeanDefinition向Map中注册 Map<name,beandefinition>。这⾥分两种BeanDefinition,RootBeanDefintion和BeanDefinition。RootBeanDefinition这种是系统级别的,是启动Spring必须加载的6个Bean。BeanDefinition是我们定义的Bean。
第⼆步:参照BeanDefintion定义的类信息,通过BeanFactory⽣成bean实例存放在缓存中。这⾥的BeanFactoryPostProcessor是⼀个,在BeanDefinition实例化后,BeanFactory⽣成该Bean之前,可以对BeanDefinition进⾏修改。BeanFactory根据BeanDefinition定义使⽤反射实例化Bean,实例化和初始化Bean的过程中就涉及到Bean的⽣命周期了,典型的问题就是Bean的循环依赖。接着,Bean实例化前会判断该Bean是否需要增强,并决定使⽤哪种代理来⽣成Bean。
Springframework
1、容器类
在⼀般性的spring项⽬中,⼤家应该也都知道,⼀般是通过直接实例化applicationContext类,来实现项⽬的启动下⾯我们来看下通过注解的⽅式来启动的情况,注解容器定义如下:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
public AnnotationConfigApplicationContext() {
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
创建了注解定义bean读取器和配置⽂件定义bean扫描器
2、注解定义bean读取器
进⼊该类构造器中,可以看到最终会执⾏该⽅法:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(DependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(AutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!ainsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!ainsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
}
注册了6个RootBeanDefinition,即系统级别的BeanDefinition。同时,经过调⽤registerPostProcessor->registerBeanDefinition,可以看到注册BeanDefinition其实就是放到BeanFactory的缓存中。
DefaultListableBeanFactory.java类中
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
...
}
上⾯的6个beanDefinition的实例参数中都有⼀个postprocessor后缀的类,我们分别点击进⼊查看即继承关系,可以看到,最终都继承⾃``接⼝
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
3、BeanFactoryPostProcessor
1、BeanFactoryPostProcessor是spring初始化bean的扩展点。
官⽂翻译如下:允许⾃定义修改应⽤程序上下⽂的bean定义,调整上下⽂的基础bean⼯⼚的bean属性值。应⽤程序上下⽂可以在其bean定义中⾃动检测BeanFactoryPostProcessor bean,并在创建任何其他bean之前先创建BeanFactoryPostProcessor。
BeanFactoryPostProcessor可以与bean定义交互并修改bean定义,但绝不能与bean实例交互。这样做可能会导致bean过早实例化,违反容器并导致意外的副作⽤。如果需要bean实例交互,请考虑实现BeanPostProcessor。实现该接⼝,可以允许我们的程序获取到BeanFactory,从⽽修改BeanFactory,可以实现编程式的往Spring 容器中添加Bean。
也就是说,我们可以通过实现BeanFactoryPostProcessor接⼝,获取BeanFactory,操作BeanFactory对象,修改BeanDefinition,但不要去实例化bean。
2、BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的⼦类,在⽗类的基础上,增加了新的⽅法,允许我们获取到BeanDefinitionRegistry,从⽽编码动态修改BeanDefinition。
例如往BeanDefinition中添加⼀个新的BeanDefinition。
这两个接⼝是在AbstractApplicationContext#refresh⽅法中执⾏到invokeBeanFactoryPostProcessors(beanFactory);⽅法时被执⾏的。
3、⽰例代码如下:
@Repository
public class OrderDao {
public void query() {
System.out.println("");
}
}
public class OrderService {
private OrderDao orderDao;
public void setDao(OrderDao orderDao) {
}
public void init() {
System.out.println("");
}
public void query() {
orderDao.query();
}
}
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//向Spring容器中注册OrderService
BeanDefinition beanDefinition = icBeanDefinition(OrderService.class)
//这⾥的属性名是根据setter⽅法
.addPropertyReference("dao", "orderDao")
.setInitMethodName("init")
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 在这⾥修改orderService bean的scope为PROTOTYPE
BeanDefinition beanDefinition = BeanDefinition("orderService");
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
回到上⾯,我们拿ConfigurationClassPostProcessor来说:在Spring中ConfigurationClassPostProcessor同时实现了BeanDefinitionRegistryPostProcessor接⼝和其⽗类接⼝中的⽅法。
1、ConfigurationClassPostProcessor#postProcessBeanFactory:主要负责对Full Configuration 配置进⾏增强,拦截@Bean⽅法来确保增强执⾏@Bean⽅法的语义。
2、ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry:负责扫描我们的程序,根据程序的中Bean创建BeanDefinition,并注册到容器中。
我们进⼊到:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = BeanName();
if (StringUtils.hasLength(beanName) && ainsBeanDefinition(beanName)) {
}
Metadata().getClassName());
return;
适合新手的spring boot
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : BeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
ImportedResources());
ImportBeanDefinitionRegistrars());
}
其中,我们可以看到:
1、通过检查是否有·@import·注解,来注册该导⼊类到容器中
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
2、遍历@Configuration类中的@bean注解,将其类注册到容器中
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
4、refresh
这个⽅法就是正式进⾏bean的处理的主要逻辑
@Override
public 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
resetCommonCaches();
}
}
}
前⾯说的⼀些扩展点类都是在这⾥才处理的,spring的扩展机制后⾯会有专门的⽂章来讲解。
SpringMVC
⽽在web项⽬中,我们⼀般都是使⽤的spring mvc,Spring Framework本⾝没有Web功能,Spring MVC使⽤WebApplicationContext类扩展ApplicationContext,使得拥有web功能。
那么,Spring MVC是如何在web环境中创建IoC容器呢?web环境中的IoC容器的结构⼜是什么结构呢?web环境中,Spring IoC容器是怎么启动呢?
1、配置
以Tomcat为例,在Web容器中使⽤Spirng MVC,必须进⾏四项的配置:
修改l,添加servlet定义;
编写l(servletname是在中配置DispactherServlet时使servlet-name的值)配置;
contextConfigLocation初始化参数
配置ContextLoaderListerner;⽰例配置如下:
<!-- servlet定义:前端处理器,接受的HTTP请求和转发请求的类 -->
<servlet>
<servlet-name>court</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- l:定义WebAppliactionContext上下⽂中的bean -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:l</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>court</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置contextConfigLocation初始化参数:指定Spring IoC容器需要读取的定义了⾮web层的Bean(DAO/Service)的XML⽂件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/l</param-value>
</context-param>
<!-- 配置ContextLoaderListerner:Spring MVC在Web容器中的启动类,负责Spring IoC容器在Web上下⽂中的初始化 -->
<listener>
<listener-class>org.t.ContextLoaderListener</listener-class>
</listener>
在l配置⽂件中,有两个主要的配置:ContextLoaderListener和DispatcherServlet。
同样的关于spring配置⽂件的相关配置也有两部分:context-param和DispatcherServlet中的init-param。
那么,这两部分的配置有什么区别呢?它们都担任什么样的职责呢?
在Spring MVC中,Spring Context是以⽗⼦的继承结构存在的。
Web环境中存在⼀个ROOT Context,这个Context是整个应⽤的根上下⽂,是其他context的双亲Context。
同时Spring MVC也对应的持有⼀个独⽴的Context,它是ROOT Context的⼦上下⽂。
对于这样的Context结构在Spring MVC中是如何实现的呢?下⾯就先从ROOT Context⼊⼿,ROOT Context是在ContextLoaderListener中配置
的,ContextLoaderListener读取context-param中的contextConfigLocation指定的配置⽂件,创建ROOT Context。
2、启动过程
Spring MVC启动过程⼤致分为两个过程:
ContextLoaderListener初始化,实例化IoC容器,并将此容器实例注册到ServletContext中;
DispatcherServlet初始化;
tomcat在启动的时候,会依次执⾏listeners的初始化,也就是执⾏该ContextLoaderListener的初始化,最终会调⽤下⾯的代码:
public void contextInitialized(ServletContextEvent event) {
this.ServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//PS : ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=Name() + ".ROOT" 根上下⽂的名称
//PS : 默认情况下,配置⽂件的位置和名称是:DEFAULT_CONFIG_LOCATION = "/l"
//在整个web应⽤中,只能有⼀个根上下⽂
if (Attribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in l!"); }
Log logger = Log(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (t == null) {
// 在这⾥执⾏了创建WebApplicationContext的操作
}
if (t instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) t;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (Parent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// PS: 将根上下⽂放置在servletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, t);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ClassLoader()) {
currentContext = t;
} else if (ccl != null) {
currentContextPerThread.put(ccl, t);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
t;
} catch (RuntimeException ex) {
<("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
} catch (Error err) {
<("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。