1. 回顾
通过前两天的学习,我们知道了spring的ioc框架的实现原理。我们常用的各种容器类,其实是最顶层接口BeanFactory的一个实现,它负责注册并管理所有的bean对象。读取并解析xml文件也有一整套机制来实现,另外bean对象在spring中是通过最顶层的接口BeanDefinition来定义的。
今天,在前两天学习的基础上,我们还是要带着问题去找答案:
- 容器是如何根据
bean的名称或类型去获取对象的?
根据前面的学习,可以推测一下这个问题的答案。前面我们说,spring通过一个Map来存储bean-name和bean-definition,那么根据名称获取bean,就是从一个Map中获取指定key的value:
Map<String, Object> beans = new HashMap<String, Object>();
Map<Class, Object> classBeans = new HashMap<String, Object>();
Object bean = beans.get(name);
- spring是否是通过反射来根据
classname获取一个真正的对象的?
2. 从容器中获取对象
User user = applicationContext.getBean(User.class);
跟踪代码进去,找到DefaultListableApplicationContext类的getBean方法:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
......
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
这是spring获取bean的核心方法,获取一个bean的逻辑是:
- 从单例对象缓存中获取该注册对象;
- 如果单例缓存中没有获取到该对象,那么走
else的逻辑;
经过调试,会发现最终是从单例的缓存中获取到了User对象,并没有走到else里面去。结合前两天学到的东西,由此便产生了两个疑惑:
- 究竟是什么时候,实例化的
User bean,并放到了singoton缓存中? - spring中的
bean默认是单例?
2.1. bean默认是否是单例?
要搞清楚bean默认是否是单例的问题很简单,我们只需要找到创建BeanDefinition对象的代码,查看它的属性scope是如何赋值的即可知道:
找到BeanDefinitionParseDelegate解析xml部分的代码:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
...
}
就看上面的代码片段,会发现是否单例是由xml文件中bean的scope属性决定的。我们的demo中user并没有配置此属性,所以其采用的是默认值。
看一下默认值是的源代码和判断是否为单例的源代码:
private String scope = SCOPE_DEFAULT;
/**
* Constant for the default scope name: {@code ""}, equivalent to singleton
* status unless overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
@Override
public boolean isSingleton() {
return SCOPE_SINGLETON.equals(scope) || SCOPE_DEFAULT.equals(scope);
}
通过上面代码片段发现:
- 如果没有指定
scope属性的值,默认值为""。 - 如果
scope的值为singleton或空,则为单例;
由此可见,==只要在配置bean的时候没有特意指定scope属性,spring默认把bean做为单例做管理;==
2.2. 单例在什么时候实例化?
我们在获取user对象的时候,直接便从单例的缓存中获取到了,这说明在容器初始化完成的时候,所有的单例已被实例化,并缓存在了单例的缓存中。
所以要搞清楚这个问题,又不得不回到容器初始化的整个过程中。在之前研究容器初始化的时候,我们留了一个问题。容器初始化的关键函数是refresh,此方法在AbstractApplicationContext抽象类中实现,在ConfigurableApplicationContext接口中定义,它是整个容器初始化的一个模板模式定义。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
...
}
当时研究初始化的时候,重点研究了第二句代码:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();,其他代码当时作为了遗留问题保留下来。
通过调试会发现,单例对象实例化是在
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
这句代码中做的。这句代码最终还是调用了上面的getBean方法,并且由是从单例缓存中没有找到此对象,所以走了else分支,else分支里有创建实例的逻辑。
最低层创建对象的方式是:
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
try {
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}
...
}
具体的细节代码就不贴出来了,最终是使用构造函数对象创建的实例。
3. 总结
学习到今天,我们基本上已经明白了spring ioc容器的整个原理:
- spring提供了一整套工具类来从资源
Resource(xml、注解等)文件中读取、解析所有的bean; - spring使用
BeanDefinition接口的实现类来表示bean的配置; - spring表从配置文件中读取的
bean存储在BeanFactory工厂中,此工厂提供了一系列的注册、管理方法,这也是spring的核心。 - spring容器的初始化,使用了一个模板方法,我们重点研究了两个方法,一个用来创建工厂容器,另外一个我们知道的是提供了单例初始化的功能;
理解到这个程度,我们已初步理解spring的原理,当然具体的一些细节用到的时候再去看代码即可。接下来可以做的事情是:
- 可以尝试自己实现一个最简单的ioc容器了;
- 可以研究一些细节方面的问题,比如说
<bean id="user" ...的其他属性是怎么实现的,如init-method等; - 可以研究更详细的一些功能,如
BeanPostProcessor、InitializingBean和DisposableBean接口功能是如何实现的,等。