zoukankan      html  css  js  c++  java
  • Spring-IOC总结

    1 Spring IOC是什么

    1.1 Spring IOC是个什么东西

    IOC是用为用户创建、管理实例对象的。用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建,从而达到与具体类解耦。 

    简单点来讲就是Spring IOC就是一个Map集合,对象的名字就是集合中的key,值就是对应的对象。我们可以通过一个对象的名字到集合中获取对象。

    1.2 IOC实现的流程

    1. Bean定义

    Spring提供了多种方式来配置Bean定义,有Xml,JavaConfig

    Xml:

    <bean id="user" class="com.ranger.bean.User">
        <constructor-arg type="String" value="ranger"></constructor-arg>
        <constructor-arg ref="cbean"></constructor-arg>
    </bean>
    <bean id="car" class="com.ranger.bean.Car">
        <constructor-arg type="String" value="mazda"></constructor-arg>
    </bean>
    

    JavaConfig的方法,这种方式要和注解配合

    @Configuration
    public class AppConfig {
    
        @Bean
        public Service myService() {
            return new ServiceImpl();
        }
    
    }
    
    1. Spring读取Bean的定义

    创建SpringIOC容器的时候指定一个配置文件(xml),或者指定包扫描的路径(JavaConfig)

    // 通过指定classpath下的配置文件 
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");
    
    

    或者使用JavaConfig的方式

    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(AppConfig.class);
    
    annotationConfigApplicationContext.refresh();
    System.out.println(annotationConfigApplicationContext.getBean(User.class));
    
    

    2. 从入口ApplicationContext开始

    大概介绍每个父接口对应的职责

    • EnvironmentCapable:获取环境变量相关的参数
    • HierarchicalBeanFactory:提供父子容器功能
    • ListableBeanFactory:BeanFactory的实现
    • ApplicationEventPublisher:时间的发布
    • ResourcePatternResolver:加载Resource文件
    • MessageSource:提供国际化功能

    ApplicationContext中定义的方法

    ApplicationContext中定义了自己的几个方法,对这几个方法做简单的介绍:

    • getApplicationName:返回容器所属的应用的名称, 默认返回空字符串
    • getAutowireCapableBeanFactory:暴露当前容器的AutowireCapableBeanFactory
    • getDisplayName:返回一个友好的容器名字
    • getId:返回当前容器的唯一Id
    • getParant:返回父容器,没有的话返回null
    • getStartupDate:返回容器第一次加载完成的时间戳

    2.1 Application的子类

    2.1.1 AbstarctApplicationContext 的子类

    我们先从ApplicationContext后面的子孙开始

    从上面的类继承图可以看出,AbastractApplicationContext 后面有两个儿子,分别是 GernericApplicationContextAbstractRefreshableApplicationContext.这两个大儿子分别都持有了BeanFactory实例,这两个儿子所有的对于BeanDefiniton的注册,Bean的实例化都是基于这个BeanFactory实例的

    它们分成了两大派系

    • AbstractRefreshableApplicationContext:这个类在每次调用refresh方法的时候都会产生一个新的beanfactory实例(通常是,但是不是必须的)。这个应用上下文会通过一系列的配置文件去加载BeanDefinition。在调用refresh方法的时候才会创建内部持有的BeanFacoty实例(可以参见该类中的refreshBeanFactory方法)

    • GenericApplicationContext:这个类内部持有唯一的一个DefaultListableBeanFactory实例,而且相较于其它ApplicationContext的实现类,这个类在创建的时候就会有一个BeanFactory的实例,意思就是在refresh方法调用前,内部持有的BeanFactory实例就已经创建,且这个类从开始到最终都是一个BeanFacoty实例。

    GenericApplicationContext实现了BeanDefinitionRegistry这个接口,这个接口干啥的呢,看名字是BeanDefinition的注册什么东东,没错,这个就是用来添加删除BeanDefiniton的。GenericApplicationContext也还有几个儿子,后面会简单分析一下他们的不同。

    2.1.2 AbstractApplicationContext抽象类简单说明

    上面都提到了refresh方法,这个方法是AbastractApplicationContext实现的,用于配置Context.该类使用了模板方法模式,很多方法都留给了子类去实现。

    在AbstractApplicationContext中,实现了大多数ApplicationContext接口从BeanFactroy接口继承来的方法。

    我们还可以看到,AbstractApplicationContext的父类是ConfigurableApplicationContext,这个类提供了配置Context的方法,还提供了生命周期方法。

    3 BeanDefinition的加载

    通过上面的分析我们看到,虽然都有一个共同的祖先叫做ApplicationContext,但是不同的子孙还是有不同的加载BeanDefinition的方法,但是其它方面都是一样的,SpringIOC中最重要的方法refresh就在他们共同的老爸AbstractApplicationContext中。refresh方法会根据BeanDefinition来创建Bean对象(除开lazy loading)

    我们就从我们常见的ClasspathXmlApplicationContext开始分析:

    现在项目中包含上面图中的类和配置,我们编写一个主方法:

    public class Application {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");
    
            Person person = applicationContext.getBean(Person.class);
            System.out.println(person);
    
        }
    }
    
    

    springcontext.xml

    <?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-3.0.xsd"
           default-lazy-init="true" default-init-method="" default-destroy-method="">
    
        <bean id="car" class="com.ranger.spring.ioc.bean.Car">
            <constructor-arg name="brand" value="mazda"></constructor-arg>
        </bean>
    
        <bean id="person" class="com.ranger.spring.ioc.bean.Person">
            <property name="car" ref="car"></property>
        </bean>
    </beans>
    
    

    调试启动Application类的main方法

    因为最终BeanDefinition的注册都是在DefaultListableBeanFactory 中完成的,所以在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)打断点就能看到下面的调用栈

    从线程调用栈的下面向上看。

    得到一个基本的流程:

    这里面的解析Document获取BeanDefinition也比较复杂,如果有兴趣可以去看看。

    那么GenericXmlApplicationContext加载BeanDefinition的流程是不是也和上面一样呢。

    可以看到流程是一样的。

    只是GenericXmlApplicationContext会先调用load来加载BeanDefinition,然后调用refresh完成配置。

    而ClasspathXmlApplicationContext会在refresh方法调用的时候完成BeanDefinition的加载。

    4 bean工厂-DefaultListableBeanFactory

    通过前面的分析我们可以看到,ApplicationContext的大部分操作其实都是基于DefaultListableBeanFactory来完成的。

    DefaultListableBeanFactory是BeanFactory的一个实现类

    现在我们来认识一下它:

    1. 先看看最上面的祖先BeanFactory

    通过读源码的doc,

    • 这个接口是spring bean容器的根接口,它有一些为了提供特定功能的子接口ListableBeanFactory和ConfigurableBeanFactory

    • 实现这个接口的对象持有一系列的 bean definitions,每个bean definition 都有一个唯一的字符串名字。返回的Bean可以是单例的,也可以是独立的(每次都要创建),具体返回什么类型取决于applicationcontext的配置。

    • BeanFactory通过依赖注入来完成配置,通常的手段是用setter或者constructor

    • 通常情况BeanFactory加载的BeanDefinition保存在一个配置资源中,比如XML文件。但是具体存储在哪儿是没有限制的,比如LDAP,XML,properties等等。

    • HierarchicalBeanFactory会先从本上下文找,找不到从父BeanFactory找,且本工厂实例中的bean会覆盖父工厂

    • BeanFactory的实现类应该尽可能支持bean的生命周期方法,比如BeanNameAware,BeanClassLoaderAware,等等。

      对于这些生命周期方法的支持,BeanFacoty没有给出抽象的接口,需要实现类自己去实现

    BeanFactory的源码:

    public interface BeanFactory {
    
        // 用来区分FactoryBean和其产生的对象
    	String FACTORY_BEAN_PREFIX = "&";
    
    	// 通过BeanName获取Bean
    	Object getBean(String name) throws BeansException;
    
    	// 通过beanName和bean 的Class类型来获取Bean
    	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    
    	// 增加获取bean的参数
    	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
    	boolean containsBean(String name);
    
    	// bean是否是单例
    	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    	// bean是否是prototype
    	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    	//查询指定了名字的Bean的Class类型是否与指定类型匹配
    	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);
    
    }
    

    BeanFactory 有三个子类接口:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory,还有一个实现类SimpleJndiBeanFactory。

    这里对于BeanFactory的体系介绍就先不说了,太多了,单独写吧。

    5 后记

    ApplicationContext的体系很大,重要抓住了核心几个比较重要的几个类:AbstractApplicationContext,以及它的两个大儿子 GenericApplicationContextAbstractRefreshableApplicationContext。大部分功能都在这里面实现了。

    两个大儿子生下的儿子都是基于他们做了一些扩展。

    阅读代码可以发现,ApplicationContext很多的方法都留到了子类去实现,这里用到了模板方法设计模式。

    最终对于注册BeanDefinition和基于BeanDefinition创建bean实例都是归结到了DefaultListableBeanFactory中。

    前面对Spring容器的体系做了整体的了解,接下来会分析bean的创建。

  • 相关阅读:
    OpenCV里面的一些常用函数
    c++ 里面的字符类型转换
    互斥研究
    git 命令
    pipe的操作
    二叉树总结(五)伸展树、B-树和B+树
    二叉树总结(四)平衡二叉树
    二叉树总结(三)二叉搜索树
    [LeetCode]Construct Binary Tree from Preorder and Inorder Traversal
    二叉树总结(一)概念和性质
  • 原文地址:https://www.cnblogs.com/watertreestar/p/12686973.html
Copyright © 2011-2022 走看看