2022-08-23  阅读(44)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/series/article/1779174008

一个 bean 经历了 createBeanInstance() 被创建出来,然后又经过一番属性注入,依赖处理,历经千辛万苦,千锤百炼,终于有点儿 bean 实例的样子,能堪大任了,只需要经历最后一步就破茧成蝶了。这最后一步就是初始化,也就是 initializeBean(),所以这篇文章我们分析 doCreateBean() 中最后一步:初始化 bean。

                protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                            // 激活 Aware 方法
                            invokeAwareMethods(beanName, bean);
                            return null;
                        }, getAccessControlContext());
                    }
                    else {
                        // 对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAware
                        invokeAwareMethods(beanName, bean);
                    }
            
                    Object wrappedBean = bean;
                    if (mbd == null || !mbd.isSynthetic()) {
                        // 后处理器
                        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
                    }
            
                    try {
                        // 激活用户自定义的 init 方法
                        invokeInitMethods(beanName, wrappedBean, mbd);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(
                                (mbd != null ? mbd.getResourceDescription() : null),
                                beanName, "Invocation of init method failed", ex);
                    }
                    if (mbd == null || !mbd.isSynthetic()) {
                        // 后处理器
                        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
                    }
                    return wrappedBean;
                }

初始化 bean 的方法其实就是三个步骤的处理,而这三个步骤主要还是根据用户设定的来进行初始化,这三个过程为:

  1. 激活 Aware 方法
  2. 后置处理器的应用
  3. 激活自定义的 init 方法

激活 Aware 方法 Aware ,英文翻译是意识到的,感知的,Spring 提供了诸多 **Aware 接口用于辅助 Spring Bean 以编程的方式调用 Spring 容器,通过实现这些接口,可以增强 Spring Bean 的功能。 Spring 提供了如下系列的 Aware 接口:

  • LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ
  • BeanClassLoaderAware:加载Spring Bean的类加载器
  • BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI
  • ResourceLoaderAware:底层访问资源的加载器
  • BeanFactoryAware:声明BeanFactory
  • PortletConfigAware:PortletConfig
  • PortletContextAware:PortletContext
  • ServletConfigAware:ServletConfig
  • ServletContextAware:ServletContext
  • MessageSourceAware:国际化
  • ApplicationEventPublisherAware:应用事件
  • NotificationPublisherAware:JMX通知
  • BeanNameAware:声明Spring Bean的名字

invokeAwareMethods() 源码如下:

                private void invokeAwareMethods(final String beanName, final Object bean) {
                    if (bean instanceof Aware) {
                        if (bean instanceof BeanNameAware) {
                            ((BeanNameAware) bean).setBeanName(beanName);
                        }
                        if (bean instanceof BeanClassLoaderAware) {
                            ClassLoader bcl = getBeanClassLoader();
                            if (bcl != null) {
                                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                            }
                        }
                        if (bean instanceof BeanFactoryAware) {
                            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
                        }
                    }
                }

这里代码就没有什么好说的,主要是处理 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。关于 Aware 接口,后面会专门出篇文章对其进行详细分析说明的。 后置处理器的应用 BeanPostProcessor 在前面介绍 bean 加载的过程曾多次遇到,相信各位不陌生,这是 Spring 中开放式框架中必不可少的一个亮点。 BeanPostProcessor 的作用是:如果我们想要在 Spring 容器完成 Bean 的实例化,配置和其他的初始化后添加一些自己的逻辑处理,那么请使用该接口,这个接口给与了用户充足的权限去更改或者扩展 Spring,是我们对 Spring 进行扩展和增强处理一个必不可少的接口。

                public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
                        throws BeansException {
            
                    Object result = existingBean;
                    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                        Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
                        if (current == null) {
                            return result;
                        }
                        result = current;
                    }
                    return result;
                }
            
                @Override
                public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
                        throws BeansException {
            
                    Object result = existingBean;
                    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                        Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
                        if (current == null) {
                            return result;
                        }
                        result = current;
                    }
                    return result;
                }

其实逻辑就是通过 getBeanPostProcessors() 获取定义的 BeanPostProcessor ,然后分别调用其 postProcessBeforeInitialization()postProcessAfterInitialization() 进行业务处理。 激活自定义的 init 方法 如果熟悉 <bean> 标签的配置,一定不会忘记 init-method 方法,该方法的执行就是在这里执行的。

               protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
                        throws Throwable {
                    // 首先会检查是否是 InitializingBean ,如果是的话需要调用 afterPropertiesSet()
                    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((PrivilegedExceptionAction<Object>) () -> {
                                    ((InitializingBean) bean).afterPropertiesSet();
                                    return null;
                                }, getAccessControlContext());
                            }
                            catch (PrivilegedActionException pae) {
                                throw pae.getException();
                            }
                        }
                        else {
                            // 属性初始化的处理
                            ((InitializingBean) bean).afterPropertiesSet();
                        }
                    }
            
                    if (mbd != null && bean.getClass() != NullBean.class) {
                        String initMethodName = mbd.getInitMethodName();
                        if (StringUtils.hasLength(initMethodName) &&
                                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                                !mbd.isExternallyManagedInitMethod(initMethodName)) {
                            // 激活用户自定义的 初始化方法
                            invokeCustomInitMethod(beanName, bean, mbd);
                        }
                    }
                }

首先检查是否为 InitializingBean ,如果是的话需要执行 afterPropertiesSet(),因为我们除了可以使用 init-method 来自定初始化方法外,还可以实现 InitializingBean 接口,该接口仅有一个 afterPropertiesSet() 方法,而两者的执行先后顺序是先 afterPropertiesSet()init-method。 关于这篇博客的三个问题,LZ 后面会单独写博客来进行分析说明。 经过六篇博客终于把 Spring 创建 bean 的过程进行详细说明了,过程是艰辛的,但是收获很大,关键还是要耐着性子看。 更多阅读


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文