讲解 Spring 实例化的不同方式及相关生命周期源码剖析(二)

简介: 讲解 Spring 实例化的不同方式及相关生命周期源码剖析(二)

通过 XML 配置,让 Spring 容器解析它去加载实例

<bean id="myFactoryBean" class="com.vnjohn.factorybean.MyFactoryBean"/>
<bean id="mySmartFactoryBean" class="com.vnjohn.factorybean.MySmartFactoryBean"/>

启动测试类

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("factoryBean.xml");
        MyFactoryBean bean = (MyFactoryBean) ac.getBean( "&myFactoryBean");
        System.out.println(bean);
        User bean1 = (User) ac.getBean("myFactoryBean");
        System.out.println(bean1.getUsername());
        User bean2 = (User) ac.getBean("myFactoryBean");
        System.out.println(bean2.getUsername());
        // 若 MyFactoryBean#isSingleton 方法返回 true,以下会返回 true,否则会返回 false
        System.out.println(bean1==bean2);
    }
}

在微服务组件中 open-feign 引入了 FeignClientFactoryBean 客户端类,来实现自定义的一些操作流程~

FactoryBean、BeanFactory 这两者经常会用来进行对比,都是用来创建对象的,只是其用处、作用不同,如下:

  • FactoryBean 接口:不需要遵守 Spring 生命周期顺序实例化类型,getObject—>返回对象、getObjectType—>返回对象类型、isSingleton—>判断是否属于单例;FactoryBean 一共创建了两个对象:第一个是实现 FactoryBean 接口的子类,第二个是通过 getObject 返回的类型,这两个都是交由给 Spring 进行管理的,但需要注意的是,实现 FactoryBean 接口的对象是存放在一级缓存里,但是调用 getObject 方法的对象如果其【isSingleton】方法返回为 false,则每次进行获取时都要去加载一个新的对象,返回 true 是会存入到 FactoryBeanRegistrySupport#factoryBeanObjectCache 中的,下次获取直接从缓存中取用即可.
  • BeanFactory 接口:必须要严格遵守 Spring 生命周期创建对象,从实例化—>初始化、invokeAwareMethod、BPP#before、BPP#after,此流程非常复杂且麻烦

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor 接口实现了 BeanPostProcessor 接口,简称 BPP;一般的 BPP 都是在初始化前后进行调用的,此接口会提前在 createBean 方法内,判别它的实例化前/后方法是否会提前进行 Bean 实例创建,通过 InstantiationAwareBeanPostProcessor 类可以提前生成代理对象,就不会再去执行 doCreateBean 方法

之前在介绍 AbstractApplicationContext#refresh 方法内一些核心的方法时,有提到 registerBeanPostProcessors,它会提前将 Spring 容器中所有 BeanPostProcessor 接口实现类进行注册且创建好实例对象,随即在后续创建业务对象时就可以用到这些 BPP 配置类来进行额外的加载和配置工作了~

关于 InstantiationAwareBeanPostProcessor 方法提前实例化的核心逻辑在 createBean 方法内的 resolveBeforeInstantiation 方法进行实现的,流程图如下:

AbstractBeanFactory#addBeanPostProcessor 方法中会判别是否有自定义的 BPP 实现了 InstantiationAwareBeanPostProcessor 接口,若有的话设置 hasInstantiationAwareBeanPostProcessors=true,后续对所有类型进行该 BPP 调用,若满足对应的类型,那么就提前完成实例化、初始化操作!

public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
  Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
  // 后添加的 BeanPostProcessor 会覆盖之前的,先删除,然后在添加
  this.beanPostProcessors.remove(beanPostProcessor);
  // 此处是为了设置某些状态变量,这些状态变量会影响后续的执行流程,只需要判断是否是指定的类型,然后设置标志位即可
  if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
    // 该变量表示 Beanfactory 是否已注册过 InstantiationAwareBeanPostProcessor
    this.hasInstantiationAwareBeanPostProcessors = true;
  }
  if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
    // 该变量表示 beanFactory 是否已注册过 DestructionAwareBeanPostProcessor
    this.hasDestructionAwareBeanPostProcessors = true;
  }
  // 将 beanPostProcessor 添加到 beanPostProcessors 缓存中
  this.beanPostProcessors.add(beanPostProcessor);
}

先调用 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法完成实例化前操作,在这里可以完成赋值,基于动态代理实现;后调用 InstantiationAwareBeanPostProcessorAdapter#postProcessAfterInitialization 方法完成初始化后操作

在 postProcessBeforeInstantiation 方法执行期间其实就是提前生成该类的 CGLIB 子类代理对象返回,以下代码是创建代理对象的一般过程

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
enhancer.setCallback(new MyMethodInterceptor());
enhancer.create();

关于扩展此 BPP 类型的整个流程讲解

1、定义一个需要提前被 BPP 实现类实例化、初始化的类型

