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

 更新时间:2023年04月28日 11:01:33   作者:雷X峰  
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理,下面这篇文章主要给大家介绍了关于spring初始化源码之关键类和扩展接口的相关资料,需要的朋友可以参考下

前言

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类型)

类关系图如下:

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

1.2、关键类之XmlBeanDefinitionReader

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

1.3、关键类之ClassPathXmlApplicationContext

先上类关系图:

这个就是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、BeanPostProcessor: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的创建。

通过实现该接口也可以完成对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,BeanFactoryPostProcessor,
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 BeansException {
        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;
    }
}

3、启动类

public class AppMain {
    public static void main(String[] args) {
        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();
    }
}

代码执行结果:

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

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

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

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

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

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

总结

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

相关文章

  • Java 的 FileFilter文件过滤与readline读行操作实例代码

    Java 的 FileFilter文件过滤与readline读行操作实例代码

    这篇文章介绍了Java 的 FileFilter文件过滤与readline读行操作实例代码,有需要的朋友可以参考一下
    2013-09-09
  • 什么是springMVC 视图和视图解析器实例代码

    什么是springMVC 视图和视图解析器实例代码

    在 springMVC 中的目标方法最终返回都是一个视图,返回的视图都会由一个视图解析器来处理,这篇文章主要介绍了什么是springMVC 视图和视图解析器,需要的朋友可以参考下
    2023-09-09
  • 使用Aop的方式实现自动日志记录的方式详细介绍

    使用Aop的方式实现自动日志记录的方式详细介绍

    这篇文章主要介绍了使用Aop的方式实现自动日志记录,通过监听器去监听,当访问到具体的类方法,通过aop切面去获取访问的方法,然后将日志记录下来,就这种方式给大家介绍的非常详细,需要的朋友可以参考下
    2022-04-04
  • java中isEmpty和isBlank的区别小结

    java中isEmpty和isBlank的区别小结

    Java中的isEmpty和isBlank都是用来判断字符串是否为空的方法,但在不同的情况下有所区别,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • java多线程之停止线程的方法实例代码详解

    java多线程之停止线程的方法实例代码详解

    这篇文章主要介绍了java多线程之停止线程的方法实例代码详解,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • Java基础之练习打印三角形

    Java基础之练习打印三角形

    这篇文章主要介绍了Java基础之练习打印三角形,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • 关于JSqlparser使用攻略(高效的SQL解析工具)

    关于JSqlparser使用攻略(高效的SQL解析工具)

    这篇文章主要介绍了关于JSqlparser使用攻略(高效的SQL解析工具),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Java中值类型和引用类型的比较与问题解决

    Java中值类型和引用类型的比较与问题解决

    这篇文章主要给大家介绍了关于Java中值类型和引用类型的比较与问题解决方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12
  • Spring温故而知新系列教程之AOP代理

    Spring温故而知新系列教程之AOP代理

    Spring AOP 是代理模式的应用,可以使用JDK提供的Proxy类或通过字节码增强来实现。下面这篇文章主要给大家介绍了关于Spring之AOP代理的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-05-05
  • Java中BEAN与EJB的区别浅析

    Java中BEAN与EJB的区别浅析

    这篇文章主要介绍了Java中BEAN与EJB的区别浅析,本文总结了它们之间的不同之处,需要的朋友可以参考下
    2015-03-03

最新评论