目录
一、BeanFactory
BeanFactory获取bean
public class BeanTest {
public static void main(String[] args) {
//创建工厂对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建一个读取器,绑定工厂
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//读取配置文件信息
reader.loadBeanDefinitions("beans.xml");
//使用工厂 getBean() 方法获取创建的对象
UserService userService1 = (UserService) beanFactory.getBean("userService");
System.out.println("userService1 = " + userService1);
UserService userService2 = beanFactory.getBean(UserService.class);
System.out.println("userService2 = " + userService2);
UserService userService3 = beanFactory.getBean("userService", UserService.class);
System.out.println("userService3 = " + userService3);
}
}
运行结果:
BeanFactory 是 Spring 的一个早期接口,称为 Spring 的 Bean 工厂,Bean 创建的主要逻辑和功能都被封装在 BeanFactory 中,这里我们使用的 DefaultListableBeanFactory 是他的一个实现。
BeanFactory 中的 getBean()
方法用于获取创建的对象,其中传递的参数可以是 bean 的名称、bean 的类型,或者两者结合。
从代码运行的结果来看,三种方式获取到的对象都是一个,这是因为默认情况下 Spring 创建的 Bean 对象是单例的。
二、ApplicationContext
上面我们了解了怎么通过 BeanFactory 去创建获取 bean,但是实际的开发中我们不会去使用 BeanFactory 来创建 bean,BeanFactory 是 IoC 容器的基本实现,是 Spring 内部使用的接口,面向 Spring 本身,一般不面向开发人员,不提供开发人员使用。面向 Spring 的使用者,几乎所有场合使用的都是 ApplicationContext。
public class BeanTest {
public static void main(String[] args) {
//加载配置文件信息
ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
UserDao userDao = (UserDao) classPathXmlApplicationContext.getBean("userDao");
System.out.println("userDao = " + userDao);
}
}
ApplicationContext 称为 Spring 容器,内部封装了 BeanFactory,功能比起 BeanFactory 更加丰富强大,使用 ApplicationContext 开发时,配置文件我们习惯命名为 applicationContext.xml
ApplicationContext 本身也是一个接口,这里使用的 ClassPathXmlApplicationContext 是他的实现,用于加载类加载路径下的配置文件
三、两者的继承体系
3.1 BeanFactory
BeanFactory 是 Spring 的核心接口,在项目运行中需要有具体的实现,这个具体的实现就是我们上面使用的 DefaultListableBeanFactory
ApplicationContext 内部会维护一个 BeanFactory,其具体的实现也是 DefaultListableBeanFactory
DefaultListableBeanFactory 中存在两个 map 集合需要我们注意的
-
beanDefinitionMap:类定义集合
这个集合的内部存储的是类定义的信息
Java 是面向对象的语言,一切都可以定义为对象,类的定义信息,在 Spring 中也是通过对象来进行存储的
我们在 xml 中定义的 bean 标签,在项目运行时会被抽取封装为一个个的对象,这些对象对应的类就是 BeanDefinition,BeanDefinition 中会存储我们在 xml 中配置的类的信息,例如类的全类名 beanClass,作用范围 scope 等
-
singletonObjects:单例池
准确的说,singletonObjects 并不是 DefaultListableBeanFactory 的属性,而是他的父类 DefaultSingletonBeanRegistry 的属性
这个 map 是用于存放 Spring 成品 bean 的
Spring 在运行时,大致的步骤是先将 bean 的注册信息存储至 beanDefinitionMap 中,然后再通过遍历 beanDefinitionMap 获取内部的类定义信息,使用工厂和反射来创建对象,再将创建好的对象存储在 singletonObjects 中,之后再获取使用。
在创建对象的过程中,我们可以使用 Spring 提供的后处理器,对 beanDefinitionMap 的内容进行操作,或者在创建好的对象存储进 singletonObjects 之前,对对象进行操作,以达到对 bean 的修改
3.2 ApplicationContext
ApplicationContext,在 Spring 的基础环境下(即只依赖 spring-context 坐标时),ApplicationContext 有三个需要注意的实现类
ClassPathXmlApplicationContext:适用于加载类路径下的 xml 文件中的类定义信息
AnnotationConfigApplicationContext:适用于加载基于注解定义标记的类的类定义信息
FileSystemXmlApplicationContext:适用于加载文件系统下的 xml 文件中的类定义信息
3.2.1 AnnotationConfigApplicationContext 的使用
ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 都是用于 xml 配置的,主要就是引用配置文件时填写的路径不同,就不需要再演示了,这里我们来说说 AnnotationConfigApplicationContext 的配置方式
AnnotationConfigApplicationContext 是用于加载注解定义标记的类的类定义信息,意思就是我们将 xml 中的 bean 标签,使用 Java 中的注解来代替
代码如下
首先我们要提供一个配置类,这个配置类就相当于我们的 xml 配置文件
public class BeanConfig {
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
配置类中提供一个方法,方法直接返回我们要的对象,且方法上方需要加上 @Bean
注解,加上这个注解就相当于 xml 中的 bean 标签了
之后在测试类中,我们用 AnnotationConfigApplicationContext 指定配置类
public class BeanTest {
public static void main(String[] args) {
//指定配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
UserDaoImpl userDao = context.getBean("userDao", UserDaoImpl.class);
System.out.println("userDao = " + userDao);
}
}
这样就可以实现和 xml 一样的效果了
其中创建的 Bean 的名字,默认就是方法的名称,如果我们需要指定其他的名称,可以使用 @Bean
注解中的value 和 name 属性来指定
我们后面会学习的其他操作,例如作用域 Scope,也可以通过注解来定义
如果 bean 中出现了引用其他 bean 的情况,在注解配置的模式下,直接使用 setter 方法注入即可,不过其他的 bean 必须使用方法参数的方式传递进去,不能调用 bean 的方法,否则无法保证单例
方法参数注入:userService 中的 userDao 和 Spring 容器中的 userDao 是同一个对象
public class BeanConfig {
@Bean
//将需要注入的参数作为方法的参数传递
//Spring 在创建 bean 的时候,如果发现需要参数会自动去容器查找,确保了 userDao 的单例
public UserService userService(UserDao userDao) {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao);
return userService;
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
调用 userDao 方法注入:userService 中的 userDao 不是容器中的对象,是调用了 userDao() 方法后新生成的对象
public class BeanConfig {
@Bean
//userService 中的 userDao 是调用方法新生成的另一个 userDao
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao());
return userService;
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
补充
ApplicationContext 在依赖了其他的 Spring 坐标后,会有更多的实现
四、两者的关系和不同
- BeanFactory 是 Spring 的早期接口,称为 Spring 的 Bean 工厂,ApplicationContext 是后期更高级的接口,称为 Spring 容器
- ApplicationContext 在 BeanFactory 的基础上对功能进行了扩展,例如监听和国际化功能等。BeanFactory 的 API 更偏向于底层。ApplicationContext 的 API 大多是对这些底层的封装。
- Bean 创建的逻辑和功能主要都封装在 BeanFactory中,ApplicationContext 则是继承了 BeanFactory,且 ApplicationContext 内部还维护着 BeanFactory 的引用,所以两者既有继承关系又有融合关系
- 两者创建 Bean 的时机不同,BeanFactory 创建 Bean 的时机是在第一次调用 getBean 方法的时候,而 ApplicationContext 默认则是在配置文件加载的时候,容器就将 Bean 都实例化并初始化
继承关系:
ApplicationContext 是继承了 BeanFactory 的,同时他也继承了像 MessageSource、ResourcePatternResolver 等,相比 BeanFactory 多了更多的功能
融合关系:BeanFactory 是 ApplicationContext 的一个属性
通过打断点我们可以看到,applicationContext 对象中存在属性 BeanFactory,其实现为 DefaultListableBeanFactory