Spring 源码系列之容器概览~

2年前 (2022) 程序员胖胖胖虎阿
230 0 0

松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot+Vue+微人事视频教程


上篇文章和小伙伴们分享了 Spring 中的 FactoryBean(Spring 源码第 9 篇,深入分析 FactoryBean),大家知道 Spring 中还有一个和 FactoryBean 单词很像的类叫做 BeanFactory!

今天松哥就再通过一篇文章来和大家聊聊 BeanFactory!

1.容器概览

Spring 中的 Ioc 容器,我们可以大致上分为两种:

  • BeanFactory
  • ApplicationContext

1.1 BeanFactory

BeanFactory 是最最基础的 IoC 容器,它提供了一个 IoC 容器所需的基本功能。在本系列前面文章中我们所用到的 XmlBeanFactory 就是它的实现之一。

BeanFactory 默认采用延迟初始化策略,即当容器启动时,并未完成 Bean 的初始化,只有当调用到该 Bean 的实例时,才会完成其初始化操作,并进行依赖注入。

例如如下代码:

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = factory.getBean(User.class);

当第一行代码执行的时候,beans.xml 中配置的 User 对象并未进行初始化,只有当第二行 getBean 方法调用时,User 对象才进行了初始化操作。

这样设计的好处是容器启动速度快,因为要做的事情比较少。

1.2 ApplicationContext

ApplicationContext 是在 BeanFactory 的基础上实现的,BeanFactory 的功能它都有,算是一种高级容器。

ApplicationContext 在 BeanFactory 的基础上提供了事件发布、国际化等功能。

同时,ApplicationContext 和 BeanFactory 还有一个很大的不同在于 ApplicationContext 在容器启动时,就会完成所有 Bean 的初始化,这也就以为着容器启动时间较长,并且对系统资源要求也较高。

例如如下一段代码:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

当这段代码执行时,beans.xml 中配置的 User 对象就会完成初始化操作。

2.BeanFactory

BeanFactory 的继承关系图太大了,放在文章里显示不全。小伙伴们可以在 IDEA 中自行查看,我这里就不贴图出来了。

我们来看下 BeanFactory 的源码:

public interface BeanFactory {
 String FACTORY_BEAN_PREFIX = "&";
 Object getBean(String name) throws BeansException;
 <T> getBean(String name, Class<T> requiredType) throws BeansException;
 Object getBean(String name, Object... args) throws BeansException;
 <T> getBean(Class<T> requiredType) throws BeansException;
 <T> getBean(Class<T> requiredType, Object... args) throws BeansException;
 <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
 <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
 boolean containsBean(String name);
 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
 boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
 @Nullable
 Class<?> getType(String name) throws NoSuchBeanDefinitionException;
 @Nullable
 Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
 String[] getAliases(String name);
}

可以看到,BeanFactory 主要提供了一些和 Bean 相关的查询方法。

FACTORY_BEAN_PREFIX

这是定义的 FactoryBean 前缀,具体参考Spring 源码第 9 篇,深入分析 FactoryBean。

getBean

getBean 方法一共有五个。

在获取一个 Bean 的时候,可以指定 Bean 的名字,也可以指定 Bean 的类型,也可以同时指定,这个好理解。

还有两个重载的方法里边有一个 args 参数,这个可能有的小伙伴们没怎么用过,我这里稍微解释下。

其实我们大概能猜出来 args 是获取 Bean 时所需要的参数,如果使用该方法给 Bean 配置参数,需要将 Bean 的 scope 设置为 prototype,这就意味着每次获取 Bean 的时候,才去进行 Bean 的初始化(否则配置的参数无法生效)。

松哥通过一个例子来给大家演示下。

首先我们提供一个 User 类:

public class User {
    private String username;
    private Integer age;

