1.spring简介
2.spring容器流程梳理
3.总结
1.spring简介
spring我相信大家都不陌生,只要你是一位JAVA开发人员,肯定会使用它,但是我们日常的使用中,很多时候只需要写业务逻辑,使用别人已经封装好的技术,对其原理却一无所知。在此系列的博客中,笔者希望尽可能地总结spring源码,让我们不再只会使用它,让读者懂其原理。
所谓:有道无术,术尚可求,有术无道,止于术。
框架的使用可能就是‘术’,而框架的底层实现原理,就是‘道’。有了‘道’之后,‘术’是可以根据自己的需要造出来的。就像知道了数学公式是如何推导的,自然不用记住公式,还能对公式进行魔改,甚至发明公式。但是如果只有‘术’,就像只知道了公式的形状,并不知道其来源,稍微变一下我们就认不出来了。
无论是学什么东西,笔者认为,积累‘道’是非常重要的,且不可急功近利,否则就是欲速则不达。
2.spring容器流程梳理
我们在使用spring的时候,使用最多的就是AOP和IOC。
IOC:控制反转
理念:让别人为我们服务!
假设我现在需要一个对象,一般情况下我们都是需要自己new出来,但是有了IOC之后,我们将一些对象的控制权给了spring容器,当我们想要的时候,spring就会自己把对象送来!
看到这里我们可能比较迷糊,还是不知道ioc的具体使用,我们来举个例子好了:
假设我们有一个UserServiceImpl,是IUserService的实现类,但是我们每次使用的时候,都需要:
IUserService userService = new UserServiceImpl();
因为很多地方都要用到这个对象,所以我们要写很多很多个new UserServiceImpl();
如果此时,IUserService更改了需求,需要把所有IUserService的实现类UserserviceImpl全部改成UserserviceImpl2(),那代码更改量是非常恐怖的。但是如果有了IOC,我们就可以和实现类解耦了。主要更改IOC容器里面的实现类,当我们用到这个接口的实现类的时候,让它给我们送来就行了(只需要修改一个地方)。
AOP:面向切面编程
我们都知道,在我们编码的时候,事务控制,权限控制等代码逻辑,会像一把刀一样,横在我们业务逻辑中间,我们将这部分代码称之为,横切业务逻辑代码。
那么将这部分代码统一独立出去,将横切逻辑也业务逻辑分离,并进行统一管理,这就是AOP。
有了AOP和IOC的概念之后,接下来我们来梳理一下,spring的总体执行流程:
一开始,我们学习spring框架,都是从配置文件开始的,我们在spring配置文件中写一个bean,然后我们再从容器中读出来。
xml配置文件:
//将xml加载到容器中
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取bean
A a = (A) classPathXmlApplicationContext.getBean(A.class);
通过上述代码,我们大致可以分析出,我们使用spring的时候,大概是这样一个执行逻辑:
我们大概可以总结出这个逻辑,但是spring里面却远远不止这么多操作,我们先来对上面这张图进行一个扩充,加入一个概念BeanDefinition!
BeanDefinition:
它是配置bean元信息的一个接口,包含:
1)拥有属性存储的功能,比如类信息,比如加载模式(懒加载等等)
2)拥有资源获取的能力,也就是读取配置资源的能力
3)对Bean对象的描述能力
因为有的bean不是马上被实例化的(lazy-load),然后也可能有的bean包含了其它bean的信息,也可能有的bean的属性值需要读取别的文件(${jdbc.username}),所以我们需要beanDefinition。
那么,bean信息除了从xml中来,还可以从注解,json,yml,json等地方来,然后我们还可以对此图进行一个扩充。
其实IOC容器里面,并不仅仅只有反射,这么简单,因此我们对IOC容器也进行一个扩充。
我们先引入一个概念,beanFactoryPostProcessor:
BeanFactoryPostProcessor接口,可以对BeanDefinition元数据进行操作。比如配置文件中的${jdbc.username},我们可以使用BeanFactoryPostProcessor将它的值从文件中读取并赋值。
可见BeanFactoryPostProcessor可以修改BeanDefinition里面的属性值。
然后就是初始化,
在spring中,容器将对象的初始化和实例化分成了两步操作:
分别是初始化和实例化:
如图所示:
初始化,顾名思义,就是我们平时所说的,在堆内存开辟一块空间
实例化比较复杂,分成了很多步,流程图如所示,我们将它并入上面那个大图,再进行讲解:
填充属性:
具体可以参考populateBean方法,大概就是装配一些对象,比如对象A里面有对象B,就将B从容器中取出来并装进入,这个方法后续会详细进行讲解。
执行aware接口方法:
Aware接口的功能就是,一个类只要实现这个类接口的相关子类,则可以拿到相应的东西。
那么具体可以拿到什么呢?我们可以看一下这个类有哪些实现接口:
比如说 ApplicationContextAware,我们实现了这个接口,就可以从获取ApplicationContext 这个对象:
public class AwareDemo implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
BeanPostProcessor:Before:
接下来执行beanPostProcessor前置方法,实现了这个接口的bean,我们要执行它的前置方法
public class BeanPostProcessorDemo implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return null;
}
}
init-method:
如果bean的文件配置中有init标签,我们就要执行它对应的方法:
BeanPostProcessor:After:
后置方法,可以参考上面。
3.总结
今天我们总结了Bean容器的大体的执行流程,也只是粗略地概括了一下,大致可以用一张图来进行概括:
另外值得一提的是,beanFactoryPostProcessor和beanPostProcessor是有区别的,区别如图所示: