SpringIOC原理与应用

mark

ICO容器的结构如上图所示,首先要让IOC容器去读取Bean的配置信息,并在容器中生成一份相应的Bean定义注册表,根据这张注册表去实例化Bean,装配好Bean之间的依赖关系,为上层提供准备就绪的环境,Spring提供一个配置文件描述Bean还有Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。

SpringIOC容器支持的功能

1、依赖注入

2、依赖检查

3、自动装配

4、支持集合

5、指定初始化和销毁的方法

6、支持回调方法,但是需要实现Spring的接口,略带有侵入性

其中最核心的功能就是依赖注入和自动装配了。

SpringIOC源码分析

Spring 作者 Rod Johnson 设计了两个接口用以表示容器。BeanFactory和ApplicationContext, BeanFactory 粗暴简单,可以理解为就是个 HashMap,Key 是BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。 ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。

BeanDefinition

BeanDefinition与Bean的关系, 就好比类与对象的关系,类在spring的数据结构就是BeanDefinition。根据BeanDefinition得到的对象就是我们需要的Bean。 理解Bean与BeanDefinition是理解spring的整个架构的基础与关键, BeanDefinition接口是顶级基础接口,用来描述Bean,里面存放Bean元数据,比如Bean类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等一些列信息。

BeanDefinition继承的两个接口:

  • BeanMetadataElement接口:BeanDefinition元数据,返回该Bean的来源
  • AttributeAccessor接口:提供对BeanDefinition属性操作能力,

继承或实现BeanDefinition的接口:

  • AbstractBeanDefinition类:抽象类统一实现了BeanDefinition定义的一部分操作,可以说是定义了BeanDefinition很多默认的属性。 正是在AbstractBeanDefinition基础上, Spring衍生出了一些列BeaDefinition。

这里我们可以关注下重写的equals()、hashcode()、toString()方法

此外initMethodName属性,destroyMethodName 属性,这两个属性bean的生命周期有关,此处只提一句,后续讲解。

接下来,我们看看从AbstractBeanDefinition上衍生出来的几个类

  • RootBeanDefinition: 代表一个xml,java Config来的BeanDefinition
  • ChildBeanDefinition: 可以让子BeanDefinition定义拥有从父母哪里继承配置的能力
  • GenericBeanDefinition: spring2.5后注册bean首选的是GenericBeanDefinition。GenericBeanDefinition允许动态的设置父bean.GenericBeanDefinition可以作为RootBeanDefinition与ChildBeanDefinition的替代品。
  • AnnotatedBeanDefinition接口: 表示注解类型BeanDefinition。有两个重要的属性,AnnotationMetadata,MethodMetadata分别表示BeanDefinition的注解元信息和方法元信息 实现了此接口的BeanDefinition可以获取到注解元数据和方法元数据。
  • AnnotatedGenericBeanDefinition类: 表示@Configuration注解注释的BeanDefinition类
  • ScannedGenericBeanDefinition类: 表示@Component、@Service、@Controller等注解注释的Bean类

BeanDefinitionRegistry

提供向IOC容器注册BeanDefinition对象的方法,将定义Bean 的资源文件解析成 BeanDefinition 后需要将其注入容器中,这个过程就是由 BeanDefinitionRegistry 来完成。 BeanDefinitionRegistry 继承了 AliasRegistry 接口,其核心子类有三个:SimpleBeanDefinitionRegistry、DefaultListableBeanFactory、GenericApplicationContext。

mark

  • AliasRegistry: 用于别名管理的通用型接口,作为 BeanDefinitionRegistry 的顶层接口。AliasRegistry 定义了一些别名管理的方法。
  • BeanDefinitionRegistry:BeanDefinition 的注册接口,如 RootBeanDefinition 和 ChildBeanDefinition。它通常由 BeanFactories 实现,在 Spring 中已知的实现者为:DefaultListableBeanFactory 和 GenericApplicationContext。BeanDefinitionRegistry 是 Spring 的 Bean 工厂包中唯一封装 BeanDefinition 注册的接口。BeanDefinitionRegistry 接口定义了关于 BeanDefinition 注册、注销、查询等一系列的操作。
  • SimpleBeanDefinitionRegistry:SimpleBeanDefinitionRegistry 是 BeanDefinitionRegistry 一个简单的实现,它还继承 SimpleAliasRegistry( AliasRegistry 的简单实现),它仅仅只提供注册表功能,无工厂功能。SimpleBeanDefinitionRegistry 使用 ConcurrentHashMap 来存储注册的 BeanDefinition。
  • DefaultListableBeanFactory: DefaultListableBeanFactory,ConfigurableListableBeanFactory(其实就是 BeanFactory ) 和BeanDefinitionRegistry 接口的默认实现:一个基于BeanDefinition元数据的完整 Bean工厂。所以相对于SimpleBeanDefinitionRegistry而言,DefaultListableBeanFactory则是一个具有注册功能的完整Bean工厂。它同样是用ConcurrentHashMap数据结构来存储注册的BeanDefinition。

