依赖
class Coder {
Computer mComputer;
public Coder () {
mComputer = new Computer();
}
public void coding(){
mComputer.coding();
}
}
码农需要电脑才能敲代码,在这个码农类中,码农持有电脑的引用,所以说码农依赖电脑。依赖是一种关系。
依赖注入
在上面的coder代码中,Computer是coder自己创建的;假如我们把上面代码改写成如下:
class Coder {
Computer mComputer;
public void setMComputer(Computer mComputer){
this.mComputer = mComputer;
}
public void coding(){
mComputer.coding();
}
}
如果改成这样,那么computer从何而来?我们可以在另外一个类中,通过setComputer注入,比如:
public class Context {
public static void main(String[] args) {
Computer computer = new Computer();
Coder coder = new Coder();
coder.setComputer(computer);
}
}
形如Coder里面的computer不是在Coder里面(本类)直接通过 new Computer()来赋值,而是通过在第三方的类中,通过setComputer(computer)注入进去的,就叫做依赖注入。
控制反转
形如上面的Coder里面的computer不是Coder自己来创建,而是把对computer的控制权交给第三方,在第三方的类中创建,然后再注入(setComputer/computer()),就叫做控制反转。
依赖注入的好处
在类中我们依赖的是接口,容器注入的是实现类。假如我们在Coder里面直接写死了new Computer(),那么有一天我们要升级为Macbook,我们是不是还要去改Coder里面的代码,把computer = new Computer()改为 computer = new Macbook(),又有一天,我们突然想换个小米笔记本,岂不是又要改成 new Mibook()。这样子改来改去是比较麻烦的,而且要是有很多个Coder,比如JavaCoder,PHPCoder,他们都有computer的引用,公司统一规定要升级为MacBook,我们就要改很多个不同的类。而如果我们采用依赖注入,我们只需要在容器里面改Computer computer = new Macbook()即可。因为Coder都是接口的引用,所以就都不用改了。
IOC容器
上面我们说到context把coder对象和computer对象创建出来,再把computer对象注入到了coder中。在spring中,也有一个context,它把所有配置了的对象(bean)创建出来,然后再根据他们的依赖关系,把不同的bean注入到另外的bean中,形成一个关系集合,这样的context就叫做IOC容器。
core

Core就是发现、建立和维护每 个Bean之间的关系所需要的一系列的工具,从这个角度看来,Core这个组件叫Util更能让你理解。在这些工具类中,定义了资源的访问方式的resource是其中最重要的组成部分之一。
beans
Bean组件在Spring的org.springframework.beans包下。这个包下的所有类主要解决了三件事:
- Bean的解析
- Bean的定义
- Bean 的创建
Bean的解析

Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这里只讲XmlBeanDefinitionReader。
- XmlBeanDefinitionReader通过DocumentLoader把bean的定义文件解析成一颗DOM树,然后对DOM中的各个结点就行解析。
- 对DOM树根节点中的各个结点进行解析。
- 循环各个子树,检查节点是默认的名称空间还是扩展的名称空间,比如<bean>、<property>是默认的名称空间,而<aop:xxx>、<context:xxx>这种标签是扩展的标签需要额外的标签处理器,默认标签的一级标签是<beans>。
- 解析bean标签,import和beans标签最后也会走到这一步。bean标签的解析逻辑被委托BeanDefinitionParserDelegate,调用parseCustomElement()方法解析出了BeanDefinition对象。
Bean 的定义

当 Spring 成功解析你定义的一个 <bean/> 节点后,在 Spring 的内部就被转化成 BeanDefinition 对象。以后所有的操作都是对这个对象完成的。
bean的创建

Spring Bean 的创建是典型的工厂模式,它的顶级接口是 BeanFactory。BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory。从bean的解析和定义之后,我们已经得到一个BeanDefinition对象。那么BeanFactory 是怎么根据BeanDefinition创建对应的对象的呢?可以参考大致代码:
//1、创建个bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//2、创建一个bean的定义
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(Computer.class);
//定义属性
MutablePropertyValues mpvs = new MutablePropertyValues();
PropertyValue name = new PropertyValue("name","MiBook");
PropertyValue price = new PropertyValue("price",5399);
mpvs.addPropertyValue(name);
mpvs.addPropertyValue(price);
//设置bean的类型和属性
bd.setPropertyValues(mpvs);
//3、将定义好bean注册到容器
beanFactory.registerBeanDefinition("computer", bd);
beanFactory.preInstantiateSingletons();
//4、使用bean
Computer computer = (Computer) beanFactory.getBean("computer");
System.out.println(computer);
context

Context 位于 Spring 的 org.springframework.context 包下,它继承BeanFactory,说明spring容器运行的主体对象是bean;它还继承resourceLoader对象,说明它可以访问任何外部资源。初始化容器时,会调用AbstractApplicationContext的refresh方法。那么都做了什么事呢?
- 构建 BeanFactory
- 注册可能感兴趣的事件
- 创建 Bean 实例对象
- 触发被监听的事件
一点想法
其实分析这个spring,有点像之前分析springmvc,springmvc有一个存放url与method对应关系的map,那么spring是否存在beanId和object对应的map呢?经过我一番查找,确实找到了这个map,他就是DefaultSingletonBeanRegistry的singletonObjects,是一个ConcurrentHashMap。

protected void addSingleton(String beanName, Object singletonObject) {
Map var3 = this.singletonObjects;
synchronized(this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject != null?singletonObject:NULL_OBJECT);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
bean的生命周期

1.Spring对Bean进行实例化(相当于程序中的new Xx())
2.Spring将值和Bean的引用注入进Bean对应的属性中
3.如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
5.如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把y应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )
6.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)
7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
8.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
9.经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。
网友评论