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

}

2.2. AbstractBeanFactory的getBean()方法

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

protected T doGetBean(final String name, @Nullable final Class 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 {

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 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);

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

);

     }
  }
  catch (BeansException ex) {
     throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

[外链图片转存中…(img-AIECIy2p-1720080385038)]

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值