zoukankan      html  css  js  c++  java
  • Spring IOC

    前言

    IOC 全称为 Inversion of Control(控制反转),通过反射技术,将 Bean 注入 IOC容器,由 Spring IOC 容器来负责管理对象的生命周期和对象之间的依赖关系

    依赖注入(DI),就是由 IOC容器 在运行期间,动态地将某种依赖关系注入到对象之中

    依赖注入(DI)和控制反转(IOC)是从不同的角度的描述同一件事情:通过将 Bean 引入 IOC容器,利用依赖关系注入的方式,实现对象之间的解耦

    IOC 是把以前工厂方法里写死的对象生成代码,改变为由配置文件来定义,把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性

    主要依赖源码是 spring-beans 和 spring-context 两个包

    ContextLoaderListener

    Spring 初始化的入口在 ContextLoaderListener。Spring 为我们提供的 IOC容器,需要指定容器的配置文件,然后由该监听器初始化并创建该容器。
    
    Spring 通常可以在 web.xml 配置:
    
        <context-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>/WEB-INF/applicationContext.xml</param-value>  
        </context-param> 
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    ContextLoaderListener 本质上是创建了一个 WebApplicationContext,自动装载 ApplicationContext 的配置信息。
    ContextLoaderListener 是实现了 javax.servlet.ServletContextListener 接口的服务器端程序,
    随 web 应用的启动而启动,只初始化一次,随 web 应用的停止而销毁。
    
    Spring IOC 容器先根据监听初始化 WebApplicationContext,然后再初始化 web.xml 中其他配置的 servlet,
    并加载其设置的配置信息和参数信息到上下文中(初始化上下文信息 servletContext),
    然后将 WebApplicationContext 设置为 servletContext 的父容器。
    

    依赖注入三种方法

    构造器注入:
    
        index 是索引,指定注入的属性,从0开始
        type 是指该属性所对应的类型
        ref 是指引用的依赖对象
        value 当注入的不是依赖对象,而是基本数据类型时,就用value
    
        被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。
    
        YoungMan(BeautifulGirl beautifulGirl){
            this.beautifulGirl = beautifulGirl;
        }
    
        xml方式:
    
            <!-- 注册A -->
            <bean id="a" class="spring.A">
                <constructor-arg ref="b"></constructor-arg>
    
                <constructor-arg index="0">  
                    <list>  
                        <ref bean="user1"/>  
                        <ref bean="user2"/>  
                        <value>张三</value>
                    </list>  
                </constructor-arg>
            </bean>
    
            <!-- 注册B -->
            <bean id="b" class="spring.B"></bean>
    
        构造器注入方式比较直观,对象构造完毕后就可以直接使用。
    
        解决循环依赖问题:一个构造器注入,一个setter注入。
    
    
    setter 方法注入:
    
        对于 JavaBean 对象而言,我们一般都是通过 getter 和 setter 方法来访问和设置对象的属性。
        通过set方法注入属性,那么Spring会通过默认的无参构造方法来实例化对象,
        所以如果在类中重写带有参数的构造方法,一定要把无参构造方法也写上,否则spring没有办法实例化对象,导致报错。
    
        public class YoungMan {
            private BeautifulGirl beautifulGirl;
            public void setBeautifulGirl(BeautifulGirl beautifulGirl) {
                this.beautifulGirl = beautifulGirl;
            }
        }
    
        <!-- 注册A -->
        <bean id="a" class="spring.A">
            <!-- 写法一 -->
            <!-- <property name="UserName" ref="b"></property> -->
            <!-- 写法二 -->
            <property name="userName" ref="b"></property>
    
            <property name="cars">
                <list>
                    <ref bean="car1"/>
                </list>
            </property>
        </bean>
    
        <!-- 注册B -->
        <bean id="b" class="spring.B"></bean>
    
    
    基于注解的注入:
    
        bean的属性autowire,autowire主要有三个属性值:byName,byType。
    
            byName:
                被注入bean的id<名称>必须与set方法后半截匹配,并且id名称的第一个单词的首字母必须小写。
    
            byType:
                查找所有的set方法,将符合参数<类型>的bean注入。
    
        注册bean的注解有以下几种:
    
            @Component:用于注册所有的bean
            @Repository:用于注册dao层的bean
            @Controller:用于注册控制层的bean
            @Service:用于注册服务层的bean
    
        @Resource和@Autowired之间的区别
    
            @Resource(name="userDao"):
                java的注解,如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
                如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
                如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
                如果既没有指定name,又没有指定type,则自动按照byName方式进行装配,
                如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
    
            @Autowired:
                spring注解,默认是以byType的方式去匹配类型相同的bean,
                如果匹配到一个,那么就直接注入该bean,
                如果byType查找到多个的话,使用@Qualifier("userDao")注解指定某个具体名称的bean,
                没有找到的话就抛出异常。
                依赖对象必须存在,如果允许null值,可以设置它的required属性为false(@Autowired(required=false))
    

    启动 Spring 容器

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
    
    FileSystemXmlApplicationContext:
        构造函数需要一个 xml 配置文件在系统中的路径,其他和 ClassPathXmlApplicationContext 基本上一样。
    
    AnnotationConfigApplicationContext:
        基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置。
    

    ClassPathXmlApplicationContext 源码分析

    public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    
        //如果已经有 ApplicationContext,并需要配置成父子关系
        public ClassPathXmlApplicationContext(ApplicationContext parent) {
            super(parent);
        }
    
        public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent) throws BeansException {
            super(parent);
            Assert.notNull(paths, "Path array must not be null");
            Assert.notNull(clazz, "Class argument must not be null");
            this.configResources = new Resource[paths.length];
            for(int i = 0; i < paths.length; ++i) {
                this.configResources[i] = new ClassPathResource(paths[i], clazz);
            }
    
            this.refresh();    //核心方法
        }
    }
    

    refresh() 初始化 Bean容器

    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    
        /**
         * 初始化 Bean容器
         */
        public void refresh() throws BeansException, IllegalStateException {
            synchronized(this.startupShutdownMonitor) {
    
                //准备工作,准备新的上下文环境、记录下容器的启动时间、标记“已启动”状态、执行一些属性的初始化。
                this.prepareRefresh();
    
                /**
                 * 创建 BeanFactory:DefaultListableBeanFactory
                 * 读取 Spring 配置文件,验证配置文件的内容,并封装成 Resource,根据 Resource 加载 XML 配置文件,并解析成 Document 对象
                 * 拿到 Document 中的根节点,遍历根节点和所有子节点。根据命名空间,进行不同的解析,将 bean 节点内容解析成 BeanDefinition
                 * 加载到 BeanFactory 中(将 BeanDefinition 注册到注册表中(也就是beanDefinitionNames、beanDefinitionMap、aliasMap缓存))
                 */
    
                /**
                 * "加载到 BeanFactory 中"的内容主要指的是添加到以下3个缓存:
                 *     beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合
                 *     beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射
                 *     aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。
                 */
                ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
    
                //设置 BeanFactory 的类加载器,添加 BeanPostProcessor,手动注册特殊的bean
                this.prepareBeanFactory(beanFactory);
    
                try {
                    this.postProcessBeanFactory(beanFactory);
    
                    //BeanFactoryPostProcessor 是针对 BeanFactory 的扩展,主要用在 bean 实例化之前,读取 bean 的定义,并可以修改它。
                    this.invokeBeanFactoryPostProcessors(beanFactory);
    
                    //BeanPostProcessor 是针对 bean 的扩展,主要用在 bean 实例化之后,允许开发者对 bean 实例进行修改。
                    this.registerBeanPostProcessors(beanFactory);
    
                    //初始化当前 ApplicationContext 的 MessageSource,用于国际化
                    this.initMessageSource();
    
                    /**
                     * 初始化当前 ApplicationContext 的事件广播器(ApplicationEvent),使用了一个标准的观察者模式:
                     *     对于applicationEventMulticaster内部的监听者applicationListeners,每次事件到来都会一一获取通知。
                     *     如果系统有需要广播的情况下,会发送一个 applicationEvent 事件,
                     *     注册的listener会根据自己关心的类型进行接收和解析。
                     */
                    this.initApplicationEventMulticaster();
    
                    //模板方法,提供给子类扩展实现,可以重写以添加特定于上下文的刷新工作,默认实现为空(在实例化 singleton beans 之前)
                    this.onRefresh();
    
                    //注册监听器,与广播器是同时存在的。Spring初始化广播器,但是并没有为广播器绑定Listener。Spring在此方法中进行了绑定。
                    this.registerListeners();
    
                    //实例化所有的 singleton beans(lazy-init(懒加载)除外)
                    this.finishBeanFactoryInitialization(beanFactory);
    
                    //广播事件,表示 ApplicationContext 初始化完成
                    this.finishRefresh();
    
                } catch (BeansException var9) {
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                    }
    
                    //销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源。
                    this.destroyBeans();
    
                    this.cancelRefresh(var9);
                    throw var9;
                } finally {
                    this.resetCommonCaches();
                }
            }
        }
    
    
        /**
         * 创建 Bean容器(Bean 并没有实例化)
         */
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    
            this.refreshBeanFactory();    //关闭旧的 BeanFactory,创建新的 BeanFactory
    
            return this.getBeanFactory();    //返回刚刚创建的 BeanFactory
        }
    
        protected final void refreshBeanFactory() throws BeansException {
            if (this.hasBeanFactory()) {
    
                this.destroyBeans();    //如果 ApplicationContext 中已经加载过 BeanFactory,销毁所有 Bean
                this.closeBeanFactory();    //关闭 BeanFactory
            }
    
            try {
                DefaultListableBeanFactory beanFactory = this.createBeanFactory();    //创建并初始化BeanFactory实现类的实例
    
                beanFactory.setSerializationId(this.getId());    //序列化
    
                this.customizeBeanFactory(beanFactory);    //设置 BeanFactory 的两个配置属性:是否允许 BeanDefinition 覆盖、是否允许循环引用
    
                //BeanDefinition 中保存了 Bean 信息
                //比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean
                //初始化 Bean容器(配置文件的配置信息转换为一个个 BeanDefinition,然后注册各个 BeanDefinition 到 BeanFactory)
                this.loadBeanDefinitions(beanFactory);
    
                synchronized(this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            } catch (IOException var5) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
            }
        }
    }
    

    详细过程

    1. prepareRefresh  准备刷新容器
    
      (1) initPropertySources()  自定义属性设置,空方法,留给子类继承
    
      (2) this.getEnvironment().validateRequiredProperties()  首先获取环境配置,然后校验必需属性
    
      (3) this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners)  初始化事件监听器
    
      (4) this.earlyApplicationEvents = new LinkedHashSet()  初始化早期事件
    
    2. obtainFreshBeanFactory  获取Bean工厂
    
      (1) this.refreshBeanFactory()  新建一个Bean工厂,类型为DefaultListableBeanFactory
    
      (2) this.getBeanFactory()  返回刚刚创建的Bean工厂
    
    3. prepareBeanFactory  对Bean工厂做各种预处理设置
    
      (1) 在Bean工厂中设置类加载器、属性解析器等
    
      (2) 在Bean工厂中添加部分Bean后置处理器,例如ApplicationContextAwareProcessor、ApplicationListenerDetector
    
      (3) 在Bean工厂中设置忽略自动注入的接口
    
      (4) 设置自动装配规则
    
      (5) 在Bean工厂中注册一些Bean组件,例如环境配置ConfigurableEnvironment
    
    4. postProcessBeanFactory  Bean工厂的后置处理工作
    
    5. invokeBeanFactoryPostProcessors  执行Bean工厂后置处理器
    
      这一步是在Bean工厂的标准初始化(1-4)之后进行的,主要是执行BeanFactoryPostProcessor及其子接口的
    
      BeanFactoryPostProcessor的子接口主要是指BeanDefinitionRegistryPostProcessor:可以向容器中注册自定义的BeanDefinition
    
      (1) 从容器器中获取BeanDefinitionRegistryPostProcessor类型的Bean组件
    
      (2) 将BeanDefinitionRegistryPostProcessor类型的Bean组件按照顺序分类并排序,即是否实现了PriorityOrdered、Ordered接口
    
      (3) 依次执行实现了PriorityOrdered接口的、实现了Ordered接口的、
            没有实现任何顺序接口的Bean组件的postProcessBeanDefinitionRegistry(registry)方法
    
      (4) 执行所有BeanDefinitionRegistryPostProcessorBean的postProcessBeanFactory(beanFactory)方法
    
      (5) 从容器中获取其他的BeanFactoryPostProcessor类型的Bean组件,即不是BeanDefinitionRegistryPostProcessor类型的
    
      (6) 剩下的步骤跟上面类似,就是先按照实现的顺序接口分类,在每个类别下排序,然后依次执行它们的postProcessBeanFactory(beanFactory)方法
    
    6. registerBeanPostProcessors  注册Bean后置处理器,这种处理器用于拦截bean的创建过程
    
          BeanPostProcessor有很多子接口,每种子接口的执行时机各有不同
    
          |-DestructionAwareBeanPostProcessor
    
          |-InstantiationAwareBeanPostProcessor  
    
          |-MergedBeanDefinitionPostProcessor
    
          |-SmartInstantiationAwareBeanPostProcessor
    
      (1) 获取所有的BeanPostProcessor的Bean名称
    
      (2) 将所有的Bean按优先顺序分为三类:
    
         |-实现了PriorityOrdered接口的列表priorityOrderedPostProcessors
    
         |-实现了Ordered接口的列表orderedPostProcessors
    
         |-没有实现任何顺序接口的列表nonOrderedPostProcessors
    
         |-MergedBeanDefinitionPostProcessor类型的,都放在internalPostProcessors中
    
      (3) 注册priorityOrderedPostProcessors
    
      (4) 注册orderedPostProcessors
    
      (5) 注册nonOrderedPostProcessors
    
      (6) 注册internalPostProcessors
    
      (7) 注册ApplicationListenerDetector,它的作用是在Bean实例化之后判断其是否为
    
          ApplicationListner类型,如果是,则将其添加进容器的监听器集合
    
    7. initMessageSource  初始化消息源Bean,用于消息绑定、消息解析等功能,并且提供国际化解决方案
    
      (1) 获取beanFactory
    
      (2) 判断beanFactory中是否包含id为messageSource的Bean
    
      (3) 如果已存在,则赋值给容器的messageSource属性,这种情况是我们自己在容器中注册了这个Bean
    
      (4) 如果不存在,则新建一个DelegatingMessageSource,并赋值给容器的messageSource属性,
    
          然后在beanFactory中注册这个新Bean,并设置其id为messageSource
    
    8. initApplicationEventMulticaster  初始化事件广播器
    
      (1) 获取beanFactory
    
      (2) 判断beanFactory中是否存在id为applicationEventMulticaster的Bean
    
      (3) 如果已存在,则赋值给容器的applicationEventMulticaster属性,这种情况是我们自己在容器中注册了这个Bean
    
      (4) 如果不存在,则新建一个SimpleApplicationEventMulticaster,并赋值给容器的
    
          applicationEventMulticaster属性,然后在beanFactory中注册这个新Bean,
    
          并设置其id为applicationEventMulticaster
    
    9. onRefresh  没有任何操作,留给子类继承的,我们可以自定义子容器,在重写方法中做一些我们想要的操作
    
    10. registerListeners  注册事件监听器
    
      (1) 获取容器的属性applicationListeners,这是一个事件监听器的集合,将集合中的每个元素都添加进事件广播器
    
      (2) 从容器中获取所有ApplicationListener类型的Bean,将这些Bean添加进事件广播器
    
      (3) 发布早期事件,即容器的earlyApplicationEvents属性,然后清空早期事件
    
    11. finishBeanFactoryInitialization  完成剩下的单实例bean的实例化
    
      (1) 进入DefaultListableBeanFactory.preInstantiateSingletons()方法,获取容器中所有的Bean id列表
    
      (2) 遍历Bean id列表,对每个Bean,获取其Bean定义信息,即RootBeanDefinition
    
      (3) 从Bean定义信息中筛选掉抽象类、非单实例、懒加载的,这些bean在创建容器时并不实例化,
    
          另外还有工厂Bean,即实现了FactoryBean接口的,需要另外一套逻辑进行实例化
    
      (4) 从缓存中获取单实例Bean,即DefaultSingletonBeanRegistry类的singletonObjects属性,所有被创建过的
    
          单实例Bean都会被缓存在这个映射中;如果缓存中存在,说明这个Bean之前被创建过,直接返回
    
      (5) 如果缓存中不存在,则开始新建:AbstractBeanFactory.getBean(beanName)
    
         ① 将Bean标记为已创建,即将其id存入AbstractBeanFactory的alreadyCreated属性中
    
         ② 获取Bean的定义信息,即RootBeanDefinition
    
         ③ 从定义信息中获取该Bean依赖的Bean,如果存在,则重新从第11(4)步开始执行,创建这些依赖的Bean
    
         ④ 创建完依赖Bean(如果存在)之后,以下开始正式新建目标Bean
    
         ⑤ 给Bean后置处理器一个机会用代理对象代替目标对象,即执行InstantiationAwareBeanPostProcessor
    
            类型的Bean后置处理器的postProcessBeforeInstantiation、postProcessAfterInitialization方法
    
         ⑥ 执行MergedBeanDefinitionPostProcessor类型Bean后置处理器的postProcessMergedBeanDefinition方法
    
         ⑦ 执行AbstractAutowireCapableBeanFactory.populateBean(beanName, rootBeanDefinition, beanWrapper)方法,即属性赋值。
    
            在属性赋值之前,首先拿到InstantiationAwareBeanPostProcessor类型的Bean后置处理器,
    
            并执行postProcessAfterInstantiation、postProcessProperties、postProcessPropertyValues方法。
    
            然后才执行该类的applyPropertyValues方法,利用反射调用Bean的setter方法进行属性赋值
    
         ⑧ 执行以下三种aware接口的方法:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
    
         ⑨ 执行Bean后置处理器的初始化前方法,即BeanPostProcessor.postProcessBeforeInitialization(Object bean, String beanName)
    
          ⑩ 执行Bean的初始化方法,即InitializingBean.afterPropertiesSet(),以及在容器中自定义的initMethod
    
          ⑪ 执行Bean后置处理器的初始化后方法,即BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)
    
          ⑫ 如果需要,注册Bean的销毁方法,例如在容器中自定义的destroyMethod。这里只是注册,并不调用
    
      (6) 通过第11(5)步,单实例Bean已经创建并初始化完成,接着,会调用AbstractBeanFactory的父类方法——
    
          DefaultSingletonBeanRegistry.getSingleton(beanName, false)方法,将新建的Bean存入singletonObjects属性中,即缓存
    
      (7) 回到DefaultListableBeanFactory.preInstantiateSingletons()方法(见第11(1)步)
    
          如果新建的Bean实现了SmartInitializingSingleton接口,则执行afterSingletonsInstantiated()回调方法
    
    12. finishRefresh  完成容器刷新
    
      (1) 初始化生命周期处理器(LifecycleProcessor),先从BeanFactory中按类型获取,
    
          如果没有就新建一个DefaultLifecycleProcessor,并注册进BeanFactory
    
      (2) 获取上一步注册的生命周期处理器,回调其onRefresh()方法
    
      (3) 发布容器刷新事件,即ContextRefreshedEvent
    

    BeanFactory

    BeanFactory是个Factory:
    
        在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
    
        它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
    
        BeanFactory只是个接口,并不是IOC容器的具体实现,
        Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等。
    
        ApplicationContext接口由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议ApplicationContext优先
    

    FactoryBean

    FactoryBean是个特殊的Bean:
    
        FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式。
    
        它是一个能生产或者修饰对象生成的工厂Bean,为IOC容器中bean的实现提供了更加灵活的方式。
    
        它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象。
    
  • 相关阅读:
    MySQL服务端恶意读取客户端文件漏洞 (DDCTF2019和国赛均涉及到这个漏洞)
    (转载)基于BIGINT溢出错误的SQL注入
    程序逻辑问题
    ansible笔记
    centos6 sersync2使用
    vsftpd服务
    rsync和rsync后台模式
    mysql5.6和5.7安装 centos
    mysql5.7-my.cnf
    bind-dns服务器搭建
  • 原文地址:https://www.cnblogs.com/loveer/p/11566625.html
Copyright © 2011-2022 走看看