前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简话bean加载

简话bean加载

作者头像
LiosWong
发布2018-10-29 17:46:34
3680
发布2018-10-29 17:46:34
举报
文章被收录于专栏:后端沉思录后端沉思录

首先看示例代码:

代码语言:javascript
复制
<!--no-lazy-init   scope=singleton-->
<bean class="com.lios.service.test.LiosTestA" id="liosTestA"/>
<bean class="com.lios.service.test.LiosTestB" id="liosTestB"/>
<bean class="com.lios.service.test.LiosServiceServiceImpl" id="liosServiceService"/>

ClassPathXmlApplicationContext resource = new ClassPathXmlApplicationContext("app.xml");
BeanFactory beanFactory = resource.getBeanFactory();
LiosServiceServiceImpl liosServiceService = (LiosServiceServiceImpl) beanFactory.getBean("liosServiceService");
liosServiceService.t();

LiosServiceServiceImpl:

代码语言:javascript
复制
@Service
public class LiosServiceServiceImpl {
    private int i = 1;
    @Autowired
    LiosTestA liosTestA;
    public void t(){
        try {
            liosTestA.testA();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===========>");
    }
}

LiosTestA:

代码语言:javascript
复制
@Service
public class LiosTestA {
    @Autowired
    LiosTestB liosTestB;
    public void testA(){
        liosTestB.testB();
    }
}

以上代码就是LiosServiceServiceImpl类中引用了LiosTestA,LiosTestA类中引用了LiosTestB,今天的问题是LiosServiceServiceImpl如何引用LiosTestA,LiosTestA如何引用LiosTestB? 看过源码的同学肯定知道, org.springframework.context.support.AbstractApplicationContext#refresh是spring解析xml、初始化bean的入口,该方法里会调用:

代码语言:javascript
复制
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

这个方法会实例化所有的non-lazy-init单例的bean,毫无疑问,这个方法是分析问题的入口,紧跟进去:

代码语言:javascript
复制
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

再跟进去,调用了 org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法,再跟进去 getBean(beanName);方法,调用了 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)方法:

代码语言:javascript
复制
@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

进入doGetBean方法中,绕了那么多,这个方法才是会真正干事:

代码语言:javascript
复制
//获取beanName
final String beanName = transformedBeanName(name);
//从缓存里获取bean,第一次时毫无疑问,sharedInstance为null
Object sharedInstance = getSingleton(beanName);
//获取RootBeanDefinition,其实用BeanDefinition初始化
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
创建bean
createBean(beanName, mbd, args)

下面分析 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.Class<T>)方法,明显这是把具体实现委托给子类实现了,继续跟: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]),该方法里有一段这样的代码:

代码语言:javascript
复制
instanceWrapper = createBeanInstance(beanName, mbd, args);

继续跟:

代码语言:javascript
复制
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
//.... 后面省略

继续跟 resolveBeanClass()方法,其实该方法里利用反射创建了bean实例,createBeanInstance()方法主要是返回BeanWrapper对象,该对象用bean实例初始化,getWrappedInstance()即可返回bean对象:

代码语言:javascript
复制
/**
 * Return the bean instance wrapped by this object, if any.
 * @return the bean instance, or {@code null} if none set
 */
Object getWrappedInstance();

所以上面的LiosServiceServiceImpl、LiosTestA、LiosTestB都是通过resolveBeanClass()方法创建实例,但是里面的引用的属性如何创建呢,那回到 doCreateBean()方法,继续看下面的代码:

代码语言:javascript
复制
// Initialize the bean instance.
Object exposedObject = bean;
try {
    //初始化bean属性的值
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
}

首先分析 populateBean(beanName,mbd,instanceWrapper),为了直接点,直接看后置处理器BeanPostProcessor:

代码语言:javascript
复制
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvs == null) {
        return;
    }
  }
}

再看InstantiationAwareBeanPostProcessor的实现类, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues:

代码语言:javascript
复制
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);

一直跟进去到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject:

代码语言:javascript
复制
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

再跟进去:

代码语言:javascript
复制
result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);

继续跟进去:

代码语言:javascript
复制
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

doResolveDependency方法会根据属性的属性类型去获取引用,继续跟,可以看到 org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法里的这段代码:

代码语言:javascript
复制
result.put(candidateName, getBean(candidateName));

getBean(candidateName)这个不是调用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)么,没错,是的! 回到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject中:

代码语言:javascript
复制
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}

这段代码会设置bean中的属性的值,一个真正的bean已经完成了. 再回到 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,看这句代码:

代码语言:javascript
复制
initializeBean(beanName, exposedObject, mbd);

跟进后再进入 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods方法:

代码语言:javascript
复制
boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

看到InitializingBean、afterPropertiesSet()是否会想起什么呢,如果一个bean实现了InitializingBean接口后,在bean被容器加载时,自动调用afterPropertiesSet()方法,现在明白是咋回事了吧. 说了那么多,总结下LiosServiceServiceImpl类的加载过程,首先容器会加载LiosServiceServiceImpl或者LiosTestA或者LiosTestB,默认是没有明确顺序之分,如果按照先加载LiosTestA的话,会先创建LiosTestA实例,里面的属性LiosTestB值还是为空,然后设置其属性的值,其实就是调用getBean方法,完成LiosTestA的实例创建后,创建LiosServiceServiceImpl套路完全一样,其属性LiosTestA已经在缓存有了,直接获取即可. 最后,bean的加载远远不止这么复杂,文中有错误之处,麻烦指正!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档