4、spring核心源码解析之自定义bean三种方式@import、ImportSelector、ImportBeanDefinitionRegistrar

前言

​ 随着spring boot的流行,@Enable*设计模式渐渐兴起,通常一个注解就可以帮我们完成一个很使用功能。而在大多数的带有@Enable注解中,我们通常会看到 一个注解@Import去帮助我们导入一些类(bean)。个人觉得这是一个非常重要的spring ioc容器的扩展点之一,它可以让我们通过一个注解去给容器中注入很多bean,这也是spring boot中最常用、最基础的注解。

​ 本文将主要介绍@Import、ImportSelector、ImportBeanDefinitionRegistrar的使用场景,以及源码分析他们注册bean的原理。(其实本文的源码分析原理再上一篇文章中已经讲到了,所以本文只会分析主要源码)。

1. 自定义注册bean之@Import的使用

 单纯的使用@Import注解注入bean,这种用法比较少,最起码要配合类似于@Enable*注解类,来使用@Impor。比如通过一个@Enable的功能注解来控制某个功能的开关。
public class TestImport {

	@Bean
	public Person person(){
		Person person = new Person();
		person.setName("coyhzx");
		return person;
	}
}

​ 同时在启动类上导入TestImport类

@Import(TestImport.class)
@Configuration
public class MyApp {}
#输出结果
person
com.upanda.app.test.Person@ef9296d

2. 自定义注册bean之ImportSelector的使用

