Spring5.x之IOC

本文目的

1、知道什么是IOC

2、知道什么是IOC容器

3、知道什么是bean

3、autowireMode的作用

准备

spring framework使用5.2.19版本

什么是IOC

英文:Inversion of Control (IoC) ,中文:控制反转

也称之为依赖注入dependency injection (DI)

这是一个过程,此过程是通过使用直接构造类来控制其依赖的对象的实例化,是依赖bean本身的逆序,因此称之为控制反转。

大白话说明

原先我要使用B但B依赖A,我是先创建A,在创建B时将A传递进去,才使用B。现在我直接使用B创建B和A交给了Spring容器,而你在向Spring容器获取B时,它有就直接返回没有创建,这时发现依赖再先创建A,A创建好再创建B并注入,然后返回B给你使用,整体流程和原先是反着来的。 

官网对IOC的解释原文:

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

什么是IOC容器 

IOC的实现是由一个叫容器来实现这个过程的,容器它负责bean的实例化、配置和组装Bean。

那容器是怎么实例化对象的? 

容器通过读取配置元数据来获取有关哪些对象实例化,配置和组装的说明。

那容器读取配置元数据有哪些方式呢?

1、XML【一开始就支持】;2、Java注释【2.5开始引入注释,3.0提供较为完成的体系如@Configuration@Bean@Import, and @DependsOn 】、3、Java代码API。

了解IOC容器代码,从哪里入手?

由spring-bean模块中的org.springframework.beans包和spring-context模块中的 org.springframework.context包提供了基础,BeanFactory是顶级类,派生出子接口ApplicationContext,常用实现类ClassPathXmlApplicationContext、AnnotationConfigWebApplicationContext

关系图

什么是bean

bean其实就是容器根据元配置产生的对象

它在容器中被定义为BeanDefinition,其包含1、实际的类;2、一些行为元素如,范围【scope=prototype/singleton】、生命周期回调之类行为;3、属性【bean的依赖,属性值】;autowireMode和 Lazy initialization mode等

如何注册外部bean

通常情况下容器读取元配置是通过xml配置,java注解,还有就是可以通过容器api方式,注册外部类【DefaultListableBeanFactory#registerSingleton(..) 和registerBeanDefinition(..)

bean的命名

每个bean在容器中都有一个唯一的名称【标识】,若是要多个标识可以定义别名,

定义别名:

        使用xml的为fromName的bean定义别名使用alias标签,<alias name="fromName" alias="toName"/>

        使用注解@Bean({"a","b"}),默认第一个为bean名称其余为别名,注意@AliasFor并不是 给bean起别名,而是标识注解属性之间是等价的或者不同注解内的属性是等价的,后面有空再研究说明

默认bean的名称生成规则

在不指定bean名称时,默认bean的名称以类名首字母小写作为名称【驼峰形式】,需要注意的是首字母是连续两个大写就保持原来的类名作为bean的名称

bean的实例化

有三种方式:1、通过反射调用构造器方法进行实例化;2、通过静态工厂实例化对象;3、通过实例工厂实例化对象

bean的依赖注入方式

两种通过1、构造器注入;2、通过setter方法【又分为两种形式根据名称或者类型】

BeanDefinition中的autowireMode的作用

标识着bean的注入模式,用到的有四个值0、1、2、3

        autowireMode=0是默认值,半自动通过使用@Autowired、@Resource注解,标识哪些需要注入,由AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor处理这两个注解将依赖对象注入

     

        1和2是对应着依赖注入的setter注入,当autowireMode=1是根据setXX(A a)这个XX作为bean名称【注意不是名称不是a】去容器找到bean传入到setter方法中,从而实现注入;autowireMode=2根据类型注入,setXX(A a)更加A这个类型去容器获取实例并注入

 //set注入根据type匹配,
// 根据类型所有匹配类型的方法都执行,
// set方法只能有一个参数且在容器中才执行,多个参数即使都在容器中也不执行
// 重载方法只执行最后一个        

        3是构造器注入

 //set方法注入将不会生效
