一文彻底理解Spring IOC 容器设计理念与源码解读

IOC核心理论


1、IOC 理论概要


实体Bean的创建 基于Class 构建构造方法构建静态工厂方法 创建FactoryBean创建

(1) 基于ClassName构建

<code><bean>/<code>

这是最常规的方法,其原理是在spring底层会基于class属性通过反射进行构建。

(2) 构造方法创建

<code><bean>
<constructor-arg>
<constructor-arg>
/<bean>/<code>

如果需要基于参数进行构建,就采用 构造方法构建,其 对应 属性如下:

name : 构造方法参数变量方法type: 参数类型index :参数索引,从0开始value : 参数值,spring会自动转换成参数 实际类型值ref: 引用串的 其他对象

(3) 静态工厂方法创建

<code><bean>
<constructor-arg>
/<bean>/<code>

如果你正在对一个对象进行A/B测试,就可以采用静态工厂方法的方式创建,其基于策略创建不同的对象或填充不同的属性。

该模式下必须创建一个静态工厂方法,并且方法返回该实例,spring会调用该静态方法创建对象。


<code>public static HelloSpring build(String type) {
if (type.equals("A")) {
return new HelloSpring("luban", "man");
} else if (type.equals("B")) {
return new HelloSpring("diaocan", "woman");
} else {
throw new IllegalArgumentException("type must A or B");
}
}/<code>

(4) FactoryBean创建

<code><bean> 1
<property>
/<bean>/<code>

指定一个Bean工厂来创建对象,对象构建初始化完全交给该工厂来实现。配置Bean时指定该工厂类的一个类名:

<code>public class DriverFactoryBean implements FactoryBean {
private String jdbcUrl;
public Object getObject() throws Exception {
return DriverManager.getDriver(jdbcUrl);
}
public Class> getObjectType() {
return Driver.class;
}
public boolean isSingleton() {
return true;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
}
/<code>

3、bean的基本特性

作用范围生命周期装载机制

a 作用范围

很多时候Bean对象是无状态的,而有些时候又是有装填的,无状态的对象我们采用单例即可,而又状态则必须是多例模式,通过scope即可创建:

<code>scope = "prototype"
scope="singleton"
<bean>/<code>

如果一个Bean设置成prototype,我们可以通过BeanFactoryAware获取BeanFactory对象,即可每次获取的都是新对象。

Bean对象的创建、初始化、销毁即是Bean的声明周期,通过init-method、destory-method属性可以分别制指定周期构建方法与 初始方法。

<code><bean>/<code>

如果觉得 麻烦,可以让Bean去实现InitializingBean.afterPropertiesSet() 、DisposableBean.destroy()方法,分别对应 初始化 和销毁 方法。

c、加载机制

.指示Bean在何时进行加载。设置lazy-init即可,其值如下:

true: 懒加载,即延迟加载false: 非懒加载,容器启动时即创建对象default: 默认,采用default-lazy-init 中指定值,如果default-lazy-init 没有指定就是false

什么时候使用懒加载??

懒加载会使容器启动的更快,而非懒加载可以时容器启动时更快的发现程序当中的错误,选择哪一个就是追求的是启动速度 ,还是希望更早的 发现错误,一般我们会选择后者。


4、依赖注入

spring ioc 如何依赖注入的。有以下几种方式:

set 方法注入构造方法注入自动注入(byName、byType)方法注入(lookup-method)

@Autowired 与 @Resources的区别:

@Autiwired 与 @Resource 都可以用来装配bean,都可以写在字段上,或者写在setter 方法上@Autiwired 默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果允许为null,可以设置它的required 属性为false,如:@Autowired(required = false)@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。

Spinrg IOC 容器设计


IOC设计原理与实现


1、 Bean的构建过程

spring.xml 文件中保存了我们对bean的描述配置,BeanFactory会读取这些配置然后生成对应的Bean,这是我们对ioc原理的一般理解。

BeanDefinition (Bean定义)

ioc实现中 我们在xml中描述的Bean的信息 最后都将保存至BeanDefinition(定义)对象中,其中xml bean 与 BeanDefnition 是一对一的关系。

由此可见,xml bean 中设置的属性最后都体现在BeanDefinition中。如

(1) BeanDefinitionRegistry(Bean注册器)

在上表中我们并没有看到xml bean中的id 和 name 属性没有体现在定义中,原因是ID其作为当前Bean的存储key注册到了BeanDefinitionRegistry注册器中。name作为别名key注册到了AliasRegistry注册中心。其最后都指向其对应的BeanDefinition。

<code>public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String var1, BeanDefinition var2)
void removeBeanDefinition(String var1)

