讲解 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 + '\'' +
                '}';
    }
}


目录
相关文章
|
19天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
22天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
12天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
17天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
23天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0
|
24天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
34 0
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
24天前
|
人工智能 监控 安全
Java+Spring Cloud +Vue+UniApp微服务智慧工地云平台源码
视频监控系统、人员实名制与分账制管理系统、车辆管理系统、环境监测系统、大型设备监测(龙门吊、塔吊、升降机、卸料平台等)、用电监测系统、基坑监测系统、AI算法分析(安全帽佩戴、火焰识别、周界报警、人员聚众报警、升降机超载报警)、安全培训、设备监测。
28 4