一 概念
IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让 容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。
DI(Dependency Injection)依赖注入:就是指对象是被动接受依赖类而不是自己主动去找,换句话说 就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。
依赖查找(DependencyLookup) : 主动根据 spring 提供的api查询相应的bean.
二、 核心容器
Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用, 其相互关系如下:
2.1、 BeanFactory
BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范。
BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。 但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接 口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合, 它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对象的数据访问所做的限制。
ListableBeanFactory: 接口表示这些 Bean 是可列表的,依赖查找 bean的集合。
HierarchicalBeanFactory :有层次的bean,父容器类似双亲委派机制。表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。
AutowireCapableBeanFactory:接口定义 Bean 的自动装配规则。
DefaultListableBeanFactory : 默认实现的
DefaultListableBeanFactory 实现的设计模式有:
抽象工厂(BeanFactory 接口实现)
组合模式(组合 AutowireCandidateResolver 等实例)
单例模式(Bean Scope)
原型模式(Bean Scope)
模板模式(模板方法定义:AbstractBeanFactory)
适配器模式(适配 BeanDefinitionRegistry 接口)
策略模式(Bean 实例化)
代理模式(ObjectProvider 代理依赖查找)
2.2、 ApplicationContext
如果说BeanFactory 是容器中的屌丝,ApplicationContext 应该算容器中的高帅富。 ApplicationContext 是具备应用特性的BeanFactory 超集。
ApplicationContext 是 Spring 提供的一个高级的IOC 容器, ApplicationContext 除了IoC 容器角色,还有提供:
- 面向切面(AOP)
- 配置元信息(Configuration Metadata)
- 资源管理(Resources)
- 事件(Events)
- 国际化(i18n)
- 注解(Annotations)
- Environment 抽象(Environment Abstraction)
2.3、 具体实现
BeanFactory 里只对 IOC 容器的基本行为作了定义,根本不关心你的 Bean 是如何定义怎样加载的。 正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。
public interface BeanFactory {
//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;
//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
而要知道工厂是如何产生对象的,我们需要看具体的 IOC 容器实现,Spring 提供了许多 IOC 容器 的实现。比如 XmlBeanFactory,ClasspathXmlApplicationContext 、AnnotationConfigApplicationContext等。其中 XmlBeanFactory 就 是针对最基本的 IOC 容器的实现,这个 IOC 容器可以读取 XML 文件定义的 BeanDefinition(XML 文件中对 bean 的描述),如果说 XmlBeanFactory 是容器中的屌丝,ClasspathXmlApplicationContext 应该算容器中 的高帅富.。
2.4 、具体代码
1、BeanFactory 作为 IoC 容器示例
/**
* {@link BeanFactory} 作为 IoC 容器示例
*
* @author
* @since
*/
public class BeanFactoryAsIoCContainerDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件 ClassPath 路径
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载配置
int beanDefinitionsCount = reader.loadBeanDefinitions(location);
System.out.println("Bean 定义加载的数量:" + beanDefinitionsCount);
// 依赖查找集合对象
lookupCollectionByType(beanFactory);
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
}
2、ApplicationContext 作为 IoC 容器示例
/**
* 注解能力 {@link ApplicationContext} 作为 IoC 容器示例
*
* @author
* @since
*/
@Configuration
public class AnnotationApplicationContextAsIoCContainerDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class)
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupCollectionByType(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
/**
* 通过 Java 注解的方式,定义了一个 Bean
*/
@Bean
public User user() {
User user = new User();
user.setId(1L);
user.setName("坤仔");
return user;
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
}
其中 下面两段代码相等
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
applicationContext.refresh();
AnnotationConfigApplicationContext applicationContext1 =
new AnnotationConfigApplicationContext(AnnotationApplicationContextAsIoCContainerDemo.class);
器底层实现:
public AnnotationConfigApplicationContext(Class... componentClasses) {
this();
this.register(componentClasses);
this.refresh();
}
2.5、容器生命周期
1 启动 ——> 运行 ——> 停止
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
// 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class)
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 关闭应用上下文
applicationContext.close();
三 、依赖查找 DependencyLookup
应用程序里面还是要调用容器的bean查找接口查找bean实例。 缺点:还是有侵入性,性能低。
注意: 延迟查找的意思: 延迟是指非一次性获得
代码示例:
/**
* 依赖查找示例
* 1. 通过名称的方式来查找
*
* @author
* @since
*/
public class DependencyLookupDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 按照名字查找
lookupByName(beanFactory);
// 按照类型查找
lookupByType(beanFactory);
// 按照类型查找集合对象
lookupCollectionByType(beanFactory);
// 通过注解查找对象
lookupByAnnotationType(beanFactory);
// 使用 ObjectFactory实现延迟查找
lookupInLazy(beanFactory);
}
//按照名字查找
private static void lookupByName(BeanFactory beanFactory) {
User user = (User)beanFactory.getBean("user");
System.out.println("按照名字实时查找:" + user);
}
//按照类型查找
private static void lookupByType(BeanFactory beanFactory) {
User user = beanFactory.getBean(User.class);
System.out.println("按照类型实时查找:" + user);
}
/**
* 按照类型查找集合对象
* 根据 User能查询出来两个bean
* 使用 ListableBeanFactory
* @param beanFactory
*/
private static void lookupCollectionByType(BeanFactory beanFactory) {
if(beanFactory instanceof ListableBeanFactory ){
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
/**
* 根据注解查找 查找标注注解为@Super 的bean
* @param beanFactory
*/
private static void lookupByAnnotationType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
}
}
/**
* 使用 ObjectFactory实现延迟查找
* @param beanFactory
*/
private static void lookupInLazy(BeanFactory beanFactory) {
ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
User user = objectFactory.getObject();
System.out.println("延迟查找:" + user);
}
}
3.1 单一类型查找 BeanFactory
单一类型依赖查找接口-BeanFactory
• 根据Bean 名称查找
• getBean(String)
• Spring 2.5 覆盖默认参数:getBean(String,Object...)
• 根据Bean 类型查找
• Bean 实时查找
• Spring 3.0 getBean(Class)
• Spring 4.1 覆盖默认参数:getBean(Class,Object...)
• Spring 5.1 Bean 延迟查找
• getBeanProvider(Class)
• getBeanProvider(ResolvableType)
• 根据Bean 名称+ 类型查找:getBean(String,Class)
代码示例 其他在 上面已经放入代码了。
3.2、集合类型依赖查找接口-ListableBeanFactory
集合类型依赖查找接口-ListableBeanFactory
• 根据Bean 类型查找
• 获取同类型Bean 名称列表
• getBeanNamesForType(Class)
• Spring 4.2 getBeanNamesForType(ResolvableType)
• 获取同类型Bean 实例列表
• getBeansOfType(Class) 以及重载方法
• 通过注解类型查找
• Spring 3.0 获取标注类型Bean 名称列表
• getBeanNamesForAnnotation(Class<? extends Annotation>)
• Spring 3.0 获取标注类型Bean 实例列表
• getBeansWithAnnotation(Class<? extends Annotation>)
• Spring 3.0 获取指定名称+ 标注类型Bean 实例
• findAnnotationOnBean(String,Class<? extends Annotation>)
接口方法:
获取同类型Bean 名称列表 :getBeanNamesForType(Class) 根据类型查找beanName,其实是从BeanDefinition 查找类型去匹配,BeanDefinition特点bean的定义原信息并不是bean已经被初始化了。
获取同类型Bean 实例列表 :getBeansOfType(Class) ,可能会导致bean提前初始化,导致信息不全出现问题。
所以一般在判断时候首先要根据名字去判断,在根据类型去判断。
@Configuration
public class ObjectProviderDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 BeanDefinition 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
applicationContext.register(ObjectProviderDemo.class);
//启动 容器上下文
applicationContext.refresh();
// 按照类型查找集合对象
lookupCollectionByType(applicationContext);
//关闭
applicationContext.close();
}
private static void lookupCollectionByType(AnnotationConfigApplicationContext applicationContext) {
if(applicationContext instanceof ListableBeanFactory){
ListableBeanFactory listableBeanFactory = applicationContext;
String[] resultString = listableBeanFactory.getBeanNamesForType(String.class);
Map<String, String> result = listableBeanFactory.getBeansOfType(String.class);
Arrays.asList(resultString).forEach(System.out::println);
System.out.println("查找到的所有的 String 集合对象:" + result);
}
}
@Bean
public String helloWorld() { // 方法名就是 Bean 名称 = "helloWorld"
return "Hello,World";
}
@Bean
public String message() {
return "Message";
}
}
3.3、层次性依赖查找接口-HierarchicalBeanFactory
HierarchicalBeanFactory接口提供 获取父容器的方法, 这个机制类似与双亲委派机制。只有继承他的都要层次结构。
子接口:ConfigurableBeanFactory:可配置的接口。
ConfigurableListableBeanFactory接口 是使用组合模块。他具有 集合、层次、自动注入的功能。
AbstractBeanFactory中containsBean也有对父容器的检查,递归的实现。
HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory
实例代码
/**
* 层次性依赖查找示例
* Created by dukun on 2020/7/2.
*/
@Configuration
public class HierarchicalDependencyLookupDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
applicationContext.register(ObjectProviderDemo.class);
// 1. 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
// 2. 设置 Parent BeanFactory
HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
beanFactory.setParentBeanFactory(parentBeanFactory);
//判断本地容器是否包含 bean
displayContainsLocalBean(beanFactory, "user");//这个应该是不包含
displayContainsLocalBean(parentBeanFactory, "user");//这个本地容器包含
System.out.println("==============================================");
displayContainsBean(beanFactory, "user");
}
//判断本地容器是否包含 bean
private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name : %s] : %s\n", beanFactory, beanName,
beanFactory.containsLocalBean(beanName));
}
//判断是否bean
private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name : %s] : %s\n", beanFactory, beanName,
containsBean(beanFactory, beanName));
}
//递归判断父容器或者本地容器是否包含bean
private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
if (containsBean(parentHierarchicalBeanFactory, beanName)) {
return true;
}
}
return beanFactory.containsLocalBean(beanName);
}
//创建父容器
private static HierarchicalBeanFactory createParentBeanFactory() {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件 ClassPath 路径
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载配置
reader.loadBeanDefinitions(location);
return beanFactory;
}
}
问题:基本都是单一查找,依赖查找可以在LB这种场景应用,但这个分层查找有啥用?主要复用bean
比如 Spring MVC 中,Biz 组件放在 Root ApplicationContext,而 Web 组件放在 DispatcherServlet 的 ApplicationContext,后者是前者的子 ApplicationContext,所以,子 ApplicationContext 可以读取父 ApplicationContext。
容器的层次关系主要的目的是实现 Bean 复用,假设一个应用存在一个 Root ApplicationContext,内部的 Bean 来自于一个 jar 包,那么,这个jar 包可能被两个不同的 Servlet 应用使用,这时,ServletContext A 和 ServletContext B 同时复用了这个 parent ApplicationContext,而它自己也有 ApplicationContext,这也是 Spring Web MVC 所涉及的应用上下文架构。
3.4、Bean 延迟依赖查找接口
延迟是指非一次性获得
• org.springframework.beans.factory.ObjectFactory
• org.springframework.beans.factory.ObjectProvider
• Spring 5 对Java 8 特性扩展
• 函数式接口
• getIfAvailable(Supplier)
• ifAvailable(Consumer)
• Stream 扩展-stream()
getBeanProvider方法 为ObjectProvider 中的 其又继承 ObjectFactory。
延迟查找(延迟是指非一次性获得 ):主要用于暂时性地获取某个 Bean Holder 对象,如果过早的加载,可能会引起未知的状态,比如,当 A bean 依赖 B bean 时,如果过早地初始化 A,那么 B bean 里面的状态可能是中间状态,这样容易导致一些错误。
ObjectProvider 对象获取时,并没有获取实际 Bean 的对象,而是在 getObject() 或其他获取方法时才获取。
/**
* 通过 {@link ObjectProvider} 进行依赖查找
* Created by dukun on 2020/7/2.
*/
@Configuration
public class ObjectProviderDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 BeanDefinition 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
applicationContext.register(ObjectProviderDemo.class);
//启动 容器上下文
applicationContext.refresh();
//单一类型的延迟查找
lookupByObjectProvider(applicationContext);
//关闭
applicationContext.close();
}
//ObjectProvider 继承 ObjectFactory
private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
System.out.println(objectProvider.getObject());
}
private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
objectProvider.stream().forEach(System.out::println);
}
@Bean
public String helloWorld() { // 方法名就是 Bean 名称 = "helloWorld"
return "Hello,World";
}
}
3.6 依赖查找中的经典异常
BeansException 子类型
异常类型 | 触发条件(举例) | 场景举例 |
---|---|---|
NoSuchBeanDefinitionException | 当查找Bean 不存在于IoC 容器时 | BeanFactory#getBean ObjectFactory#getObject |
NoUniqueBeanDefinitionException | 类型依赖查找时,IoC 容器存在多 个Bean 实例 | BeanFactory#getBean(Class) |
BeanInstantiationException | 当Bean 所对应的类型非具体类时 | BeanFactory#getBean |
BeanCreationException | 当Bean 初始化过程中 | Bean 初始化方法执行异常 时 |
BeanDefinitionStoreException | 当BeanDefinition 配置元信息非法 时 | XML 配置资源无法打开时 |
安全依赖查找(这里安全是指是否会报异常)
内建可查找的依赖
• AbstractApplicationContext 内建可查找的依赖
• 注解驱动Spring 应用上下文内建可查找的依赖(部分)
注解驱动Spring 应用上下文内建可查找的依赖(续)
四、Spring IoC 依赖注入
直接在容器启动时通过构造器,getter setter等形式注入依赖。
优点:性能高,侵入小
两者区别:区别在于依赖的对象是否为主动获取,是的话,就是依赖查找,否则就是依赖注入,由框架绑定完成。