首页 > 其他分享 >Spring的IoC容器之BeanFactory

Spring的IoC容器之BeanFactory

时间:2022-11-07 23:13:50浏览次数:44  
标签:容器 BeanFactory 对象 Spring bean 实例 IoC throws

Spring IoC 容器之 BeanFactory

首先,Spring 提供了两种容器类型:BeanFactory 和 ApplicationContext。

  • BeanFactory:基础容器类型,提供了完整的 IoC 服务支持。默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中某个对象的时候,猜对该对象进行初始化以及依赖注入操作。适用于资源有限、功能要求不多的场景。
  • ApplicationContext:在 BeanFactory 的基础上构建,提供了其他高级功能,如事件发布、国际化支持等。在该类型容器启动后,所管理的对象默认全部初始化并完成依赖注入。适用于资源充足、要求功能完善的场景。

本文主要介绍 BeanFactory。

public interface BeanFactory {

	String FACTORY_BEAN_PREFIX = "&";

	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	boolean containsBean(String name);

	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);
}

BeanFactory 的对象注册

BeanFactory 只是一个接口,我们需要一个实现来管理对象。DefaultListableBeanFactory 间接实现了 BeanFactory,且实现了 BeanDefinitionFactory。该接口担任着 Bean 的注册管理的角色。

每一个受管控的 bean,在容器中都会有一个 BeanDefinition 的实例与之对应,该 BeanDefinition 的实例负责保存对象的所有必要信息,包括对象的 class 类型、是否是抽象类、构造方法参数以及其他属性等。当客户端向 BeanFactory 请求相应对象时,BeanFactory 会通过这些信息给客户端返回一个完备可用的对象实例。RootBeanDefinition 和 ChildBeanDefinition 是两个主要实现类。

实例如下:

public class BeanFactoryTest {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory beanFactory = bind(beanRegistry);
        FXNewsProvider fxNewsProvider1 = beanFactory.getBean(FXNewsProvider.class);
        FXNewsProvider fxNewsProvider2 = beanFactory.getBean(FXNewsProvider.class);
        System.out.println(fxNewsProvider1 == fxNewsProvider2);  // true

    }

    public static BeanFactory bind(BeanDefinitionRegistry registry) {
        AbstractBeanDefinition fxNewsProvider = new RootBeanDefinition(FXNewsProvider.class);
        AbstractBeanDefinition dowJonesNewsListener = new RootBeanDefinition(DowJonesNewsListener.class);
        AbstractBeanDefinition dowJonesNewsPersister = new RootBeanDefinition(DowJonesNewsPersister.class);

        // 将 bean 定义注册到容器中
        registry.registerBeanDefinition("fxNewsProvider", fxNewsProvider);
        registry.registerBeanDefinition("dowJonesNewsListener", dowJonesNewsListener);
        registry.registerBeanDefinition("dowJonesNewsPersister", dowJonesNewsPersister);

        // 通过构造方法注入
        // ConstructorArgumentValues arguments = new ConstructorArgumentValues();
        // arguments.addIndexedArgumentValue(0, dowJonesNewsListener);
        // arguments.addIndexedArgumentValue(1, dowJonesNewsPersister);
        // fxNewsProvider.setConstructorArgumentValues(arguments);

        // 通过 setter 注入
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.addPropertyValue(new PropertyValue("dowJonesNewsListener", dowJonesNewsListener));
        mutablePropertyValues.addPropertyValue(new PropertyValue("dowJonesNewsPersister", dowJonesNewsPersister));
        fxNewsProvider.setPropertyValues(mutablePropertyValues);

        return (BeanFactory) registry;
    }
}

bean 的 scope

Spring 容器最初只有 singleton 和 prototype 两种 scope 类型,后面又引入了三种:request、session 和 global session。

  • singleton:在容器中只存在一个实例,所有对该对象的引用都将共享这个实例。该实例从容器启动,并在第一次初始化之后,将一直存活到容器退出。
  • prototype:容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例返回。虽然这种类型的对象的实例化和属性设置等工作是由容器负责的,但是主要对象返回后,容器不再拥有该对象的引用,即不再管理该对象。

容器启动的一些细节

