写点什么

Spring 核心原理之 IoC 容器中那些鲜为人知的细节(3)

作者:Tom弹架构
  • 2021 年 12 月 25 日
  • 本文字数:10837 字

    阅读完需:约 36 分钟

本文节选自《Spring 5 核心原理》


Spring IoC 容器还有一些高级特性,如使用 lazy-init 属性对 Bean 预初始化、使用 FactoryBean 产生或者修饰 Bean 对象的生成、IoC 容器在初始化 Bean 过程中使用 BeanPostProcessor 后置处理器对 Bean 声明周期事件进行管理等。

1 关于延时加载

我们已经知道,IoC 容器的初始化过程就是对 Bean 定义资源的定位、载入和注册,此时容器对 Bean 的依赖注入并没有发生,依赖注入是在应用程序第一次向容器索取 Bean 时通过 getBean()方法完成的。当 Bean 定义资源的< bean>元素中配置了 lazy-init=false 属性时,容器将会在初始化时对所配置的 Bean 进行预实例化,Bean 的依赖注入在容器初始化时就已经完成。这样,当应用程序第一次向容器索取被管理的 Bean 时,就不用再初始化和对 Bean 进行依赖注入了,而是直接从容器中获取已经完成依赖注入的 Bean,提高了应用程序第一次向容器获取 Bean 的性能。

1.1. refresh()方法

IoC 容器读入已经定位的 Bean 定义资源是从 refresh()方法开始的,我们从 AbstractApplicationContext 类的 refresh()方法入手分析,回顾一下源码:


@Overridepublic void refresh() throws BeansException, IllegalStateException {    ...      //子类的refreshBeanFactory()方法启动      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();    ...}
复制代码


在 refresh()方法中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();启动了 Bean 定义资源的载入、注册过程。finishBeanFactoryInitialization()方法是对注册后的 Bean 定义中的预实例化(lazy-init=false,Spring 默认进行预实例化,即为 true)的 Bean 进行处理的地方。

1.2. 使用 finishBeanFactoryInitialization()处理预实例化的 Bean

当 Bean 定义资源被载入 IoC 容器之后,容器将 Bean 定义资源解析为容器内部的数据结构 BeanDefinition,并注册到容器中,AbstractApplicationContext 类中的 finishBeanFactoryInitialization()方法对配置了预实例化属性的 Bean 进行预初始化,源码如下:


//对配置了lazy-init属性的Bean进行预实例化处理protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {   //这是Spring 3新加的代码,为容器指定一个转换服务(ConversionService)   //在对某些Bean属性进行转换时使用   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {      beanFactory.setConversionService(            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));   }
if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders (strVal)); }
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); }
//为了使类型匹配,停止使用临时的类加载器 beanFactory.setTempClassLoader(null);
//缓存容器中所有注册的BeanDefinition元数据,以防被修改 beanFactory.freezeConfiguration();
//对配置了lazy-init属性的单例模式的Bean进行预实例化处理 beanFactory.preInstantiateSingletons();}
复制代码


其中 ConfigurableListableBeanFactory 是一个接口,preInstantiateSingletons()方法由其子类 DefaultListableBeanFactory 提供。

1.3. 对配置了 lazy-init 属性的单例模式的 Bean 的预实例化

对配置了 lazy-init 属性的单例模式的 Bean 的预实例化相关源码如下:


