开发者

spring初始化源码之关键类和扩展接口详解

开发者 https://www.devze.com 2023-04-29 10:22 出处:网络 作者: 雷X峰
目录前言1、关键接口和类1.1、关键类之 DefaultListableBeanFactory1.2、关键类之XMLBeanDefinitionReader1.3、关键类之ClassPathXmlApplicationContext2、spring初始化过程中对外暴露的扩展接口3、扩展点的启动顺序
目录
  • 前言
  • 1、关键接口和类
    • 1.1、关键类之 DefaultListableBeanFactory
    • 1.2、关键类之XMLBeanDefinitionReader
    • 1.3、关键类之ClassPathXmlApplicationContext
  • 2、spring初始化过程中对外暴露的扩展接口
    • 3、扩展点的启动顺序
      • 总结

        前言

        spring的IOC容器初始化流程很复杂,本文只关注流程中的关键点,勾勒出主要轮廓,对容器的初始化有一个整体认识,以下基于spring的5.1.2.RELEASE分析,本文演示代码地址:https://github.com/amapleleaf/spring-code.git

        本文分为两部分:《spring初始化源码浅析之关键类和扩展接口》、《spring初始化源码浅析之代码浅析》

        1、关键接口和类

        1.1、关键类之 DefaultListableBeanFactory

        该类核心功能:

        1、提供注册、获取等等与BeanDefinition对象操作相关的方法,BeanDefinition缓存在DefaultListableBeanFactory的beanDefinitionMap变量(ConcurrentHashMap类型)

        2、提供创建、注册、获取、单例等等跟bean对象操作相关的方法供ApplicationContext使用,bean对象缓存在DefaultSingletonBeanRegistry的singletonObjects 变量(ConcurrentHashMap类型)

        类关系图如下:

        spring初始化源码之关键类和扩展接口详解

        从类图中看到DefaultListableBeanFactory实现了很多接口,spring 根据该类的功能定义了不同层次的接口。接口核心功能主要分两类:1、AliasRegistry、BeanDefinitionRegistry接口主要提供BeanDefinition和alias注册、获取的方法,2、左半部分*BeanFactory相关接口、SingletonBeanRegistry接口提供对象的创建、缓存和获取等方法

        1.2、关键类之XmlBeanDefinitionReader

        该类负责分析xml中bean的定义,并解析成BeanDefinition对象,然后调用DefaultListableBeanFactory的注册方法缓存到DefaultListableBeanFactory中

        1.3、关键类之ClassPathXmlApplicationContext

        先上类关系图:

        spring初始化源码之关键类和扩展接口详解

        这个就是spring上下文,是spring启动的入口类,从父类AbstractApplicationContext的refresh()方法中可以看出该类的主要功能:设置springContext.xml路径、创建DefaultListableBeanFactory、提供对象创建过程中的各种扩展点、事件的注册和分发等等。

        2、spring初始化过程中对外暴露的扩展接口

        1、BeanNameAware:void setBeanName(String name);

        该bean获取自己在DefaultListableBeanFactory中的id或name ,在spring框架里用的多,我们一般很少用到。

        2、BeanFactoryAware:void setBeanFactory(BeanFactory beanFactory)

        获取创建该bean的DefaultListableBeanFactory对象,可以从该对象中回去bean对象,不过绝大多数时候我们是从ApplicationContext中来获取。

        3、ApplicationContextAware:void setApplicationContext(ApplicationContext applicationContext)

        获取该bean所属的applicationContext,从而可以获取到该上下文的bean对象。自己写一个工具类实现该接口然后在配置文件中配置或加上@Component注解,通过这个工具类就很方便的在应用中动态获取bean对象,这种工具类在很多老的项目中几乎是一个标配。

        4、InitializingBean:void afterPropertiesSet()

        spring提供两中方式来对bean初始化后的扩展,一种是实现InitializingBean接口,一种是使用通过init-method方法指定,spring初始化bean时在执行InitializingBean接口的afterPropertiesSet方法后就紧接着执行init-method指定的方法。使用init-method不会对spring产生依赖因此使用频率较高,但由于这种方式使用反射方式来调用所以性能上低于直接调用 InitializingBean接口的afterPropertiesSet方法,后面会有相应的代码分析。

        5、指定init-method

        用的较多,可以理解为spring在bean对象初始化完后会通过反射的方式来执行该bean中init-method指定的方法。通过在xml文件中的bean标签配置init-method或在该bean的方法上使用@PostConstruct注解达到效果。

        6、BeanFactoryPostProcessor:void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

        从后面的代码分析中我们发现,实现BeanFactoryPostProcessor接口的bean的创建及接口方法的调用时间早于普通bean的创建。实现该接口可以拿到beanFactory对象,然后就看可以在普通bean对象创建之前进行干预调整,PropertyPlaceholderConfigurer类大家应该比较熟悉,该类实现BeanFactoryPostProcessor接口,在postProcessBeanFactory方法中将beanDefinitionMap中所有的BeanDefinition中的含有占位符的值修改为指定属性文件中的值,这样在创建对象的时候就能获取到真实值。

        7、BeanPojsstProcessor:Object postProcessBeforeInitialization(Object bean, String beanName);

             BeanPostProcessor:Object postProcessAfterInitialization(Object bean, String beanName);

        该接口需要注意与BeanFactoryPostProcessor接口的区别:

        BeanFactoryPostProcessor接口:A实现了该接口,spring启动的时候,在所有普通bean对象创建之前会先创建A对象并调用其postProcessBeanFactory方法,方法参数为beanFactory。

        BeanPostProcessor接口:A实现了该接口,spring在创建普通的bean 对象B时,在B对象初始化之前将B对象的实例和beanname作为入参调用A的前置方法postProcessBeforeInitialization,在B对象初始化之后将B对象的实例和beanname作为入参调用A的后置方法postProcessAfterInitialization。由此也可知实现该接口bean的创建时间早于普通bean的创建。开发者_Go学习

        通过实现该接口也可以完成对bean对象的调整,但与BeanFactoryPostProcessor还是有本质的区别,实现BeanFactoryPostProcessor可以理解为对创建的模板的调整,是对BeanDefinition对象的调整,而BeanPostProcessor则是在对象过程中做的临时的调整,是对创建好的bean对象的调整

        使用BeanPostProcessor需要注意:

        ①、前置、后置方法需要将修改后的bean对象返回这样getbean时才能获取到正确的bean对象

        ②、针对layz的bean对象创建则不会回调该接口的方法

        8、ApplicationListener:void onApplicationEvent(E event)

        spring上下文启动完成后回调该接口,比较常用。

        3、扩展点的启动顺序

        1、HelloWorldService bean对象

        public class HelloWorldService implements BeanFactoryAware,BeanNameAware,BeanFactoryPhttp://www.devze.comostProcessor,
        BeanPostProcessor,InitializingBean ,
        ApplicationListener<ContextRefreshedEvent>,ApplicationContextAware {
            private String name;
            private AtomicInteger count = new AtomicInteger(1);
            private String getSeq(){
                return count.getAndIncrement()+"->";
            }
            public  HelloWorldService(){
                System.err.println(getSeq()+"HelloWorldService constructor");
            }
            public void initMethod(){
                System.err.println(getSeq()+"init method");
            }
            public void sayHello(){
                System.err.println(getSeq()+name+"say:hello,world");
            }
         
            public String getName() {
                return name;
            }
         
            public void setName(String name) {
                this.name = name;
            }
            public void setBeanName(String name) {
                System.err.println(getSeq()+"BeanNameAware.setBeanName:"+name);
            }
         
            public void setBeanFactory(BeanFactory beanFactory) throws BeansExcephpption {
                System.err.println(getSeq()+"BeanFactoryAware.setBeanFactory:"+beanFactory);
            }
         
            public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
                System.err.println(getSeq()+"ApplicationContextAware.setApplicationContext:->"+applicationContext);
            }
         
            public void afterPropertiesSet() {
                System.err.println(getSeq()+"InitializingBean.afterPropertiesSet");
            }
         
            public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
                BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("peopleService");
                beanDefinition.getPropertyValues();
                MutablePropertyValues m = beanDefinition.getPropertyValues();
                m.addPropertyValue("content", "i am ok");
                System.err.println(getSeq()+"BeanFactoryPostProcessor.postProcessBeanFactory 将peopleService的content属性修改为i am ok");
            }
         
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                System.err.println(getSeq()+"BeanPostProcessor.postProcessBeforeInitialization->"+beanName);
                return bean;
            }
         
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                System.err.println(getSeq()+"BeanPostProcessor.postProcessAfterInitialization->"+beanName);
                return bean;
            }
         
            public void onApplicationEvent(ContextRefreshedEvent event) {
                System.err.println(getSeq()+"ApplicationListener.onApplicationEvent: Refreshed->"+event.getApplicationContext());
            }
        }

        2 、非lazy的普通bean对象,PeopleService

        public class PeopleService{
            private String content="";
            public PeopleService(){
                System.err.println("PeopleService constructor");
            }
            public void say(){
                System.err.println("PeopleService say:["+content+"]");
            }
         
            public String getContent() {
                return content;
            }
         
            public void setContent(String content) {
                this.content = content;
          nHlAuTj  }
        }

        3、启动类

        public class AppMain {
            public static void main(String[] args) {
            php    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
                System.err.println("===============================================");
                HelloWorldService helloWorldService =  applicationContext.getBean("helloWorldService",HelloWorldService.class);
                helloWorldService.sayHello();
         
                PeopleService peopleService =  applicationContext.getBean("peopleService",PeopleService.class);
                peopleService.say();
            }
        }

        代码执行结果:

        spring初始化源码之关键类和扩展接口详解

        从输入结果中我们得到以下信息:

        1、HelloWorldService 实现 BeanFactoryPostProcessor接口所以创建时间早于普通非lazy的bean对象PeopleService

        2、1-7为HelloWorldService 创建过程输出的日志,可以看到各扩展接口的执行顺序

        3、第7步之后开始创建PeopleService对象,创建过程中回调用HelloWorldService(实现了BeanPostProcessor接口) 的前置和后置方法

        4、spring上下文启动完成后发布ContextRefreshedEvent事件,输出第10步日志

        《spring初始化源码浅析之代码浅析》将从代码来分析spring的初始化流程

        总结

        到此这篇关于spring初始化源码之关键类和扩展接口的文章就介绍到这了,更多相关spring初始化源码内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号