​ ImportSelector是一个接口,接口中提供了一个方法selectImports。实现该方法,返回要导入的bean的名称数组,即可导入bean,importingClassMetadata是注解的元数据。

	/**
	 * 选择并返回应基于导入@Configuration类的{@link AnnotationMetadata}导入的类的名称。
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

使用方法:

public class TestImportSelect implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.upanda.app.test.Person"};
	}
#输出结果
person
com.upanda.app.test.Person@gd2296d

​ 同时在启动类上导入TestImportSelect类,该导入方式与直接导入一个类的区别就是,它可以拿到一个注解的元数据以及它可以同时注册多个类,返回类名即可。比如说,我们可以根据注解里面的某个属性值类注入一个或倒戈bean。

3. 自定义注册bean之ImportBeanDefinitionRegistrar的使用

​ TestImportBeanDefinitionRegistrar是一个接口,目前有两个默认实现default方法,可以通过registerBeanDefinitions方法获取我们的容器,同时可以自定义创建bean以及向容器中注册bean的逻辑。

public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClassName("com.upanda.app.test.Person");
		beanDefinition.setScope("singleton");
		registry.registerBeanDefinition("person",beanDefinition);
	}
}
#输出结果
person
com.upanda.app.test.Person@da9296d

4. 自定义注册bean之spring经典实现–spring开启动态代理功能@EnableAspectJAutoProxy

​ sprinig framework开启aop动态代理功能,使用@EnableAspectJAutoProxy注解实现,而该注解使用@Import注解导入自定义的bean的这种方式是最经典、也是spring framework框架中内部使用的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	//是否使用cglib动态代理,默认使用jdk动态代理
	boolean proxyTargetClass() default false;

	//代理应由AOP框架公开为{@code ThreadLocal} ,以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。 默认情况下为关闭,即不能保证{@code AopContext}访问将起作用
	boolean exposeProxy() default false;

}

​ 该注解会为我们导入AspectJAutoProxyRegistrar这个类,该类通过实现ImportBeanDefinitionRegistrar接口来自定义bean的注入逻辑。具体不涉及到AOP的核心功能,简单理解通过该方式注入了aop相关的后置处理器bean对象(后续aop源码中在具体讲解)。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

5. 自定义Bean三种方式的源码解析

​ 其实在上一篇文章@Configuration源码的解析中,以及提及到了@import注解在哪里进行处理了,这边继续进行上一章进行分析,侧重于@import的导入。

​ 在ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法中parser.parse(candidates);解析配置类里,有一个处理import的方法processImports。

// Process any @Import annotations
//处理@Import注解,getImports(sourceClass)方法获取类上@Import导入的类
processImports(configClass, sourceClass, getImports(sourceClass), true);

方法getImports(sourceClass)获取到@import导入的类。

	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		//1. 搜集Imports导入的类
		collectImports(sourceClass, imports, visited);
		return imports;
	}

	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {

		if (visited.add(sourceClass)) {
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();
				if (!annName.equals(Import.class.getName())) {
					//递归获取@Import注解导入的类
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}

在配置类解析器ConfigurationClassParser类中的processImports方法里,该方法作用非常大。其作用我在源码中有注释,但是在这里也总结一下。

1.处理了@import导入的类,并将其加入到ConfigurationClassParser的配置类集合configurationClasses中。

2.处理实现了ImportSelector接口导入的类,递归处理selectImports中导入的类,最终也会加入到ConfigurationClassParser的配置类集合configurationClasses中。

3.处理实现ImportBeanDefinitionRegistrar接口导入的类,同时将该类直接放入ConfigurationClassParser配置类的importBeanDefinitionRegistrars集合中。

​ 这里并没有注册bean,但是这里将@import、实现ImportSelector接口导入的类,以及实现ImportBeanDefinitionRegistrar接口的类,都放入了配置类的各种集合中,在后文中,注册配置类中就会将这些类全部注入到容器中。

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							//2 处理ImportSelector,注册自定义的bean
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							//将读取到的类名数组,转换成SourceClass类数组
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							//递归处理导入的类,同时将其加入到解析类的集合中,也因为导入的类可能实现了ImportBeanDefinitionRegistrar接口 --默认导入的会最后的else中,当作普通的配置类进行处理
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						// 处理ImportBeanDefinitionRegistrar,注册自定义的bean
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						//3 将实现了ImportBeanDefinitionRegistrar接口的类,加入到配置类中的importBeanDefinitionRegistrars集合中
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						//1. 处理@Import导入的既不是实现ImportSelector ,也不是实现ImportBeanDefinitionRegistrar的类
						//且把它当作@Configuration类处理,也有一点递归调用
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						//将其加入到ConfigurationClassParser解析类的集合
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

​ processConfigurationClass这个递归处理配置类的方法中,会把每个配置类都加入到解析器的configurationClasses类中。

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
		//重点:如果souurceClass不为空,则将器方法解析类的配置集合类中,后续会用到
		this.configurationClasses.put(configClass, configClass);
	}

​ 真正注入bean的是在ConfigurationClassPostProcessor的this.reader.loadBeanDefinitions(configClasses)中。

//读取注册配置类(包括beanMethod、@import、实现importSelect、实现),并且注册到容器中
this.reader.loadBeanDefinitions(configClasses);

​ 该reader为spring专门为配置类读取创建的一个类ConfigurationClassBeanDefinitionReader。reader.loadBeanDefinitions()方法会遍历加载所有的配置bean。至于在往下面,调用我们的容器为我们注入bean的逻辑相对而言很简单,有兴趣的可以打个断点看看,也可以参考我的spring源码分析的第一篇文章注入bean的流程图。

	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		for (ConfigurationClass configClass : configurationModel) {
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}
	//为配置类注册bean的定义
	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

		if (configClass.isImported()) {
			//注册@import导入的类
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			//注册@Bean导入的类
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
		//注册@ImportedResources导入的类
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		//注册ImportBeanDefinitionRegistrars
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

​ bean的定义以及特殊bean的注入、自定义注册源码解析了很多了,接下来可能就会开始spriing的DI在源码中是怎样实现的了。

​ 请敬请期待我的下一篇文章~

上一篇:3、spring核心源码解析之@Configuration注解详解
下一篇:5、spring核心源码解析之DI依赖注入、自动装配@Autowired和@Resource

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBean生命周期源码主要涉及到Bean的初始化、依赖注入、后置处理、销毁等各个阶段。以下是一个简化的Spring Bean生命周期源码解析: 1. 实例化(Instantiation):Spring通过反射机制实例化Bean对象。这是通过调用构造函数或者工厂方法来完成的。 2. 属性注入(Property Injection):在实例化后,Spring将通过依赖注入(Dependency Injection)来设置Bean的属性值。这是通过调用setter方法或者直接访问字段来完成的。 3. Aware接口回调:Spring会检查Bean是否实现了某些Aware接口(如BeanNameAware、ApplicationContextAware等),并通过回调方法将相关的信息注入到Bean中。 4. BeanPostProcessor前置处理(BeanPostProcessor Pre-Initialization):Spring会检查是否有注册的BeanPostProcessor,如果有,则在Bean初始化前调用它们的postProcessBeforeInitialization方法。 5. 初始化(Initialization):初始化阶段包括两个步骤: a. 调用自定义的初始化方法(如通过@PostConstruct注解标记的方法或实现了InitializingBean接口的afterPropertiesSet方法)。 b. 调用BeanPostProcessor后置处理方法postProcessAfterInitialization。 6. 使用(In Use):此时Bean已经初始化完成,可以使用了。 7. 销毁(Destruction):在容器关闭或者手动销毁时,Spring会调用销毁方法来释放Bean占用的资源。 a. 调用自定义的销毁方法(如通过@PreDestroy注解标记的方法或实现了DisposableBean接口的destroy方法)。 b. 调用BeanPostProcessor后置处理方法postProcessBeforeDestruction。 以上是简化的Spring Bean生命周期源码解析,实际的源码会更加复杂和详细。Spring通过BeanPostProcessor和各种回调接口,提供了丰富的扩展点和生命周期管理功能,使开发者能够在Bean的不同阶段进行自定义操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值