public void preInstantiateSingletons() throws BeansException {   if (this.logger.isDebugEnabled()) {      this.logger.debug("Pre-instantiating singletons in " + this);   }
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) { //获取指定名称的Bean定义 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //Bean不是抽象的,是单例模式的,且lazy-init属性配置为false if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果指定名称的Bean是创建容器的Bean if (isFactoryBean(beanName)) { //FACTORY_BEAN_PREFIX="&",当Bean名称前面加"&"符号 //时,获取的是容器对象本身,而不是容器产生的Bean //调用getBean方法,触发Bean实例化和依赖注入 final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); //标识是否需要预实例化 boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { //一个匿名内部类 isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> ((SmartFactoryBean<?>) factory).isEagerInit(), getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { //调用getBean()方法,触发Bean实例化和依赖注入 getBean(beanName); } } else { getBean(beanName); } } }
复制代码


通过对 lazy-init 处理源码的分析可以看出,如果设置了 lazy-init 属性,则容器在完成 Bean 定义的注册之后,会通过 getBean()方法触发指定 Bean 的初始化和依赖注入。如前所述,这样当应用程序第一次向容器索取所需的 Bean 时,容器不再需要对 Bean 进行初始化和依赖注入,可直接从已经完成实例化和依赖注入的 Bean 中取一个现成的 Bean,提高了第一次获取 Bean 的性能。

2 关于 FactoryBean 和 BeanFactory

Spring 中,有两个很容易混淆的类:BeanFactory 和 FactoryBean。BeanFactory:Bean 工厂,是一个工厂(Factory),Spring IoC 容器的最高层接口就是 BeanFactory,它的作用是管理 Bean,即实例化、定位、配置应用程序中的对象及建立这些对象之间的依赖。FactoryBean:工厂 Bean,是一个 Bean,作用是产生其他 Bean 实例。这种 Bean 没有什么特别的要求,仅需要提供一个工厂方法,该方法用来返回其他 Bean 实例。在通常情况下,Bean 无须自己实现工厂模式,Spring 容器担任工厂的角色;在少数情况下,容器中的 Bean 本身就是工厂,其作用是产生其他 Bean 实例。当用户使用容器时,可以使用转义字符“&”来得到 FactoryBean 本身,以区别通过 FactoryBean 产生的实例对象和 FactoryBean 对象本身。在 BeanFactory 中通过如下代码定义了该转义字符:String FACTORY_BEAN_PREFIX = "&";


如果 myJndiObject 是一个 FactoryBean,则使用 &myJndiObject 得到的是 myJndiObject 对象,而不是 myJndiObject 产生的对象。

2.1. FactoryBean 源码

//工厂Bean,用于产生其他对象public interface FactoryBean<T> {
//获取容器管理的对象实例 @Nullable T getObject() throws Exception;
//获取Bean工厂创建的对象的类型 @Nullable Class<?> getObjectType();
//Bean工厂创建的对象是否是单例模式的,如果是, //则整个容器中只有一个实例对象,每次请求都返回同一个实例对象 default boolean isSingleton() { return true; }
}
复制代码

2.2. AbstractBeanFactory 的 getBean()方法

在分析 Spring IoC 容器实例化 Bean 并进行依赖注入的源码时,提到在 getBean()方法触发容器实例化 Bean 时会调用 AbstractBeanFactory 的 doGetBean()方法,其重要源码如下:



protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... BeanFactory parentBeanFactory = getParentBeanFactory(); //当前容器的父容器存在,且当前容器中不存在指定名称的Bean if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { //解析指定Bean名称的原始名称 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { //委派父容器根据指定名称和显式的参数查找 return (T) parentBeanFactory.getBean(nameToLookup, args); } else { //委派父容器根据指定名称和类型查找 return parentBeanFactory.getBean(nameToLookup, requiredType); } } ... return (T) bean;}
//获取给定Bean的实例对象,主要完成FactoryBean的相关处理protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean, //也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象, //如果调用本身就想获得一个容器的引用,则返回这个工厂Bean实例对象 //如果指定的名称是容器的解引用(dereference,即对象本身而非内存地址) //且Bean实例也不是创建Bean实例对象的工厂Bean if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); }
//如果Bean实例不是工厂Bean,或者指定名称是容器的解引用 //调用者获取对容器的引用时,直接返回当前的Bean实例 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; }
//处理指定名称不是容器的解引用,或者根据名称获取的Bean实例对象是一个工厂Bean //使用工厂Bean创建一个Bean的实例对象 Object object = null; if (mbd == null) { //从Bean工厂缓存中获取指定名称的Bean实例对象 object = getCachedObjectForFactoryBean(beanName); } //让Bean工厂生产指定名称的Bean实例对象 if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; //如果从Bean工厂生产的Bean是单例模式的,则缓存 if (mbd == null && containsBeanDefinition(beanName)) { //从容器中获取指定名称的Bean定义,如果继承了基类,则合并基类的相关属性 mbd = getMergedLocalBeanDefinition(beanName); } //如果从容器得到了Bean定义信息,并且Bean定义信息不是虚构的, //则让工厂Bean生产Bean实例对象 boolean synthetic = (mbd != null && mbd.isSynthetic()); //调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean()方法 //实现工厂Bean生产Bean实例对象的过程 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object;}
复制代码


在上面获取给定 Bean 的实例对象的 getObjectForBeanInstance()方法中,会调用 FactoryBean- RegistrySupport 类的 getObjectFromFactoryBean()方法,该方法实现了 Bean 工厂生产 Bean 实例对象。

2.3. AbstractBeanFactory 生产 Bean 实例对象

AbstractBeanFactory 类中生产 Bean 实例对象的主要源码如下:


//Bean工厂生产Bean实例对象protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {   //Bean工厂是单例模式,并且Bean工厂缓存中存在指定名称的Bean实例对象   if (factory.isSingleton() && containsSingleton(beanName)) {      //多线程同步,以防止数据不一致      synchronized (getSingletonMutex()) {         //直接从Bean工厂的缓存中获取指定名称的Bean实例对象         Object object = this.factoryBeanObjectCache.get(beanName);         //如果Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象         if (object == null) {            //调用Bean工厂的获取对象的方法生产指定Bean的实例对象            object = doGetObjectFromFactoryBean(factory, beanName);            Object alreadyThere = this.factoryBeanObjectCache.get(beanName);            if (alreadyThere != null) {               object = alreadyThere;            }            else {               if (shouldPostProcess) {                  try {                     object = postProcessObjectFromFactoryBean(object, beanName);                  }                  catch (Throwable ex) {                     throw new BeanCreationException(beanName,                           "Post-processing of FactoryBean's singleton object failed", ex);                  }               }               //将生产的实例对象添加到Bean工厂的缓存中               this.factoryBeanObjectCache.put(beanName, object);            }         }         return object;      }   }   //调用Bean工厂的获取对象的方法生产指定Bean的实例对象   else {      Object object = doGetObjectFromFactoryBean(factory, beanName);      if (shouldPostProcess) {         try {            object = postProcessObjectFromFactoryBean(object, beanName);         }         catch (Throwable ex) {            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);         }      }      return object;   }}
//调用Bean工厂的方法生产指定Bean的实例对象private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException {
Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { //实现PrivilegedExceptionAction接口的匿名内部类 object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> factory.getObject(), acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //调用BeanFactory接口实现类的创建对象方法 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); }
//创建出来的实例对象为null,或者因为单例对象正在创建而返回null if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } object = new NullBean(); } return object;}
复制代码


从上面的源码分析中可以看出,BeanFactory 接口调用其实现类的获取对象的方法来实现创建 Bean 实例对象的功能。

2.4. FactoryBean 实现类的获取对象的方法

FactoryBean 接口的实现类非常多,比如 Proxy、RMI、JNDI、ServletContextFactoryBean 等。FactoryBean 接口为 Spring 容器提供了一个很好的封装机制,具体的获取对象的方法由不同的实现类根据不同的实现策略来提供,我们分析一下最简单的 AnnotationTestFactoryBean 类的源码:


public class AnnotationTestBeanFactory implements FactoryBean<FactoryCreatedAnnotationTestBean> {   private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean();   public AnnotationTestBeanFactory() {      this.instance.setName("FACTORY");   }   @Override   public FactoryCreatedAnnotationTestBean getObject() throws Exception {      return this.instance;   }   //AnnotationTestBeanFactory产生Bean实例对象的实现   @Override   public Class<? extends IJmxTestBean> getObjectType() {      return FactoryCreatedAnnotationTestBean.class;   }   @Override   public boolean isSingleton() {      return true;   }}
复制代码


Proxy、RMI、JNDI 等其他实现类都根据相应的策略提供方法,这里不做一一分析,这已经不是 Spring 的核心功能,感兴趣的“小伙伴”可以自行深入研究。

3 再述 autowiring

Spring IoC 容器提供了两种管理 Bean 依赖关系的方式:(1)显式管理:通过 BeanDefinition 的属性值和构造方法实现 Bean 依赖关系管理。(2)autowiring:Spring IoC 容器有依赖自动装配功能,不需要对 Bean 属性的依赖关系做显式的声明,只需要配置好 autowiring 属性,IoC 容器会自动使用反射查找属性的类型和名称,然后基于属性的类型或者名称来自动匹配容器中的 Bean,从而自动完成依赖注入。容器对 Bean 的自动装配发生在容器对 Bean 依赖注入的过程中。在对 Spring IoC 容器的依赖注入源码进行分析时,我们已经知道容器对 Bean 实例对象的依赖属性注入发生在 AbstractAutoWireCapableBeanFactory 类的 populateBean()方法中,下面通过程序流程分析 autowiring 的实现原理。

3.1. AbstractAutoWireCapableBeanFactory 对 Bean 实例对象进行属性依赖注入

应用程序第一次通过 getBean()方法(配置了 lazy-init 预实例化属性的除外)向 IoC 容器索取 Bean 时,容器创建 Bean 实例对象,并且对 Bean 实例对象进行属性依赖注入,AbstractAutoWire- CapableBeanFactory 的 populateBean()方法就实现了属性依赖注入的功能,其主要源码如下:


//将Bean属性设置到生成的实例对象上protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {   //获取容器在解析Bean定义时为BeanDefinition设置的属性值   PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
//处理依赖注入,首先处理autowiring自动装配的依赖注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
//根据Bean名称进行autowiring自动装配处理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); }
//根据Bean类型进行autowiring自动装配处理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); }
pvs = newPvs; }
//对非autowiring的属性进行依赖注入处理 ...}
复制代码

3.2. Spring IoC 容器根据 Bean 名称或者类型进行 autowiring 自动属性依赖注入

Spring IoC 容器根据 Bean 名称或者类型进行 autowiring 自动属性依赖注入的重要代码如下:


//根据类型对属性进行自动依赖注入protected void autowireByType(      String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
//获取用户定义的类型转换器 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; }
//存放解析的要注入的属性 Set<String> autowiredBeanNames = new LinkedHashSet<>(4); //对Bean对象中非简单属性(不是简单继承的对象,如8种原始类型、字符、URL等都是简单属性)进行处理 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { //获取指定属性名称的属性描述器 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); //不对Object类型的属性进行autowiring自动依赖注入 if (Object.class != pd.getPropertyType()) { //获取属性的赋值方法 MethodParameter MethodParam = BeanUtils.getWriteMethodParameter(pd); //检查指定类型是否可以被转换为目标对象的类型 boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance()); //创建一个要被注入的依赖描述 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(MethodParam, eager); //根据容器的Bean定义解析依赖关系,返回所有要被注入的Bean对象 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { //将属性赋值为所引用的对象 pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { //为指定名称属性注册依赖Bean名称,进行属性的依赖注入 registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } //释放已自动注入的属性 autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } }}
复制代码


通过上面的源码分析可以看出,通过属性名进行自动依赖注入相比通过属性类型进行自动依赖注入要稍微简单一些。但是真正实现属性注入的是 DefaultSingletonBeanRegistry 类的 registerDependentBean()方法。

3.3. DefaultSingletonBeanRegistry 的 registerDependentBean()方法实现属性依赖注入

DefaultSingletonBeanRegistry 的 registerDependentBean()方法实现属性依赖注入的重要代码如下:


//为指定的Bean注入依赖的Beanpublic void registerDependentBean(String beanName, String dependentBeanName) {   //处理Bean名称,将别名转换为规范的Bean名称   String canonicalName = canonicalName(beanName);   Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);   if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {      return;   }
//多线程同步,保证容器内数据的一致性 //在容器中通过“Bean名称→全部依赖Bean名称集合”查找指定名称Bean的依赖Bean synchronized (this.dependentBeanMap) { //获取指定名称Bean的所有依赖Bean名称 dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { //为Bean设置依赖Bean信息 dependentBeans = new LinkedHashSet<>(8); this.dependentBeanMap.put(canonicalName, dependentBeans); } //在向容器中通过“Bean名称→全部依赖Bean名称集合”添加Bean的依赖信息 //即,将Bean所依赖的Bean添加到容器的集合中 dependentBeans.add(dependentBeanName); } //在容器中通过“Bean名称→指定名称Bean的依赖Bean集合”查找指定名称Bean的依赖Bean synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName); if (dependenciesForBean == null) { dependenciesForBean = new LinkedHashSet<>(8); this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean); } //在容器中通过“Bean名称→指定Bean的依赖Bean名称集合”添加Bean的依赖信息 //即,将Bean所依赖的Bean添加到容器的集合中 dependenciesForBean.add(canonicalName); }}
复制代码


可以看出,autowiring 的实现过程如下:(1)对 Bean 的属性调用 getBean()方法,完成依赖 Bean 的初始化和依赖注入。(2)将依赖 Bean 的属性引用设置到被依赖的 Bean 属性上。(3)将依赖 Bean 的名称和被依赖 Bean 的名称存储在 IoC 容器的集合中。Spring IoC 容器的 autowiring 自动属性依赖注入是一个很方便的特性,可以简化开发配置,但是凡事都有两面性,自动属性依赖注入也有不足:首先,Bean 的依赖关系在配置文件中无法很清楚地看出来,会给维护造成一定的困难;其次,由于自动属性依赖注入是 Spring 容器自动执行的,容器是不会智能判断的,如果配置不当,将会带来无法预料的后果。所以在使用自动属性依赖注入时需要综合考虑。


关注微信公众号『 Tom 弹架构 』回复“Spring”可获取完整源码。


本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom 弹架构 』可获取更多技术干货!


原创不易,坚持很酷,都看到这里了,小伙伴记得点赞、收藏、在看,一键三连加关注!如果你觉得内容太干,可以分享转发给朋友滋润滋润!

发布于: 4 小时前
用户头像

Tom弹架构

关注

不只做一个技术者,更要做一个思考者 2021.10.22 加入

畅销书作者,代表作品:《Spring 5核心原理》、《Netty 4核心原理》、《设计模式就该这样学》

评论

发布
暂无评论
Spring核心原理之 IoC容器中那些鲜为人知的细节(3)