IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让 容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。
DI(Dependency Injection)依赖注入:就是指对象是被动接受依赖类而不是自己主动去找,换句话说 就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。
依赖查找(DependencyLookup) : 主动根据 spring 提供的api查询相应的bean.
Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用, 其相互关系如下:
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 代理依赖查找)
如果说BeanFactory 是容器中的屌丝,ApplicationContext 应该算容器中的高帅富。 ApplicationContext 是具备应用特性的BeanFactory 超集。
ApplicationContext 是 Spring 提供的一个高级的IOC 容器, ApplicationContext 除了IoC 容器角色,还有提供:
面向切面(AOP) 配置元信息(Configuration Metadata) 资源管理(Resources)事件(Events)国际化(i18n)注解(Annotations)Environment 抽象(Environment Abstraction)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 应该算容器中 的高帅富.。
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(); }1 启动 ——> 运行 ——> 停止
// 创建 BeanFactory 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class) applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class); // 启动应用上下文 applicationContext.refresh(); // 关闭应用上下文 applicationContext.close();应用程序里面还是要调用容器的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); } }单一类型依赖查找接口-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)
代码示例 其他在 上面已经放入代码了。
集合类型依赖查找接口-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"; } }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 所涉及的应用上下文架构。
延迟是指非一次性获得
• 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"; } }BeansException 子类型
异常类型 触发条件(举例) 场景举例NoSuchBeanDefinitionException 当查找Bean 不存在于IoC 容器时 BeanFactory#getBean ObjectFactory#getObject NoUniqueBeanDefinitionException 类型依赖查找时,IoC 容器存在多 个Bean 实例BeanFactory#getBean(Class) BeanInstantiationException当Bean 所对应的类型非具体类时BeanFactory#getBeanBeanCreationException当Bean 初始化过程中 Bean 初始化方法执行异常 时 BeanDefinitionStoreException当BeanDefinition 配置元信息非法 时XML 配置资源无法打开时安全依赖查找(这里安全是指是否会报异常)
内建可查找的依赖
• AbstractApplicationContext 内建可查找的依赖
• 注解驱动Spring 应用上下文内建可查找的依赖(部分)
注解驱动Spring 应用上下文内建可查找的依赖(续)
直接在容器启动时通过构造器,getter setter等形式注入依赖。
优点:性能高,侵入小
两者区别:区别在于依赖的对象是否为主动获取,是的话,就是依赖查找,否则就是依赖注入,由框架绑定完成。