spring 源码学习---IOC相关内容与依赖查找

一 概念

       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等形式注入依赖。

优点:性能高,侵入小

两者区别:区别在于依赖的对象是否为主动获取,是的话,就是依赖查找,否则就是依赖注入,由框架绑定完成。

 

 

  

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值