ObjectFactory是怎么做到延迟依赖查找的?详解ObjectFactory延迟依赖查找

文章目录

  • 一、总览
  • 二、实例
  • 三、原理

一、总览

1、ObjectFactory(或ObjectProvider)可关联某一类型Bean。
2、ObjectFactory和ObjectProvider对象在被依赖注入和依赖查询时并未实时查找关联类型的Bean。
3、当ObjectFactory(或ObjectProvider)调用getObject()方法时,目标Bean才被依赖查找。
4、ObjectFactory(或ObjectProvider)相当于某一类型Bean依赖查找代理对象。

延迟查找并非是Bean的延迟加载,跟@Lazy是两码事,延迟指的就是查找的时候并没有查找到想要查找的那个bean而是查找到了,objectFactory或者objectProvider。并且Provider还比Factory多了能查找出多个的功能。ObjectProvider#getxxx 方法 底层还是通过BeanFactory来进行依赖查找的,但是在进行依赖查找前,可以制定以下规则,比如Bean找到后,再设置额外的属性,完成一些用户的自定义需求;Bean没有找到,该如何处理。

二、实例

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;

/**
 * {@link ObjectFactory} 延迟依赖查找示例
 * @see ObjectFactory
 * @see ObjectProvider
 */
public class ObjectFactoryLazyLookupDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class
        context.register(ObjectFactoryLazyLookupDemo.class);

        // 启动 Spring 应用上下文
        context.refresh();

        ObjectFactoryLazyLookupDemo objectFactoryLazyLookupDemo = context.getBean(ObjectFactoryLazyLookupDemo.class);

        // userObjectFactory userObjectProvider;

        // 只是拿到代理对象
        ObjectFactory<User> userObjectFactory = objectFactoryLazyLookupDemo.userObjectFactory;
        ObjectFactory<User> userObjectProvider = objectFactoryLazyLookupDemo.userObjectProvider;

        System.out.println("userObjectFactory == userObjectProvider : " +
                (userObjectFactory == userObjectProvider)); // false

        System.out.println("userObjectFactory.getClass() == userObjectProvider.getClass() : " +
                (userObjectFactory.getClass() == userObjectProvider.getClass())); // true

        // 实际对象(延迟查找) 此时,才算是真正的查找user对象,标记了@Lazy的对象才真正开始初始化
        System.out.println("user = " + userObjectFactory.getObject());
        System.out.println("user = " + userObjectProvider.getObject());
        System.out.println("user = " + context.getBean(User.class));


        // 关闭 Spring 应用上下文
        context.close();
    }

    @Autowired
    private ObjectFactory<User> userObjectFactory;

    @Autowired
    private ObjectProvider<User> userObjectProvider;

    @Bean
    @Lazy
    public static User user() {
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        return user;
    }
}

延迟查找并不是延迟初始化,延迟初始化还是取决于是否标记了lazy。那么这个延迟查找无论这个查找是否延迟,Bean的初始化都不由查找的时间点来单独决定,而是由标记了lazy的BeanDefinition和延迟查找两个一起来决定Bean的初始化的时间点。

以上演示的代码中,ObjectFactory和ObjectProvider并没有区别。
objectfactory设计是用来对特定bean进行加工的,比如需要进行aop代理,需要对bean个性化的时候可以用objectfactory包装一下,objectprovider一般是spring内部实现某一个功能时可能有考虑不到的地方,spring抽象一个接口类型然后通过objectprovide来进行延长查找你实现了这些接口的类,一般会设计成链式调用,我个人的理解是objectfactory用来增强bean的功能,objectprovide+特点功能扩展接口,来实现把我们的功能和spring内部逻辑整合,比如@configurationpropertys不能解析spel表达式但是spring提供了扩展接口和objectprovide功能,我通过这2个地方扩展出来使得@configurationpropertys也能像@value一样支持spel表达式(springboot2.3才支持的!)

三、原理

1、当调用ObjectFactory的getObject方法时,其实是调用了DependencyObjectProvider的getObject方法。

DependencyObjectProvider 是DefaultListableBeanFactory的内部类。

// org.springframework.beans.factory.support.DefaultListableBeanFactory.DependencyObjectProvider
private class DependencyObjectProvider implements BeanObjectProvider<Object> {

	private final DependencyDescriptor descriptor;

	private final boolean optional;

	@Nullable
	private final String beanName;

	public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {
		this.descriptor = new NestedDependencyDescriptor(descriptor);
		this.optional = (this.descriptor.getDependencyType() == Optional.class);
		this.beanName = beanName;
	}

	@Override
	public Object getObject() throws BeansException {
		if (this.optional) {
			return createOptionalDependency(this.descriptor, this.beanName);
		}
		else {
			// 此时才会触发doResolveDependency
			Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
			if (result == null) {
				throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
			}
			return result;
		}
	}

2、调用的doResolveDependency方法,其实本质上和getBean()方法获取bean是没有差别的。

你可能感兴趣的:(springboot,spring,spring,java,spring,boot)