SpringBoot(三)原理剖析:IOC原理
IOC(Inversion of Control,控制倒转),意思是对象之间的关系不再由传统的程序来控制,而是由spring容器来统一控制这些对象创建、协调、销毁,而对象只需要完成业务逻辑即可。IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
BeanFactory与ApplicationContext
BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,Spring 使用 BeanFactory 来实例化、配置和管理 Bean。BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承MessageSource,因此支持国际化。
- 统一的资源文件访问方式。
- 提供在监听器中注册bean的事件。
- 同时加载多个配置文件。
- 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化
容器初始化分析
在SpringBoot(一)原理剖析:SpringApplication启动原理 中,分析SpringBoot启动流程时,有3个步骤与容器的创建和初始化有关,分别是createApplicationContext()、prepareContext()和refreshContext()。因此着重从这3个方法看SpringBoot如何初始化IOC容器。
creatApplicationContext
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
- this.applicationContextClass由SpringApplicationBuilder类中的contextClass()方法赋值的,默认为空;
- webApplicationType变量是SpringApplication.run()方法调用构造方法时赋值的,本项目中是SERVLET,生成的容器是AnnotationConfigServletWebServerApplicationContext
- 通过BeanUtils工具类将获取到的容器类转换成ConfigurableApplicationContext类的实例,返回给应用使用
prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } } protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); }
- context.setEnvironemnt:为容器设置环境,和webApplicationType有关,在本项目中为StandardServletEnvironment;
- postProcessApplicationContext:在ApplicationContext中应用任何相关的后期处理,beanNameGenerator默认为空;
- applyInitializers:初始化context容器
- listeners.contextPrepared:将事件广播器注册到Spring容器中;
- logStartupInfo:记录启动信息:[Starting App on ... with PID 2536...] ;
- logStartupProfileInfo:记录活动配置文件信息: [No active profile set, falling back to default profiles: default];
- beanFactory.registerSingleton:往容器中注册指定接口实现类的单例;
- getAllSources:启动类信息;
- load:从上面获取的启动类注册到ApplicationContext的BeanFactory中;
- listeners.contextLoaded:广播出ApplicationPreparedEvent事件给相应的监听器执行
refreshContext
采用了模板方法模式,实际是调用如下AbstractApplicationContext的refresh方法:
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 beans anymore... resetCommonCaches(); } } }
- prepareRefresh:设置容器的状态 、初始化属性设置(应用监听器)、检查必备属性是否存在;
- obtainFreshBeanFactory:设置beanFactory序列化id、获取beanFactory;
- prepareBeanFactory:主要是对beanFactory做一些配置包括各种类加载器和需要忽略的依赖;
- postProcessBeanFactory:允许上下文子类对bean工厂进行后置处理;
- invokeBeanFactoryPostProcessors:调用BeanDefinitionRegistryPostProcessor实现向容器内添加bean的定义;
- registerBeanPostProcessors:注册后置处理器,用于拦截bean的创建,AOP原理的的核心方法AspectJAutoProxyRegistrar.registerBeanDefinitions在此执行;
- initMessageSource:初始化国际化相关属性;
- initApplicationEventMulticaster:初始化事件广播器;
- onRefresh:该方法是一个空实现,是留给子类实现的,主要内容是创建web容器;
- registerListeners:添加容器内事件监听器至事件广播器中;
- finishBeanFactoryInitialization:初始化所有剩下的单例bean;
- finishRefresh:清空缓存、初始化生命周期处理器、调用化生命周期处理器onRfresh方法、发布ContextRefreshedEvent事件、JXM相关处理
ApplicationContextAware实例
Spring容器初始化过程中会检测容器中的所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContextAware()方法。调用该方法时,会将容器本身作为参数传给该方法——该方法中的实现部分将Spring传入的参数(容器本身)赋给该类对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } public static boolean containsBean(String name) { return applicationContext.containsBean(name); } public static boolean isSingleton(String name) { return applicationContext.isSingleton(name); } }
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); System.out.println(SpringContextUtil.containsBean("orangeService")); System.out.println(SpringContextUtil.containsBean("appleService")); } }
运行结果如下:
... 2021-02-24 23:27:04.814 INFO 8588 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-02-24 23:27:04.840 INFO 8588 --- [ main] com.ryj.test2021.App : Started App in 7.867 seconds (JVM running for 8.778) false true