BeanFactory和ApplicationContext

两者关系

BeanFactory ApplicationContext
早期接口,被称为Spring的Bean工厂 后期更高级接口,称之为Spring 容器
BeanFactory的API更偏向底层 API大多数是对BeanFactory底层API的封装;
且有更多的拓展功能:监听功能、国际化功能等
封装了Bean创建的主要逻辑和功能 继承了BeanFactory的功能
Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建 ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。

在Spring基础环境下,常用的三个ApplicationContext作用

实现类 功能描述
ClassPathXmlApplicationContext 加载类路径下的xml配置的ApplicationContext
FileSystemXmlApplicationContext 加载磁盘路径下的xml配置的ApplicationContext
AnnotationConfigApplicationContext 加载注解配置类的ApplicationContext

在Spring的web环境下,常用的两个ApplicationContext作用

导入依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.7</version>
</dependency>
实现类 功能描述
XmlWebApplicationContext web环境下,加载类路径下的xml配置的ApplicationContext
AnnotationConfigWebApplicationContext web环境下,加载磁盘路径下的xml配置的ApplicationContext

InitializingBean 接口

完成一些Bean的初始化操作

实现InitializingBean接口与在配置文件中指定init-method有什么不同?

如果同时实现了InitializingBean接口和init-method,那么系统会先调用InitializingBean再调用init-method指定的初始化方法。

总结

1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用

2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法

Spring主要的实例化方式

  • 构造方式实例化:底层通过构造方法对Bean进行实例化

    • 无参构造方法实例化
    • 有参构造方法实例化
      • 添加参数
  • 工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化

    • 静态工厂方法实例化Bean
      • 直接配置工厂对象及其静态方法配置
    1
    2
    3
    4
    5
    6
    7
    8
    // 工厂类
    public class UserDaoFactoryBean {
    // 静态工厂方法
    public static UserDaoImpl userDaoImpl(String name, int age){
    // 可以在此编写一些其他逻辑代码
    return new UserDaoImpl(name, age);
    }
    }
    1
    2
    3
    4
    <bean id="userDaoImpl" class="com.it.factory.UserDaoFactoryBean" factory-method="userDaoImpl">
    <constructor-arg name="name" value="小🐕"/>
    <constructor-arg name="age" value="18"/>
    </bean>
    • 实例工厂方法实例化Bean
      • 先有工厂对象,再用工厂对象去调用非静态方法
    1
    2
    3
    4
    5
    6
    7
    <!-- 配置实例工厂Bean -->
    <bean id="userDaoFactoryBean2" class="com.it.factory.UserDaoFactoryBean2"/>
    <!-- 配置实例工厂Bean的哪个方法作为工厂方法 -->
    <bean id="userDaoImpl2" factory-bean="userDaoFactoryBean2" factory-method="userDaoImpl2">
    <constructor-arg name="name" value="小🐱"/>
    <constructor-arg name="age" value="18"/>
    </bean>
    1
    2
    3
    4
    5
    6
    7
    8
    // 工厂类
    public class UserDaoFactoryBean2 {
    // 非静态工厂方法
    public UserDaoImpl userDaoImpl2(String name, int age){
    // 可以在此编写一些其他逻辑代码
    return new UserDaoImpl(name, age);
    }
    }
    • 实现FactoryBean规范延迟实例化Bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //工厂类
    public class UserDaoFactoryBean3 implements FactoryBean<UserDao> {

    @Override
    public UserDao getObject() throws Exception {
    return new UserDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
    return UserDao.class;
    }
    }
    1
    <bean id="userDao2" class="com.it.factory.UserDaoFactoryBean3"/>

Bean 实例化的基本流程

Spring容器在进行初始化时,会将xml配置的的信息(Bean标签)封装成一个BeanDefinition对象,所有的 BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。

image-20230324112528550

自定义命名空间的解析过程

解决循环引用问题:三级缓存

image-20230324172845976

Spring三级缓存源码刨析思路