SpringIOC原理与应用
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。
- 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的体系结构:
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}
接下来使用另一种方式,这样就不需要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}
现在看看如果是多个实现类,如何自动注入呢?
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的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的