    public User(String username, Integer age) {
        this.username = username;
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

可以看到,User 类只有一个有参的构造方法。

接下来我们在 XML 文件中进行配置,XML 文件中需要配置 User 类的构造方法参数,我们可以配置为 null,同时记得设置 scope 属性为 prototype。

<bean class="org.javaboy.spring_demo04.User" id="user1" scope="prototype">
    <constructor-arg name="username"><null></null></constructor-arg>
    <constructor-arg name="age"><null></null></constructor-arg>
</bean>

最后再通过如下方式加载 Bean:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
User user = ctx.getBean(User.class,new Object[]{"javaboy",99});
System.out.println("user = " + user);

最终打印结果如下:

user = User{username='javaboy', age=99}

可以看到,为 Bean 配置的参数都生效了。

getBeanProvider

方法用于获取指定 Bean 的提供者,可以看到它返回的是一个 ObjectProvider,该类扩展自 ObjectFactory,从 Spring4.3 时开始提供。

可能大家对 ObjectProvider 接触较少,这里我也稍微说一下。

先来看 ObjectFactory:

@FunctionalInterface
public interface ObjectFactory<T{
 getObject() throws BeansException;
}

ObjectFactory 有点类似于我们在 Spring 源码第 9 篇,深入分析 FactoryBean 一文中所讲的 FactoryBean。

不同的是,ObjectFactory 中只有一个 getObject 方法,该方法可以返回 Object 实例。ObjectFactory 与 FactoryBean 相似,但是后者的实现通常是在 BeanFactory 中定义为 SPI 实例,而此类的实现通常是作为 API 注入给其他 Bean。

再来看 ObjectProvider(部分):

public interface ObjectProvider<Textends ObjectFactory<T>, Iterable<T{
    //返回指定类型的 bean, 如果容器中不存在, 抛出 NoSuchBeanDefinitionException 异常;如果容器中有多个此类型的 bean, 抛出 NoUniqueBeanDefinitionException 异常
 getObject(Object... args) throws BeansException;
    //如果指定类型的 bean 注册到容器中, 返回 bean 实例, 否则返回 null
 @Nullable
 getIfAvailable() throws BeansException;
    //如果指定类型的 bean 在容器中只有一个 bean, 返回 bean 实例, 如果不存在则返回 null;如果容器中有多个此类型的 bean, 抛出 NoUniqueBeanDefinitionException 异常
 @Nullable
 getIfUnique() throws BeansException;
    // Spring5.0 之后提供,返回指定类型 Bean 的迭代器
 @Override
 default Iterator<T> iterator() {
  return stream().iterator();
 }
    //转为 Stream
 default Stream<T> stream() {
  throw new UnsupportedOperationException("Multi element access not supported");
 }
 default Stream<T> orderedStream() {
  throw new UnsupportedOperationException("Ordered element access not supported");
 }
}

这是 ObjectProvider 的源码,那么它有什么用呢?松哥举个简单例子。

在 Spring4.3 之前。

假设我有一个 UserDao,如下:

@Repository
public class UserDao {
}

还有一个 UserService,如下:

@Service
public class UserService {
    private UserDao userDao;

    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String toString() {
        return "UserService{" +
                "userDao=" + userDao +
                '}';
    }
}

在 UserService 中注入 UserDao 时,必须明确给出 @Autowired 注解。

这看起来不够优雅!

从 Spring4.3 开始,在 UserService 中注入 UserDao 时,如果构造方法只有一个参数,可以不用添加 @Autowired 注解,像下面这样:

@Service
public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String toString() {
        return "UserService{" +
                "userDao=" + userDao +
                '}';
    }
}

但是如果 UserDao 为 null,则会导致 UserService 初始化失败,通过 ObjectProvider 可以解决,如下:

@Service
public class UserService {
    private UserDao userDao;

    public UserService(ObjectProvider<UserDao> userDao) {
        this.userDao = userDao.getIfUnique();
    }

