Spring源码原理篇(一)

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

内容简介:本篇主要是讲解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源码原理篇(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

数学拾遗

数学拾遗

加黑蒂 / 清华大学出版社 / 2004-8 / 49.00元

Beginning graduate students in mathematics and other quantitative subjects are expected to have a daunting breadth of mathematical knowledge ,but few have such a backgroud .This book will help stedent......一起来看看 《数学拾遗》 这本书的介绍吧!

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

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具