BeanFactory

提供IOC的配置机制、包含Bean的各种定义,便于实例化Bean、建立Bean之间的依赖关系、Bean生命周期的控制等功能。 BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似,下图是BeanFactory的体系结构:

mark

ListableBeanRegistry,该接口定义了访问容器中并基本信息的若干方法。查看Bean的个数,获取某一类型Bean的配置名,查看容器中是否包括某一Bean等方法。

HierarchicalBeanFactory,即父子级联ICO接口,此容器可以通过接口方法访问父容器,通过HierarchicalBeanFactory接口,Spring得IOC容器可以建立父子关联得容器体系,子容器可以访问父容器中的Bean,但是父容器是不能访问子容器中的Bean的。Spring使用父子容器实现了很多功能,比如在SpringMVC中,展现层的Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中,这样展现层的Bean就可以引用业务层和持久层的Bean,而持久层和业务层的却看不到展现层的Bean。

ConfigurableBeanFactory,它在SpringCore中是一个很重要的接口,增强了IOC容器的可定制性,它定义了设置类加载器、属性编辑器、以及属性初始化后置处理器等方法。

AutowireCapableFactory,它定义了将容器中的Bean按照某种规则,比如按照名称匹配,按照类型匹配等,按照这些规则,对Bean进行自动装配。

SingletonBeanRegistry,它允许在运行期间向容器注册Singleton实例Bean的方法,通过这些接口也证明了BeanFactory体系确实是提供了IOC的基础,即依赖注入和Bean的装载等功能。

BeanFactory与ApplicationContext

BeanFactory是Spring框架的基础设施,面向SpringApplicationContext面向使用Spring框架的开发者。

ApplicationContext的功能(继承多个接口)

  • BeanFactory:能够管理、装配Bean
  • ResourcePatternResolver:能够加载资源文件
  • MessageSource:能够实现国际化等功能
  • ApplicationEventPublisher:能够注册监听器,实现监听机制

ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。

SpringIOC应用示例

下面演示一个我们手动装配的例子:

Person.java

 1public class Person {
 2    private Integer id;
 3    private String name;
 4
 5    public Integer getId() {
 6        return id;
 7    }
 8
 9    public void setId(Integer id) {
10        this.id = id;
11    }
12
13    public String getName() {
14        return name;
15    }
16
17    public void setName(String name) {
18        this.name = name;
19    }
20}

ApplicationConfig.java

 1@Configuration
 2public class ApplicationConfig {
 3    @Bean(name = "person")
 4    public Person initPerson(){
 5        Person person = new Person();
 6        person.setId(1);
 7        person.setName("Tim");
 8        return person;
 9    }
10}

SpringStudyApplication.java

1@SpringBootApplication
2public class SpringStudyApplication {
3    public static void main(String[] args) {
4        ApplicationContext ctx = SpringApplication.run(SpringStudyApplication.class, args);
5        Person person = ctx.getBean(Person.class);
6        System.out.println("Name is " + person.getName());
7    }
8}

mark

接下来使用另一种方式,这样就不需要ApplicationConfig了, @SpringBootApplication已经含有了@ComponentScan注解,即已经拥有了扫描器的功能:

Person.java

 1import org.springframework.beans.factory.annotation.Value;
 2import org.springframework.stereotype.Component;
 3
 4@Component("person")
 5public class Person {
 6    @Value("1")
 7    private Integer id;
 8    
 9    @Value("Tim")
10    private String name;
11
12    public Integer getId() {
13        return id;
14    }
15
16    public void setId(Integer id) {
17        this.id = id;
18    }
19
20    public String getName() {
21        return name;
22    }
23
24    public void setName(String name) {
25        this.name = name;
26    }
27}

mark

现在看看如果是多个实现类,如何自动注入呢?

 1public interface Pet {
 2    void move();
 3}
 4
 5@Component
 6public class Dog implements Pet {
 7    @Override
 8    public void move() {
 9        System.out.println("Running...");
10    }
11}
12
13@Component
14public class Bird implements Pet {
15    @Override
16    public void move() {
17        System.out.println("flying...");
18    }
19}
20
21@Component("person")
22public class Person {
23    private Pet pet;
24
25    public Person(Pet pet) {
26        this.pet = pet;
27    }
28    
29    ...
30}

此时应该注入哪个呢?其实IOC容器也不知道该注入谁,只能报错。

1Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

@Reource(默认按名称装配,当找不到与名称匹配的bean才会按类型装配), @Autowired(默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解和@Primary注解一起使用)

@Primary自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常

@Qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的

mark