Spring源码原理篇一

42次阅读

共计 5520 个字符,预计需要花费 14 分钟才能阅读完成。

Spring 源码原理篇 – 容器初始化 &Bean 后置处理器

本篇主要是讲解 IOC 容器初始化过程中大体进行了哪一些工作,以及 Bean 后置处理器的工作原理和 BeanPostProcessor 在底层的使用。

环境准备

  • 编译器 IDEA
  • maven 依赖 spring-context version:4.3.12.RELEASE
  • maven 依赖 junit version:4.11

BeanPostProcessor 工作原理

实现 BeanPostProcessor 接口的组件,并且在两个方法体内打上断点:

public class BeanPostProcessorDefinition implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {System.out.println("postProcessBeforeInitialization -->"+s+"="+o);
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {System.out.println("postProcessorAfterInitialization -->"+s+"="+o);
        return o;
    }
}

调试后查看方法调用栈如下(如图 1):

在方法调用栈中的 initializeBean(初始化 Bean)方法中,有下面一段类似的伪代码:

initializeBean(param){wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
...
invokeInitMethods(beanName, wrappedBean, mbd);
...
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

这段伪代码的大致意思就是先执行 bean 初始化之前的方法,然后执行 bean 初始化方法,最后执行初始化后的方法。
applyBeanPostProcessorsBeforeInitialization 也是属于方法调用栈的一环,进去有类似一段伪代码:

applyBeanPostProcessorsBeforeInitialization(param)
            throws BeansException {for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {return result;}
        }
        return result;
    }

这段代码通过遍历得到所有的 BeanPostProcessor,然后挨个执行重写的 postProcessBeforeInitialization 方法,倘若有一个方法返回的 bean 为 null,那么循环就会跳出,意味着下面的 postProcessBeforeInitialization 方法不会被执行。在初始化方法后执行的 applyBeanPostProcessorsAfterInitialization 同理也是一样的。
大致总结后置处理器处理 Bean 初始化的过程(如图 2):

容器初始化流程

谈到 spring 的 IOC 容器都离不开两个接口 BeanFactory 和 ApplicationContext,其中 ApplicationContext 是 BeanFactory 的子接口,他们都可以代表 spring 容器。
图 1 打断点所示的方法调用栈可以用来分析容器初始化所进行的工作(以 AnnotationConfigApplicationContext 获取容器为例):

  • init:注册配置类,调用 refresh()刷新容器
  • refresh 过程:

    • registerBeanPostProcessors(Param)注册 Bean 后置处理器用来拦截 Bean 的创建

      • 获取已经定义了需要创建对象的 BeanPostProcessor
      • BeanPostProcessor 分别区分实现 PriorityOrdered、Ordered 的
      • 优先注册实现 PriorityOrdered 接口的 BeanPostProcessor
      • 再给容器中注册实现 Ordered 接口的 BeanPostProcessor
      • 最后注册没实现优先级接口的 BeanPostProcessor(常规的后置处理器)
      • 注册 BeanPostProcessor,实际上 spring 就会创建对象保存在容器中;
        以下是创建 Bean 的流程:
        1、doCreateBean(Param)方法内创建 Bean 实例
        2、populateBean(Param)给 bean 实例属性赋值
        3、initializeBean(Param):初始化 Bean
        4、invokeAwareMethods():处理 Bean 实现 Aware 接口的方法回调
        5、后置处理器处理的流程:图 2 的流程

        • beanFactory.addBeanPostProcessor:将创建完成的 BeanPostProcessor 放在容器中

========== 上面流程则完成对 BeanPostProcessor 的注册和创建

  • refresh 过程接上:

    • finishBeanFactoryInitialization(Param)完成对 BeanFactory 初始化的工作,剩下创建单实例的 bean
    • 单实例 Bean 被创建的方法调用栈:getBean->doGetBean()->getSingleton()-createBean-doCreateBean 然后就是上面重复的创建 Bean 的流程。这一部分 Bean 创建源码细节暂时先缓一缓,待到 spring aspectJ 源码分析再回过头来分析从 getBean 到 doCreateBean 进行了哪一些操作。

BeanPostProcessor 在 spring 底层的使用

在 spring 中,Aware 接口的 Bean 在被初始之后,可以取得一些相对应的资源,也就是说,自定义组件想要使用 Spring 容器底层的一些组件(ApplicationContext,BeanFactory,xxx)的话,自定义组件就需要实现 xxxxAware 接口;在创建对象的时候,会调用接口规定的方法注入相关组件,把 Spring 底层一些组件注入到自定义的 Bean 中;
ApplicationContextAware
可以在 Spring 初始化实例 Bean 的时候,可以通过这个接口将当前的 Spring 上下文传入,即获得 spring 容器,实际开发中,常常封装成一个工具类(方便获取容器获取 bean):

// 将组件注册添加到容器中后可以直接当作工具类
public class SpringContextTool implements ApplicationContextAware {

    private static ApplicationContext context = null;

    public static Object getBean(String beanName) {return context.getBean(beanName);
    }

    public static <T> T getBean(Class<T> clazz){return context.getBean(clazz);
    }

    public static ApplicationContext getContext() {return context;}

    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {context = applicationContext;// 打个断点}
}

原理:在重写方法打个断点,查看方法调用栈
容器看出,在 bean 初始化方法执行之前,先执行后置处理器的 postProcessBeforeInitialization 方法,程序跳进 ApplicationContextAwareProcessor 这个类中(此类实现了 BeanPostProcessor 接口),执行重写的 postProcessBeforeInitialization 方法,在跳到 invokeAwareInterfaces 方法中,判断了当前初始化 bean 时候继承了对应的 Aware,如果是则调用对应的 set 方法,传入对应的资源。

同理还有 **EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware** 也是注入 spring 底层组件
再举个 EmbeddedValueResolverAware 的例子,可以实现这个 aware 接口来完成 Spring 获取 properties 文件属性值:

public class PropertiesUtil implements EmbeddedValueResolverAware {

    private static StringValueResolver resolver;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {this.resolver = resolver;}
    public static String getPropertiesValue(String key) {StringBuilder name = new StringBuilder("${").append(key).append("}");
        return resolver.resolveStringValue(name.toString());
    }
}

需要获取 properties 文件的属性值时可以采用:propertiesUtil.getPropertiesValue(“xxxxxxx”)或者 @value(“xxxx”)来达到获取属性值。
打个断点后发现它的原理和 ApplicationContextAware 是一样的。都是判断了当前初始化 bean 时候继承了对应的 Aware,如果是则调用对应的 set 方法,传入对应的资源。源码如下:

private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }
            if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

ServletContextAware、ServletConfigAware 等几个原理也是差不多类似的。
同理还有 BeanValidationPostProcessor 也实现了 BeanPostProcessor 接口,可用于数据校验,还有 InitDestroyAnnotationBeanPostProcessor 也实现了此接口,主要是用于处理 JSR250 那几个注解的,AutowiredAnnotationBeanPostProcessor 也实现了该接口,用于处理 @autowired 注解装载 bean。总之,Bean 的赋值、注入其他组件,@autowired,@Async,生命周期等都是使用 BeanPostProcessor 来完成的。这一些使用和原理在下一章再分析并补上流程图。

正文完
 0