//注入会优先选择构造器入参最长且存在spring bean进行实例化
//参数长度相同且都在Spring容器中会使用最后一个,和bean名称先后顺序无关

  注意!!!有多个有参构造器会报如下错误,即使autowireMode=3也一样,只有一个有参或者有默认构造器就不会报错,但如果是多个有参构造器,且某一个有@Autowired则不会报错会使用该构造器实例化,有且只有一个构造器能被修饰如果有多个被修饰将报错Found constructor with 'required' Autowired annotation already,@Autowire相当于指明了使用哪个构造器实例化

代码验证autowireMode的作用

 定义实体 

package com.csh.spring.dimodule.two;

public class A {
    public A() {
        System.out.println("construct empty");
    }

	public void setC(C  c){
		System.out.println("set c by name");
	}

	public void setV(B b){
		System.out.println("set b by name v");
	}

    public void setB(B b){
        System.out.println("set b by name");
    }

	//重载方法只执行最后一个
	public void setBb(C  c){
		System.out.println("set c by type");
	}

    public void setBb(B a){
        System.out.println("set b by type");
    }

//	public A(B b) {
//		System.out.println("construct b");
//	}
//	public A(C c) {
//		System.out.println("construct c");
//	}

    public A(B b, C c){
        System.out.println("construct b c");
    }
    public A(B b, C c, D d){
        System.out.println("construct b c d");
    }
}

package com.csh.spring.dimodule.two;

public class B {
}

package com.csh.spring.dimodule.two;

public class C {
}

package com.csh.spring.dimodule.two;

public class D{
}

 后置处理器,修改bean的beanDefinition中的autowireMode

package com.csh.spring.dimodule.two;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;

public class MyBeanFactoryBeanProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");
        //注入模型默认为0

        // set注入根据name匹配,需要注意是根据setXX中的xx这个名字与bean的名称匹配而不是 参数的名字
        //结果:
//		MyBeanFactoryBeanProcessor
//		construct empty
//		set b by name v
//        beanDefinition.setAutowireMode(1);

        // set注入根据type匹配,
		// 根据类型所有匹配类型的方法都执行,
		// set方法只能有一个参数且在容器中才执行,多个参数即使都在容器中也不执行
		// 重载方法只执行最后一个
        //结果:
        //MyBeanFactoryBeanProcessor
        //construct empty
//		set b by name
//		set b by type
//		set c by name
//		set b by name v
//        beanDefinition.setAutowireMode(2);

		//构造器注入
        //set方法注入将不会生效
		//注入会优先选择构造器入参最长且存在spring bean进行实例化
		//参数长度相同且都在Spring容器中会使用最后一个,和bean名称先后顺序无关
        //结果:
        //MyBeanFactoryBeanProcessor
        //construct b c
//        beanDefinition.setAutowireMode(3);
        //实验过程发现:
        // 作为spring的bean可以不声明无参构造器且多个有参构造器而非3注入模型的需要提供无参构造器来作为实例化构造器
		// 或者有且只有一个有参构造器
		// 默认autowireMode,有多个有参构造器会报如下错误,只有一个有参或者有默认构造器就不会报错,但如果是多个有参构造器,且某一个有@Autowired则不会报错会使用该构造器实例化,有且只有一个构造器能被修饰如果有多个被修饰将报错Found constructor with 'required' Autowired annotation already,@Autowire相当于指明了使用哪个构造器实例化
		//Error creating bean with name 'a': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.csh.spring.dimodule.two.A]: No default constructor foun
        //也就是说1、2和3是先对独立的
        System.out.println("MyBeanFactoryBeanProcessor");
    }
}

初始化容器 

package com.csh.spring.dimodule.two;

import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AutowiredModuleTest {
    @Test
    public void testByNameOrTypeAndConstruct() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(A.class, C.class, MyBeanFactoryBeanProcessor.class);

        context.refresh();
		
    }


}