今天想来继续学习一下spring的部分源码,上次学习spring源码我是使用的spring boot,这次继续使用spring boot来跟踪和学习。以前就是大概走看了spring boot大概的启动过程以及内置tomcat的创建和启动。今天就想深入的去学习一点知识,就准备看下 beanfactory这个类,即DefaultListableBeanFactory的源码,但是因为这个类的体系比较庞大,所以自己只能先简单的了解一下。
一、继承体系
首先明白一点,beanfactory是spring的一个很基础性接口,或者说根接口。spring boot的context容器中注入beanfactory实际就是DefaultListableBeanFactory实例,我们通过idea自带的工具先来看看,它体系结构是怎么样的,看下面的图:

我感觉还是非常复杂的,其中涉及到的注入通用接口主要有三个,BeanFactory、SingletonBeanRegistry和AliasRegistry。我们先看下这几个接口的方法都有哪些吧!
先看AliasRegistry,这个就是一个通用接口,主要管理bean的别名的,看下方法有那些:
public interface AliasRegistry {
// 使用指定别名替换具体bean
void registerAlias(String name, String alias);
// 从注册容器中移除某个别名bean
void removeAlias(String alias);
// 给定的名称是否在注册容器中时以别名存在
boolean isAlias(String name);
// 返回给定名称的所有别名
String[] getAliases(String name);
}
这几方法感觉还是很好理解,关于它的实现这里就先不看了,继续看下个接口SingletonBeanRegistry。
SingletonBeanRegistry,看名字就知道这个是一个单列bean的注册容器,它主要是为单列bean提供通用的注册接口,可以通过BeanFactory实现来实现,以便以统一的方式公开其单例管理工具。我们还是看下代码
public interface SingletonBeanRegistry {
// 使用给定的名称注册单列bean对象,这个bean会被认为是已经完全初始化过的
void registerSingleton(String beanName, Object singletonObject);
// 根据名称返回某个单例bean,只会检查已经实例化的bean
@Nullable
Object getSingleton(String beanName);
// 检查容器中是否已经注册了某个单例bean,其名称与给定名称一致
boolean containsSingleton(String beanName);
// 返回注册容器中所有单例bean的名称
String[] getSingletonNames();
// 返回注册容器中单例bean的数量
int getSingletonCount();
// 返回注册容器中使用单例互斥锁
Object getSingletonMutex();
}
方法不多,也比较好理解,其实看到这里我觉得大家应该也猜到了,存放单例bean的容器肯定是一个map来实现的。
接着就是非常重要的BeanFactory接口,它是用于访问Spring bean容器的根接口。这是bean容器的基本客户端视图,像其他的ListableBeanFactory和ConfigurableBeanFactory接口都是为了实现特定的目的。BeanFactory是应用程序组件的中央注册容器,并集中应用程序组件的配置。需要注意的是,最好通过DI(依赖注入)通过set或构造方法来配置应用程序对象,而不是使用像BeanFactory查找一样“拉”的形式配置。 Spring的DI功能是使用BeanFactory接口及其子接口实现的。下面看代码:
public interface BeanFactory {
// 工厂bean的前缀,用来区分FactoryBean创建的bean和FactoryBean。比如,如果bean名称为myJndiObject,这是一个FactoryBean,而&myJndiObject将返回工厂,而不是工厂返回的实例。
String FACTORY_BEAN_PREFIX = "&";
// 返回给定名称bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
Object getBean(String name) throws BeansException;
// 返回指定名称和类型bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
// 返回指定名称或者其他参数bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
// 这个方法可以指定显式构造参数或工厂方法参数,覆盖bean定义中指定的默认参数(如果有的话)。
Object getBean(String name, Object... args) throws BeansException;
// 返回唯一匹配给定对象类型的bean实例
<T> T getBean(Class<T> requiredType) throws BeansException;
// 返回指定类型或者其他参数bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
// 这个方法可以指定显式构造参数或工厂方法参数,覆盖bean定义中指定的默认参数。
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 此bean工厂是否包含具有给定名称的bean定义或外部注册的单例实例
boolean containsBean(String name);
// 根据名称查找的这个bean是否为共享单例。也就是说,getBean方法总是否会返回相同的实例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 根据名称查找的这个bean是否为prototype。也就是说,getBean方法总是否会返回不同的实例
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 判断具有给定名字的bean是否与指定的类型一致。
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
// 判断具有给定名字的bean是否与指定的类型一致。
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 返回给定名称的bean的类型。
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 返回给定名称的bean别名
String[] getAliases(String name);
}
这方法还是挺多的,但是也都还好理解,现在有个大概了解就好了,主要还是看具体的实现。关于接口方法就先到这里,这里主要是了解具体的体系以及一些基本的方法。
2、DefaultListableBeanFactory创建
自己在看源码的时候一直没有发现context中的beanFactory是什么时候创建的,一直跟源码才发现创建context时就会创建beanFactory,下面通过debug方法来跟踪看一下。下面是SpringApplication的run方法,整个应用的启动基本上在这里完成的,以前有专门说过这部分,这里就不再细述,我们主要看 context创建这里。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();// 创建context
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
下面是createApplicationContext方法的代码
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
这里主要做了两件事,一是确定创建的context类型,给定了三种,分别是SERVLET、REACTIVE、default三种类型,他们对应的context容器也不相同
SERVLET:这个我们比较熟悉,但是在spring boot里面创建的context为:AnnotationConfigServletWebServerApplicationContext实例,名字比较长,但是名字也说明了很多东西,注解、可配置、servletWebServer。
REACTIVE:作为spring5以后新增的MVC应用,主要是基于事件驱动的WebFlux,这个自己也还没有去了解过,这种应用创建的context为:AnnotationConfigReactiveWebServerApplicationContext,和上面的类似,也是注解、可配置、但是是响应式的WebServer。应该也是基于Netty实现,应该和vert.x相似,应该也会使用lambda表达式来开发。
default:创建的context:AnnotationConfigApplicationContext,这个主要是应用于非web环境,但是使用spring一般是Web环境,非Web环境我暂时还没遇到过。
二就是实例化context容器,我们看下BeanUtils.instantiateClass方法的代码
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
return instantiateClass(ctor);
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
这个方法有进一步作了判断,判断应用是否为使用kotlin开发,如果是的话返回这个类的一级构造函数(kotlin和Java稍微有点区别,自己也是很久之前看了点kotlin的基础),否则返回这个类的默认无参构造函数,接着调用instantiateClass方法对其进行实例化,我们接着往下看instantiateClass方法
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
这个方法,先通过反射工具类,设置该构造函数访问权限为可访问,接着依然需要判断是否为kotlin,是的话调用kotlin相关方法,否则调用Constructor的newInstance方法创建进行实例化。我debug模式下看到调用的为AnnotationConfigServletWebServerApplicationContext类默认的构造函数。
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
看到这里是否觉感觉有些奇怪,为什么没看到beanFactory实例化呢?肯定不止我一个人这么想,大家似乎忘了一件事,子类构造函数如果没有显性调用父类构造函数,那么它会隐性调用父类的无参构造函数,即调用那么都会默认调用super()。我们看下AnnotationConfigServletWebServerApplicationContext的继承体系

这个好像更复杂了,我们先不管这个。先找它的直接父类ServletWebServerApplicationContext,看默认构造函数
public ServletWebServerApplicationContext() {
}
没有找到,那么继续向上找,看GenericApplicationContext的默认构造,如下:
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
这里可以看到直接new了一个DefaultListableBeanFactory对象,并其赋值给成员变量beanFactory。也就是说必须要先完成DefaultListableBeanFactory的创建,然后才能完成整个应用context的实例化。今天暂时先到这里,关于DefaultListableBeanFactory的相关内容以后再继续吧(感觉至少要3篇),因为这个内容太多了,而且感觉也比较难,但是这个确实非常非常重要。
网友评论