在springBoot大行其道的今天,我们似乎可以无脑使用Spring全家桶,但是我们对于Spring还是要知道其原理的。下面和小胖一起去探寻下Spring IOC的原理去吧。
idea快捷键:
查找接口的实现类:
ctrl + alt +B
查看类或接口的继承关系:
ctrl + h
小胖十分推荐的两篇博客:
小胖墙裂推荐1——idea整合SSM框架步骤
小胖墙裂推荐2——IOC源码导读
1. 测试代码
测试代码:
public class MessageMapperTest {
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
// 加载spring配置文件
applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
}
@After
public void tearDown() throws Exception {
}
@Test
public void TestIOC() {
MessageService messageService = applicationContext.getBean(MessageService.class);
System.out.println("-->" + messageService.getMessage());
}
}
spring.xml代码
service接口
public interface MessageService {
String getMessage();
}
service接口实现类
public class MessageServiceImpl implements MessageService {
public String getMessage() {
return "hello world";
}
}
2. 源码导读
在启动类中,有着ApplicationContext context=new ClassPathXmlApplicationContext(...)
就是在ClassPath
中寻找xml
配置文件,根据xml文件内容来构建ApplicationContext
,当然我们还有ClassPathXmlApplicationContext
以外,还有其他的方案可以构建ApplicationContext
。
ClassPathXmlApplicationContext
:默认情况下会去ClassPath
路径下寻找配置文件,Classpath
可以省略,路径就是编译后的classes
目录。如果是绝对路径,需要加上file:
前缀,不可省略。FileSystemXmlApplicationContext
:默认情况下在项目路径下加载,可以使用相对路径,也可以使用绝对路径,绝对路径下file:
可省略。AnnotationConfigApplicationContext
:基于注解使用,不需要配置文件。
ApplicationContext
在启动过程中,会负责创建实例Bean
,往各个bean
中注入依赖。
2.1 图解BeanFactory
BeanFactory,和中文一样,就是生产
bean
的工厂,负责生产管理各个bean
实例。
正如图上所见,我们的ApplicationContext
本质上就是一个BeanFactory
对象。
可能看到这个图的时候,大家有点想放弃的冲动了,但是,我们无须全部关注,先关注特殊重点的几个实现类或接口。
ApplicationContext
继承了ListableBeanFactory
,这个ListableFactory
的作用是可以获取多个bean
,而FactoryBean
接口虽然是最顶层接口,但是只是获取一个Bean
的。ApplicationContext
继承了HierarchicalBeanFactory
,(注:
[ˌhaɪəˈrɑ:kɪkl]] 分层的,分等级的
),也就是我们在应用程序中,可以起多个BeanFactory
,然后后将各个BeanFactory
设置为父子关系。AutowireCapableBeanFactory
[ˈkeɪpəbl] 能胜任的
大家看到Autowire
会十分的熟悉,他就是自动装配Bean用的,但是ApplicationContext并没有继承它,但是我们在实际中还是可以使用到注解的方式注入到容器中的,没有继承,但是不代表没有组合,我们可以看到ApplicationContext
里面的最后一个方法:getAutowireCapableBeanfactory()就知道了。
ConfigurableListableBeanFactory
也是一个特殊的接口,原因就是他继承了第二层的三个接口。DefaultListableBeanFactory
最底层的类,包含即基本所有的功能。
接口ApplicationContext继承的接口或者实现类:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
HierarchicalBeanFactory源码:
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* Return the parent bean factory, or {@code null} if there is none.
*/
BeanFactory getParentBeanFactory();
/**
* Return whether the local bean factory contains a bean of the given name,
* ignoring beans defined in ancestor contexts.
* This is an alternative to {@code containsBean}, ignoring a bean
* of the given name from an ancestor bean factory.
* @param name the name of the bean to query
* @return whether a bean with the given name is defined in the local factory
* @see BeanFactory#containsBean
*/
boolean containsLocalBean(String name);
}
2.2 BeanFactory源码分析
1. 点击进入ClassPathXmlApplicationContext的构造函数中
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
2. ClassPathXmlApplicationContext类的82行
//进入重构的构造方法中
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
//如果已经有ApplicationContext并且需要配置为父子关系。(HierarchicalBeanFactory)
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//根据配置文件,处理成配置文件数组
setConfigLocations(configLocations);
if (refresh) {
refresh(); //核心方法【翻新】(点击进入)
}
}
我们创建Factory工厂,为什么不是init()
类型的方法,而是refresh()
方法,因为ApplicationContext
建立之后,其实是可以通过refresh()
这个方法重建的,refresh()
会将原来的ApplicationContext
销毁,然后重新执行一次初始化操作。
AbstractApplicationContext中的504行:
@Override
public void refresh() throws BeansException, IllegalStateException {
【1. 加锁,否则refresh()重建还没结束,又来重建了,可怕...】
synchronized (this.startupShutdownMonitor) {
【2. 准备容器的重建。容器启动时间,标记“已启动”状态,处理配置文件的占位符】
prepareRefresh();
【3. (关键:)这步执行完毕,配置文件会被解析成一个个的Bean定义,注册到了BeanFactory中。
* 当然这里说的Bean还没有初始化,只是配置信息都提取出来了
* 注册也只是将这些信息保存到注册中心(敲黑板,划重点)
* 说到底核心就是:XML配置(beanName)-->beanDefinition的map中。
* (这个比较重要,下一章单独讲!!!)
】
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
【4. 设置BeanFactory,添加实现BeanPostProcessor接口的bean(后处理的bean),忽略几个特殊接口的bean】
prepareBeanFactory(beanFactory);
【PS:提一下BeanFactoryPostProcessor,若是Bean实现了该接口,那么在容器初始化之后,
spring会负责调用里面的postProcessBeanFactory()方法。其实就是后处理方法,针对于BeanFactory的】
try {
【---->注意:到达此处,所有的bean已经被加载注册完毕。但是还没初始化。】
【(就是后处理加点东西),具体的子类到这步的时候,可以添加一些特殊的BeanFactoryPostProcessor的实现类或者做一些其他的事情。】
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
【---------------------分界线--------------------】
【上面这么多,就是将XML解析为BeanDefinition对象,然后处理实现BeanFactoryPostProcessor或者
BeanPostProcessor接口的bean,其实就是后处理】
【一次性分析太多,看着就烦了,咱们直接先看postProcessBeanFactory到底是干啥的。直接看下一个标题去吧】
【下一章:重心在于分析obtainFreshBeanFactory方法。】
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2.2.1 BeanPostProcessor和BeanFactoryPostProcessor的作用以及区别
上面这么大篇幅讲
BeanPostProcessor
和BeanFactoryPostProcessor
是不是大家已经蒙了,反正小胖晕了...下面讲一下他们的作用。[ˈprəʊsesə(r)] 处理
Spring的BeanPostProcessor和BeanFactoryPostProcessor区别
Spring
提供了两种后处理bean
的扩展接口,分别是BeanPostProcessor
和BeanFactoryPostProcessor
。这两者还是有区别的。
2.2.1.1 BeanPostProcessor的使用
BeanPostProcessor
:Bean级别的处理,针对某个具体的Bean进行处理。
接口提供了两个方法,分别是初始化前和初始化后执行的方法,那啥叫初始化前呀?
public interface BeanPostProcessor
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
类似于定义Bean的时候,定义init-method所指定的方法
上面这两个方法分别在init
方法前后执行,需要注意的是,我们定义的类实现了BeanPostProcessor
接口,默认是对整个Spring
容器中所有的bean
进行处理。
等等,不是说可以针对于某个具体的bean吗?你咋又对所有的进行默认处理呢?
不要急,看到方法上的两个参数了吗?类型分别是Object
和String
,第一个参数是bean
的实例,第二个参数是bean
的id或者name属性,我们可以根据第二个参数,来确定我们要处理的具体的bean。
(PS:在上面的refresh()
源码中可知,这个处理,是发生在obtainFreshBeanFactory()
之后,也就是发生在Spring容器实例化和依赖注入之后,但是还没初始化呀...)
2.2.1.2 BeanFactoryPostProcessor的使用
public interface BeanFactoryPostProcessor
void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException;
}
正如其名,BeanFactoryXXX
,BeanFactory级别的处理,是针对整个Bean工厂
进行处理。
这个接口只有一个方法,但是参数是ConfigurableListableBeanFactory
,这个名字是不是有那么一点熟悉...
对的,在咱们的2.1 图解BeanFactory中,ConfigurableListableBeanFactory
是一个特殊的接口,原因是它继承了第二层的所有接口。
咱们想进去,研究下
ConfigurableListableBeanFactory
这个接口的源码。
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
//重点看下这个方法,获取BeanDefinition对象,通过beanName名字
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
}
那么,BeanDefinition对象又是啥呢?这个咱们下面有篇幅讲到的,这里先提一下。
BeanDefinition 源码:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
/**
* Return the name of the parent definition of this bean definition, if any.
*/
String getParentName();
/**
* Set the name of the parent definition of this bean definition, if any.
*/
void setParentName(String parentName);
String getBeanClassName();
void setBeanClassName(String beanClassName);
String getFactoryBeanName();
void setFactoryBeanName(String factoryBeanName);
String getFactoryMethodName();
void setFactoryMethodName(String factoryMethodName);
String getScope();
void setScope(String scope);
boolean isLazyInit();
void setLazyInit(boolean lazyInit);
String[] getDependsOn();
void setDependsOn(String... dependsOn);
boolean isAutowireCandidate();
void setAutowireCandidate(boolean autowireCandidate);
boolean isPrimary();
void setPrimary(boolean primary);
MutablePropertyValues getPropertyValues();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
看上去,好像就是一个entity 实体类
呀,而且,这个类长得好像
里面定义的属性呀。真聪明,就是这样的。
当我们在XML中定义了Bean标签时,Spring会把这些标签解析为一个个的javaBean,这个JavaBean就是
BeanDefinition
对象。
又回到我们的refresh()
方法,此时,我们知道调用postProcessBeanFactory()
方法时,bean刚刚被解析成BeanDefinition
对象,还没有被实例化呢。
我们调用postProcessBeanFactory()
方法,可以拿到BeanDefinition
对象,手动的修改里面的属性值。
Spring容器初始化Bean的大致过程:
1. 定义Bean标签;
2. 将Bean标签解析为BeanDefinition对象;
3. 调用构造方法实例化(IOC);
4. 属性值依赖注入;
而postProcessBeanFactory
方法的执行是在2,3步之间。即解析为BeanDefinition
对象之后,但是初始化之前。
而BeanPostProcess
是在初始化之前和初始化之后均可处理。
其实都是Spring提供的Bean后处理接口。
2.2.2 prepareBeanFactory(beanFactory);的作用
refresh()
方法中,此时Spring把我们配置在XML中的bean都注册之后,会执行prepareBeanFactory(beanFactory);
方法,手动注册一些“特殊”的方法。
类加载器的工作原理
反正小胖不知道什么是类加载器。于是,读下面源码之前先科普一波。
prepareBeanFactory()源码导读:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}