BeanDefinition getBeanDefinition(String var1)

boolean containsBeanDefinition(String var1);

String[] getBeanDefinitionNames();

int getBeanDefinitionCount();
boolean isBeanNameInUse(String var1);
}/<code>BeanDefinitionReader(Bean定义读取)

BeanDefinition中存储了XML bean信息,而BeanDefinitionRegister基于ID和name保存了Bean的定义。接下来学习的是xml bean到 BeanDefiniton,然后再注册至BeanDefintionRegister整个过程。


上图中可以看出Bean的定义是由BeanDefinitionReader从xml中读取配置并并构建出BeanDefinitionReader,然后再基于别名注册 到BeanDefinitionRegistry中。

查看BeanDefinitonRead结构

方法说明:

loadBeanDefinitions(Resource resource)基于资源装在Bean定义并注册到注册器int loadBeanDefinitions(String location)基于资源路径装载Bean定义并注册至注册器BeanDefinitionRegistry getRegistry()获取注册器ResourceLoader getResourceLoader()获取资源状态器基于示例演示BeanDefinitionReader装载过程

<code>//创建一个简单注册器


BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();
//创建bean定义读取器
BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);
// 创建资源读取器
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
// 获取资源
Resource xmlResource = resourceLoader.getResource("spring.xml");
// 装载Bean的定义
reader.loadBeanDefinitions(xmlResource);
// 打印构建的Bean 名称
System.out.println(Arrays.toString(register.getBeanDefinitionNames());/<code>

Beanfactory(bean工厂)

有了Bean的定义相当于有了产品的配方,接下来就是要把这个配方送到工厂进行上产了,在ioc当中Bean的构建是由BeanFactory负责的,其结构如下:


方法说明:

getBean(String)基于ID 或 name获取一个Bean T getBean(Class requiredType)基于Bean的类别获取一个Bean(如果出现多个该类的实例,将会报错。但可以指定primary ="true"调整优先级来解决该错误)Object getBean(String name,Object...args)基于名称获取一个Bean,并覆盖默认的构造参数boolean isTypeMatch(String name,Class> typeToMatch)指定Bean与指定Class 是否匹配

以上方法中重点要关注getBean,当用户调用getBean的时候就会触发Bean的创建动作,其实如何创建的呢?

演示基本BeanFactory获取一个Bean

<code>#创建Bean堆栈
// 其反射实例化Bean
java.lang.reflect.Constructor.newInstance(Unknown Source:-1)
BeanUtils.instantiateClass()
//基于实例化策略 实例化Bean
SimpleInstantiationStrategy.instantiate()
AbstractAutowireCapableBeanFactory.instantiateBean()
// 执行Bean的实例化方法
AbstractAutowireCapableBeanFactory.createBeanInstance()
AbstractAutowireCapableBeanFactory.doCreateBean()
// 执行Bean的创建
AbstractAutowireCapableBeanFactory.createBean()
// 缓存中没有,调用指定Bean工厂创建Bean
AbstractBeanFactory$1.getObject()
// 从单例注册中心获取Bean缓存
DefaultSingletonBeanRegistry.getSingleton()
AbstractBeanFactory.doGetBean()
// 获取Bean
AbstractBeanFactory.getBean()


// 调用的客户类
com.XXX.spring.BeanFactoryExample.main(
/<code>

从调用过程可以总结一下几点:

调用BeanFactory.getBean()会触发Bean的实例化DefaultSingletonBeanRegistry中缓存了单例BeanBean的创建于初始化AbstractAutowireCapableBeanFactory完成的

3 BeanFactory 与 ApplicationContext 区别

BeanFactory 看上去可以去做 IOC当中的大部分事情,为什么还要去定义一个ApplicationContext呢??

ApplicationContext 结构图:


从上图中可以看到ApplicationContext 它由BeanFactory接口派生而来,因而提供了BeanFactory的所有的功能。除此之外context包还包含了以下的功上襦:

MessageSource,提供国际化的消息访问资源访问,如URL和文件事件传播,实现了ApplicationListener接口的Bean载入多个上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层

2.3 BeanDefinition 加载解析与注册过程


2.4 Bean获取与创建流程