    @Override
    public String toString() {
        return "UserService{" +
                "userDao=" + userDao +
                '}';
    }
}

这就是 ObjectProvider。好啦,扯远了,我们回到 BeanFactory 继续看它里边的方法。

containsBean

判断容器中是否包含某个 Bean。

isSingleton

判断某个 Bean 是否是单例的。

isPrototype

判断某个 Bean 是否是 prototype。

isTypeMatch

返回指定名称的 Bean 是否匹配指定的类型。

getType

返回指定名称的 Bean 对应的数据类型。

getAliases

返回 Bean 的别名。

这就是 BeanFactory 中定义的所有方法,可以看到,基本上都是容器相关的查询方法,接下来将在 BeanFactory 的各种实现类中,对其进行实现。

3.二级接口

BeanFactory 作为 IoC 容器中最顶层的定义,没有继承任何接口,我们可以称之为一级接口,直接继承自 BeanFactory 的接口有三个,我们称之为二级接口。

3.1 HierarchicalBeanFactory

HierarchicalBeanFactory 继承自 BeanFactory,定义了工厂分层,在其基础上又扩展了两个方法:

public interface HierarchicalBeanFactory extends BeanFactory {
 BeanFactory getParentBeanFactory();
 boolean containsLocalBean(String name);
}
  • getParentBeanFactory 方法返回 Bean 工厂的父工厂,实现了工厂分层。
  • containsLocalBean 方法则判断本地工厂是否包含这个 Bean。

HierarchicalBeanFactory 有一个接口 ConfigurableBeanFactory,ConfigurableBeanFactory 接口继承自 HierarchicalBeanFactory 和 SingletonBeanRegistry,其中 SingletonBeanRegistry 这个接口定义了对单例 Bean 的定义以及获取方法。也就是说,ConfigurableBeanFactory 中兼具工厂分层和单例处理功能。同时,在 HierarchicalBeanFactory 中 getParentBeanFactory 所 get 到的对象,也是在 ConfigurableBeanFactory 中完成配置。

3.2 AutowireCapableBeanFactory

AutowireCapableBeanFactory 继承自 BeanFacotory,它扩展了自动装配的功能。

public interface AutowireCapableBeanFactory extends BeanFactory {
 int AUTOWIRE_NO = 0;
 int AUTOWIRE_BY_NAME = 1;
 int AUTOWIRE_BY_TYPE = 2;
 int AUTOWIRE_CONSTRUCTOR = 3;
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;
 String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";
 <T> createBean(Class<T> beanClass) throws BeansException;
 Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
 void autowireBean(Object existingBean) throws BeansException;
 Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
 void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
 Object configureBean(Object existingBean, String beanName) throws BeansException;
 Object initializeBean(Object existingBean, String beanName) throws BeansException;
 void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
 Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
 Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
 void destroyBean(Object existingBean);
 <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
 Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
 @Nullable
 Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
 @Nullable
 Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
   @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
 throws BeansException
;
}

首先通过五个常量定义了五种不同的装配策略,分别是:不自动装配、通过名称自动装配、通过类型自动装配、通过构造方法自动装配,还有一个过期的常量。

ORIGINAL_INSTANCE_SUFFIX 则是初始化实例给定名称时约定的后缀,该后缀会添加到类的全路径后面,例如:com.mypackage.MyClass.ORIGINAL

createBean 方法用来创建 Bean 实例;autowire* 方法则用来完成自动装配;configureBean 用来配置 BeaninitializeBean 用来初始化 BeanapplyBeanPropertyValues 将指定 beanBeanDefinition 应用到一个已经存在的 Bean 上;applyBeanPostProcessorsBeforeInitialization/applyBeanPostProcessorsAfterInitialization 调用 Bean 的后置处理器;destroyBean 方法用来销毁 Beanresolve* 方法用来解析 Bean

3.3 ListableBeanFactory

ListableBeanFactory 继承自 BeanFacotory,该接口可以列出工厂可以生产的所有实例。

public interface ListableBeanFactory extends BeanFactory {
 boolean containsBeanDefinition(String beanName);
 int getBeanDefinitionCount();
 String[] getBeanDefinitionNames();
 String[] getBeanNamesForType(ResolvableType type);
 String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);
 String[] getBeanNamesForType(@Nullable Class<?> type);
 String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
 <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
 <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;
 String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
 Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
 @Nullable
 <A extends Annotation> findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException;
}
  • containsBeanDefinition:判断容器是否包含某一个 Bean 的定义。
  • getBeanDefinitionCount:获取 BeanDefinition 数量。getBeanDefinitionCount 和 containsBeanDefinition 执行效率较低,慎用。
  • getBeanDefinitionNames:获取所有 Bean 的名字。
  • getBeanNamesForType:返回指定类型的 BeanName。
  • getBeansOfType:返回指定类的名称和 Bean Map(相同类型的 Bean 可能存在多种实例)。
  • getBeanNamesForAnnotation:根据注解获取指定的 BeanName。
  • getBeansWithAnnotation:根据注解获取指定的 BeanName 以及 Bean Map。
  • findAnnotationOnBean:根据指定 beanName 和注解类型查找指定的 Bean。

ListableBeanFactory 有一个实现接口 ConfigurableListableBeanFactory,这是以上所介绍接口的集大成者,ConfigurableListableBeanFactory 继承自 ListableBeanFactory、AutowireCapableBeanFactory 以及 ConfigurableBeanFactory,可以看到,三个二级接口功能集于一身,它包含了 BeanFactory 体系目前的所有方法。这些方法都在 DefaultListableBeanFactory 中进行实现,也就是我们前面所讲的 Spring 源码第六弹!松哥和大家聊聊容器的始祖 DefaultListableBeanFactory。

这就是直接继承自 BeanFactory 的三个接口。这三个接口中,有的方法大家可能用过,有的可能没用过,松哥后面抽空整一个视频,把这里涉及到的方法用法给小伙伴们演示一遍,加深大家的理解。

4.小结

好啦,本文主要和小伙伴们梳理了一下 BeanFactory 体系,扯的有点多了,下篇文章我们来继续看基于 BeanFactory 的 ApplicationContext。

今日干货

Spring 源码系列之容器概览~
刚刚发表
查看:
66666
回复:666

公众号后台回复 ssm,免费获取松哥纯手敲的 SSM 框架学习干货。

本文分享自微信公众号 - 江南一点雨(a_javaboy)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

版权声明:程序员胖胖胖虎阿 发表于 2022年11月6日 下午8:16。
转载请注明:Spring 源码系列之容器概览~ | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...