内容简介:在前面的文章浅析Spring 的IoC和DI中简述了 IOC和DI的基本概念和关系,总体上说,IOC 是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式,那么Spring 提供了两种容器类型来提供支持 IOC方式。这两种类型是:ApplicationContext 和 BeanFactory的继承关系如下:可以看到 ApplicationContext 间接继承自 BeanFactory。
在前面的文章浅析Spring 的IoC和DI中简述了 IOC和DI的基本概念和关系,总体上说,IOC 是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式,那么Spring 提供了两种容器类型来提供支持 IOC方式。这两种类型是:
- BeanFactory: 基础类型的IOC容器,提供完整的IOC服务支持
- ApplicationContext: ApplicationContext是在 BeanFactory的基础之上构建的,是相对高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext提供了其他高级特性。
ApplicationContext 和 BeanFactory的继承关系如下:
可以看到 ApplicationContext 间接继承自 BeanFactory。
BeanFactory
BeanFactory的介绍
BeanFactory 是基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用 延迟初始化策略(lazy-load) 。 只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入工作 。
BeanFactory的对象注册
BeanFactory,就是生产 Java Bean 的工厂,作为Spring 提供的基本的IoC容器,BeanFactory 帮助完成 业务对象的注册和对象间依赖关系的绑定 。
实际上,BeanFactory只是一个接口, 它负责定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作 。下面是BeanFactory的接口代码:
package org.springframework.beans.factory; public interface BeanFactory { /** * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四个不同形式的getBean方法,获取实例 */ Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; boolean containsBean(String name); // bean是否存在 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型(多实例) boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;// 名称、类型是否匹配 Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型 String[] getAliases(String name);// 根据实例的名字获取实例的别名 } 复制代码
下面我们来测试下一般情况下 BeanFactory接口的具体实现类情况:
// 实体类 @Component public class Demo { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } //Junit测试类 @RunWith(SpringRunner.class) @SpringBootTest public class ApplicationTests { @Autowired private BeanFactory beanFactory; @Test public void test() { System.out.println("concrete factory is: " + beanFactory.getClass()); Assert.assertTrue("Factory can't be null",beanFactory != null); Demo demo = (Demo) beanFactory.getBean("demo"); System.out.println("Found the demo bean: "+demo.getClass()); } } 复制代码
输出结果如下:
concrete factory is: class org.springframework.beans.factory.support.DefaultListableBeanFactory Found the demo bean: class com.pjmike.spring.Demo 复制代码
从结果可以看出,具体工厂是 org.springframework.beans.factory.support.DefaultListableBeanFactory
的实例。再来看看 BeanFactory
的继承体现:
从上图可以看出,BeanFactory有三个直接子类:
- ListableBeanFactory: 通过继承该接口可以列出所有的Bean,也可以只列出与预期类型相对应的bean
- HierarchicalBeanFactory: 支持分层bean的管理,使BeanFactory支持双亲IOC容器的管理功能
- AutowireCapableBeanFactory: 可以填充不受Spring 控制的 Bean
而三个类的子类体系就更多,详细的参考 Spring 源码。
再来看看之前提到的 DefaultListableBeanFactory
,它也是上图中最底层的实现类:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { ... } 复制代码
这个类其实就是 BeanFactory
的默认实现类,一个比较通用的BeanFactory实现类,它除了间接实现 BeanFactory接口外,还实现了 BeanDefinitionRegistry 接口, 该接口才是BeanFactory实现中担任 Bean注册管理的角色 ,它抽象的定义了Bean注册的逻辑,当然具体的是实现还是靠 DefaultListableBeanFactory 这等实现类。
ApplicationContext
ApplicationContext的介绍
ApplicationContext是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有 BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如:
- 统一资源加载策略
- 国际化信息支持
- 容器内部事件发布机制
在ApplicationContext 容器启动之后,默认全部初始化并绑定完成,所以,对于BeanFactory来说,ApplicationContext 往往要求更多的系统资源
ApplicationContext的实现
Spring 中的 Context
Spring 为基本的 BeanFactory 类型容器提供了 XmlBeanFactory 实现(继承自DefaultListableBeanFactory),相应的,它也为 ApplicationContext 类型容器提供了以下几个常用的实现:
org.springframework.context.support.FileSystemXmlApplicationContext org.springframework.context.support.ClassPathXmlApplicationContext org.springframework.web.context.support.XmlWebApplicationContext
在传统的基于 XML的Spring项目中,经常会使用到上面的实现类
SpringBoot 中的 Context
在官方文档中给出对于一个 SpringBoot 应用它对应的Context的情况:
AnnotationConfigServletWebServerApplicationContext AnnotationConfigReactiveWebServerApplicationContext AnnotationConfigApplicationContext
以上的 context
实际上也是实现了 ApplicationContext
接口
ApplicationContext 的简单实践
我们都知道IOC容器一般有两种对象注入方式:基于XML配置文件 与 基于注解驱动的方式。下面就分别从这两个角度来看如何使用 ApplicationContext
基于 XML 配置文件
- 定义个实体类
public class User { private Integer id; private String username; public User(Integer id, String username) { this.id = id; this.username = username; } public User() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } 复制代码
- 设置一个XML配置文件,声明 User Bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.pjmike.spring.domain.User"> <constructor-arg name="id" value="1"/> <constructor-arg name="username" value="pjmike"/> </bean> </beans> 复制代码
- 主程序
public class XmlBootStrap { public static void main(String[] args) { //构建一个 ApplicationContext 上下文 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); //设置此应用上下文的配置路径 context.setConfigLocations("classpath:/META-INF/spring/context.xml"); //调用 refresh 方法,完成配置的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造 context.refresh(); User user = context.getBean("user", User.class); System.out.print("user.getName() = "+ user.getUsername()); } } 复制代码
输出结果
user.getName() = pjmike 复制代码
基于注解方式
- 声明一个配置类
@Configuration public class UserConfiguration { @Bean(name = "user") public User user() { User user = new User(); user.setUsername("pj"); return user; } } 复制代码
- 主程序
public class AnnotationBootStrap { public static void main(String[] args) { // 构建一个 ApplicationContext 应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //注册一个配置 Bean context.register(UserConfiguration.class); // 调用 refresh 启动容器 context.refresh(); User user = context.getBean("user", User.class); System.out.println("user.getName() = "+user.getUsername()); } } 复制代码
输出结果
user.getName() = pj 复制代码
XML 与 Annotation 简单对比
从上面的两个例子可以看出基于XML和基于注解注入Bean 的方式是不一样的,基于XML的应用上下文 ClassPathXmlApplicationContext
需要设置配置路径,基于注解的应用上下文 AnnotationConfigApplicationContext
需要注册一个配置Bean,但它们相同的一步就是必须要调用 refresh()
方法,该方法可以看做是IOC容器的启动方法,它会做很多操作,比如完成配置的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等,不调用它,容器就无法启动。这里只是简要说明,更加详细的介绍会在后面的文章介绍。
现在是springboot盛行的阶段,基于XML配置文件的方式已经逐步被基于注解的方式所取代,如今的项目中,更多的使用 注解的方式。 关于XML与注解更详细的对比可以参阅开涛大神的文章: jinnianshilongnian.iteye.com/blog/187991…
小结
上面的文章比较简单的总结了 BeanFactory 和 ApplicationContext,为后续分析Spring IOC详细的初始化过程、Spring Bean的加载等做一个铺垫
参考资料 & 鸣谢
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 无根容器浅析
- 浅析 Docker 容器安全管控方法
- 浅析Docker容器安全管控方法
- 浅析Spring Framework框架容器启动过程
- 浅析依赖倒转、控制反转、IoC 容器、依赖注入。
- C++中vector容器大小增长规律浅析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。