public class BeforeInstantiation {
    public void doSomething(){
        System.out.println("invoke doSomething....");
    }
}

2、自定义实现 MethodInterceptor 接口,用于拦截调用的方法

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("目标方法执行之前:" + method);
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("目标方法执行之后:" + method);
        return o1;
    }
}

3、自定义扩展实现 InstantiationAwareBeanPostProcessor 接口

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    /**
     * 初始化之前处理
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("beanName:" + beanName + "----执行postProcessBeforeInitialization方法");
        return bean;
    }
    /**
     * 初始化之后处理
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("beanName:" + beanName + "----执行postProcessAfterInitialization方法");
        return bean;
    }
    /**
     * 实例化之前处理
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("beanName:" + beanName + "----执行postProcessBeforeInstantiation方法");
        if (beanClass == BeforeInstantiation.class) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback(new MyMethodInterceptor());
            BeforeInstantiation beforeInstantiation = (BeforeInstantiation) enhancer.create();
            System.out.println("创建代理对象:"+beforeInstantiation);
            return beforeInstantiation;
        }
        return null;
    }
    /**
     * 实例化之后处理
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("beanName:" + beanName + "----执行postProcessAfterInstantiation方法");
        return false;
    }
    /**
     * 使用注解时,属性注入处理方法
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        System.out.println("beanName:" + beanName + "----执行postProcessProperties方法");
        return pvs;
    }
}

4、resolveBeforeInstantiation.xml 配置文件,让 Spring 容器能够解析到

<bean id="beforeInstantiation" class="com.vnjohn.resolveBeforeInstantiation.BeforeInstantiation"/>
<bean id="myInstantiationAwareBeanPostProcessor" class="com.vnjohn.resolveBeforeInstantiation.MyInstantiationAwareBeanPostProcessor"/>

5、测试基类

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("resolveBeforeInstantiation.xml");
        BeforeInstantiation bean = ac.getBean(BeforeInstantiation.class);
        bean.doSomething();
    }
}

如此处理之后,BeforeInstantiation 这个 Bean 就会提前在 resolveBeforeInstantiation 方法之后就返回了,不会再继续往下面调用 doCreateBean 方法创建实例对象了!

在 Spring 生命周期中,只执行了实例化之前、初始化之后这两个方法,其他的未做任何处理工作

Supplier

由 Supplier 下函数式接口进行对象创建的操作,不会再去通过反射创建对象,它不局限于具体通过什么方法去创建对象实例,相比 FactoryBean 接口而言,它要求的是类必须实现其接口且必须通过 getObject 方法来返回具体的实例对象,其属于接口规范的范畴.

Supplier 属于 AbstractBeanDefinition 类下的一个属性值【Supplier<?> instanceSupplier】若该 BeanDefinition 有设置这个属性才会提前去创建,没有则不管,按照后续正常 Spring 生命周期加载实例的流程去进行获取

下面通过扩展 Spring 来演示如何实现通过这种方式进行 Bean 实例加载

1、普通类,待加载的对象

public class User {
    private String username;
    public User() { }
    public User(String username) { this.username = username; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

2、函数式编程接口实现,定义一个返回对象的方法

public class CreateSupplier {
    public static User createUser(){
        return new User("vnjohn");
    }
}

3、扩展实现 BFPP,主要为了修改某个对象的 BeanDefnition 信息,为它设置 Supplier 属性

// 在执行 BFPP 时会为 beanName 为 user 的 beanDefinition 添加回调方法,返回的是 user 对象
public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition user = beanFactory.getBeanDefinition("user");
        GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) user;
        genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser);
        genericBeanDefinition.setBeanClass(User.class);
    }
}

4、 supplier.xml 配置文件,让 Spring 容器能够解析到

<bean id="user" class="com.vnjohn.supplier.User"/>
<bean class="com.vnjohn.supplier.SupplierBeanFactoryPostProcessor"/>

5、测试基类

public class TestSupplier {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("supplier.xml");
        User bean = ac.getBean(User.class);
        System.out.println(bean.getUsername());
    }
}

FactoryMethod

当需要验证不同的重载方法中,参数只有一个时具体加载那个:通过权重值来取用对应的构造方法候选者创建实例

通过 factory-method 方式创建对象有两种方式:

  • 通过静态工厂方法创建,要在 bean 标签中指定 factory-method、class 属性为对应的静态工厂 Bean
  • 通过实例工厂方法创建,需要在 bean 标签中指定 factory-bean(实例工厂 Bean) 引用、factory- method(实际工厂中具体要调用的方法)

下面通过 Spring 来演示如何通过实例工厂方法、静态工厂方法这两种方式进行 Bean 实例加载

1、实例工厂、静态工厂要创建的对象

public class Person {
    private int id;
    private String name;
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}


目录
相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
17天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
63 9
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
147 5
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
135 1
|
2月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
51 0
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
87 0
下一篇
DataWorks