Spring 的 IoC 容器,会以某种方式加载 Configuration Metadata(各种配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。

Spring 的 IoC 容器可以分成两个阶段,容器启动阶段和 bean 的实例化阶段。

容器的启动阶段

容器启动伊始,首先会通过某种途径加载 Configuration Metadata。在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的 Configuration Metadata 进行解析和分析,并将分析后的信息编组为相应的 BeanDefinition,待注册到相应的 BeanDefinitionRegistry,这样容器启动的主要工作就结束了。

该阶段主要侧重于对象管理信息的收集,一些验证性或者辅助性的工作也可以在这个阶段完成。

bean 的实例化阶段

在第一阶段中,bean 的定义信息都已经注册到 BeanDefinitionRegistry 中。当某个请求通过容器的 getBean 方法明确请求到某个对象或者因依赖关系容器需要隐式调用 getBean 方法时,就会触发 bean 的实例化。

在该阶段,容器首先会检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的 BeanDefinition 所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当对象转配完毕之后,容器会立即返回该对象。

插手容器的启动

BeanFactoryPostProcessor

Spring 提供了 BeanFactoryPostProcessor 的机制,可以允许在容器实例化相应的对象之前,对注册到容器的 BeanDefinition 所保存的信息进行修改。

@FunctionalInterface
public interface BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

简单看一下 Spring 内置的几个常见的 BeanFactoryPostProcessor。

  1. PropertySourcesPlaceholderConfigurer

    通常情况下,我们会将例如数据库连接、服务器连接信息等放在一个 properties 文件中,这样,当系统资源变动的话,只需要修改相应的 properties 文件即可。PropertySourcesPlaceholderConfigurer 允许我们在 XML 或者在 Bean 中使用占位符(PlaceHolder,形如 ${property})。

    简单来说,当 BeanFactory 在第一阶段加载完成所有的配置信息时,BeanFactory 中保存的对象的属性还是以占位符的形式存在,如 ${jdbc.url}。当 PropertySourcesPlaceholderConfigurer 作为 BeanFactoryPostProcessor 被应用时,它会使用 properties 文件中配置信息替换相应的 BeanDefinition 中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化 bean 时,bean 定义的属性值就是最终替换完成的了。

bean 的生命周期

BeanFactory 的源码注释中描述了 bean 的生命周期:

实例化 bean:

  • BeanNameAware's setBeanName
  • BeanClassLoaderAware's setBeanClassLoader
  • BeanFactoryAware's setBeanFactory
  • EnvironmentAware's setEnvironment
  • EmbeddedValueResolverAware's setEmbeddedValueResolver
  • ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
  • ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
  • MessageSourceAware's setMessageSource (only applicable when running in an application context)
  • ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
  • ServletContextAware's setServletContext (only applicable when running in a web application context)
  • postProcessBeforeInitialization methods of BeanPostProcessors
  • InitializingBean's afterPropertiesSet
  • a custom init-method definition
  • postProcessAfterInitialization methods of BeanPostProcessors

销毁 bean:

  • postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors
  • DisposableBean's destroy
  • a custom destroy-method definition

bean 的实例化和 BeanWrapper

容器内部实例化的时候,采用策略模式来决定采用何种方式进行初始化。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的 bean 的实例或者动态生成其子类。

org.springframework.beans.factory.support.InstantiationStrategy 是实例化策略的抽象接口,其子类 SimpleInstantiationStrategy 实现了简单的对象实例化功能,通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。CglibSubclassingInstantiationStrategy 继承了 SimpleInstantiationStrategy 以反射方式实例化对象的功能,并且通过 CGLIB 的动态字节码生成功能,满足方法注入所需的对象实例化需求。容器默认采用 CglibSubclassingInstantiationStrategy

public interface InstantiationStrategy {

	/**
	 * Return an instance of the bean with the given name in this factory.
	 */
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
			throws BeansException;

	/**
	 * Return an instance of the bean with the given name in this factory,
	 * creating it via the given constructor.
	 */
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			Constructor<?> ctor, Object... args) throws BeansException;

	/**
	 * Return an instance of the bean with the given name in this factory,
	 * creating it via the given factory method.
	 */
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, Method factoryMethod, Object... args)
			throws BeansException;

}

容器根据 RootBeanDefinition 结合 CglibSubclassingInstantiationStrategy 以及不同的 bean 定义类型,就可以返回实例化完成的对象实例。但是,不是直接返回构造完成的对象实例,而是以 BeanWrapper 对构造完成的对象实例进行包裹。

至此,实例化对象的步骤结束。

BeanWrapper 接口有个实现类 BeanWrapperImpl,设置 bean 的相应属性值。BeanWrapper 定义继承了 PropertyAccessor 接口,可以以统一的方式对对象属性进行访问;另外,同时继承了 PropertyEditorRegistryTypeConverter 接口,提供属性设值的类型转换等功能。

示例:

public class BeanWrapperTest {
    public static void main(String[] args) throws Exception {
        final Object fxNewsProvider = Class.forName("com.stm.spring.ioc.bean.FXNewsProvider").newInstance();
        final Object dowJonesNewsListener = Class.forName("com.stm.spring.ioc.bean.DowJonesNewsListener").newInstance();
        final Object dowJonesNewsPersister = Class.forName("com.stm.spring.ioc.bean.DowJonesNewsPersister").newInstance();

        BeanWrapper newsProvider = new BeanWrapperImpl(fxNewsProvider);
        newsProvider.setPropertyValue("dowJonesNewsListener", dowJonesNewsListener);
        newsProvider.setPropertyValue("dowJonesNewsPersister", dowJonesNewsPersister);

        System.out.println(newsProvider.getWrappedInstance() instanceof FXNewsProvider); // true
        System.out.println(dowJonesNewsListener == newsProvider.getPropertyValue("dowJonesNewsListener")); // true
        System.out.println(dowJonesNewsPersister == newsProvider.getPropertyValue("dowJonesNewsPersister")); // true
    }
}

