Spring源码原理篇(一)

栏目: Java · 发布时间: 4年前

内容简介:本篇主要是讲解IOC容器初始化过程中大体进行了哪一些工作,以及Bean后置处理器的工作原理和BeanPostProcessor在底层的使用。实现BeanPostProcessor接口的组件,并且在两个方法体内打上断点:调试后查看方法调用栈如下(如图1):

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):

Spring源码原理篇(一)

在方法调用栈中的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源码原理篇(一)

容器初始化流程

谈到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来完成的。这一些使用和原理在下一章再分析并补上流程图。

Spring源码原理篇(一)


以上所述就是小编给大家介绍的《Spring源码原理篇(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

YC创业营: 硅谷顶级创业孵化器如何改变世界

YC创业营: 硅谷顶级创业孵化器如何改变世界

兰德尔·斯特罗斯 (Randall Stross) / 苏健 / 浙江人民出版社 / 2014-8-1 / CNY 52.90

在互联网创业成本日益降低、融资却越来越难的今天,硅谷的Y Combinator因何成为全世界创业者趋之若鹜的创业圣地?为什么25岁左右的青年最适合创业?创业者如何才能在遴选面试中脱颖而出?为什么YC特别看好那些主要由黑客组成的创业团队? YC真的歧视女性吗?如何想出能够赢得投资的新点子?创业者应该如何寻找联合创始人? 获准进入Y Combinator及其创业公司全程跟踪批量投资项目的第一人,......一起来看看 《YC创业营: 硅谷顶级创业孵化器如何改变世界》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换