至此,完成了对象属性赋值的步骤。

各种 Aware 接口的处理

当对象实例化完成并且相关属性以及依赖设置完成之后,spring 容器会检查当前对象实例是否实现了一系列以 Aware 命名结尾的接口定义。如果是,则将这些 Aware 接口定义中规定的依赖注入给当前对象实例。

BeanPostProcessor

注意:BeanPostProcessor 用于 bean 的实例化阶段,BeanFactoryPostProcessor 用于容器启动阶段。BeanPostProcessor 会处理容器内所有符合条件的实例化后的对象实例。该接口定义如下:

public interface BeanPostProcessor {

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other {@code BeanPostProcessor} callbacks.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

常见的使用 BeanPostProcessor 的场景,是处理标记接口实现类,或者为当前对象提供代理实现。例如,ApplicationContext 中的 Aware 接口就是通过 BeanPostProcessor 实现的。参考 ApplicationContextAwareProcessor 源码:

class ApplicationContextAwareProcessor implements BeanPostProcessor {

	private final ConfigurableApplicationContext applicationContext;

	private final StringValueResolver embeddedValueResolver;


	/**
	 * Create a new ApplicationContextAwareProcessor for the given context.
	 */
	public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
	}


	@Override
	@Nullable
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}

}

另外,在 CommonAnnotationBeanPostProcessor 类中提供了对 PostConstructPreDestroy 的支持。

InitializingBean 和 init-method

这两种方式都是在 BeanPostProcessor 的前置处理之后调用。比如在某些情况下,某个业务对象实例化完成后,还不能处于可以使用的状态,这个时候可以让业务对象实现 InitializingBean,在 afterPropertiesSet 方法中完善对该业务对象的处理。

public interface InitializingBean {

	/**
	 * Invoked by the containing {@code BeanFactory} after it has set all bean properties
	 * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
	 * <p>This method allows the bean instance to perform validation of its overall
	 * configuration and final initialization when all bean properties have been set.
	 */
	void afterPropertiesSet() throws Exception;

}

另外,可以指定 init-method,完成类似功能。

DisposableBean 和 destroy-method

两种方式都是为了当 singleton 对象在容器销毁时,提供一个用于对象销毁的回调。

public interface DisposableBean {

	/**
	 * Invoked by the containing {@code BeanFactory} on destruction of a bean.
	 * @throws Exception in case of shutdown errors. Exceptions will get logged
	 * but not rethrown to allow other beans to release their resources as well.
	 */
	void destroy() throws Exception;

}

总结

BeanFactory 作为 spring 提供的基础 IoC 容器,支持对象的注册以及依赖关系绑定。除了 BeanFactory,spring 还提供了 ApplicationContext 容器,其构建于 BeanFactory 之上,提供了更多丰富的功能。

标签:容器,BeanFactory,对象,Spring,bean,实例,IoC,throws
From: https://www.cnblogs.com/shitianming/p/16867852.html

相关文章

  • springboot多环境配置
    springboot整合多个环境配置springboot默认读取配置文件名称application,且多个环境配置文件必须按照如下命名Application-dev.yml开发环境application-test.yml......
  • springboot整合nacos config详解
    springboot整合nacos详解@value:读取application文件@ConfigurationProperties:读取指定文件@PropertySource:自定义文件引入@PropertySource+@Value:读取自定义文件@Pro......
  • Spring 的jar包下载
    Spring官方Spring官网(https://spring.io/)Spring的下载进入Spring官网,选择Progects下的SpringFramework选项,进入详情页面Spring官网Springframework......
  • springboot整合多个环境配置
    springboot整合多个环境配置​ springboot默认读取配置文件名称application,且多个环境配置文件必须按照如下命名Application-dev.yml 开发环境application-test.y......
  • springboot 上传文件设置文件大小限制
     报错内容:org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateExce......
  • SpringMVC添加依赖包
    `4.0.0<groupId>org.example</groupId><artifactId>ssmbuild</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.com......
  • Springboot中使用GSON报错 An attempt was made to call the method com.google.gso
    错误如下: Description:Anattemptwasmadetocallthemethodcom.google.gson.GsonBuilder.setLenient()Lcom/google/gson/GsonBuilder;butitdoesnotexist.Itscl......
  • Springboot 整合 SpringCloud组件-Eureka 微服务 EurekaClient(二)
    我们已经完成了Eureka注册中心Server的搭建与部署,那么这篇,我们就来创建一个微服务EurekaClient,将其成功注册到我们的注册中心去。 同样,创建一个springboot项目,起名clien......
  • Springboot 整合 SpringCloud组件-Gateway 网关服务 (四)
    这篇我将介绍的是网关服务,那么从标题已经知道我们整合的组件时gateway;也许有人说,为啥不用zuul,这个组件也可以用于做网关。至于这两组件的性能比较与区别,我们来看一个表格简......
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
    (建议初学者把这个系列前面的都看看,那对于redis的基本使用操作及一些消息的订阅分布都是没问题的了)Redis,一个缓存数据库。不仅仅是能用于对数据的CRUD,也可以作为一个消息中间......