Spring中ImportBeanDefinitionRegistrar源码和使用方式

 更新时间:2024年11月07日 08:49:45   作者:Dubbo-罗  
Spring容器扩展流程总结:1. 定义Mapper层,2. 通过FactoryBean创建代理对象,3. 使用ImportBeanDefinitionRegistrar修改Bean定义,4. 应用自定义注解@LuoyanImportBeanDefinitionRegistrar,5. 配置类中执行后置处理器,6. 启动类中查看源码,希望对大家有所帮助

ImportBeanDefinitionRegistrar源码和使用

第一步

定义的Mapper层:

@Mapper
public interface PayMapper {

	@Select("select * from city")
	public List<Map<String,Object>> list();
}

第二步

使用FactoryBean,通过getObject方式,创建一个对象,放入到spring容器中,这里使用代理对象,放入到spring容器中。

public class MyFactoryBean implements FactoryBean, InvocationHandler {
	private Class aClass;

	public MyFactoryBean(Class aClass) {
		this.aClass = aClass;
	}

	@Override
	public Object getObject() throws Exception {
		Class[] interfaces = new Class[]{aClass};
		Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, this);
		return proxyInstance;
	}

	@Override
	public Class<?> getObjectType() {
		return null;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("代理对象,获取sql语句");
		Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), null);
		Select declaredAnnotation = method1.getDeclaredAnnotation(Select.class);
		System.out.println(declaredAnnotation.value()[0]);
		return null;
	}
}

第三步

spring的ImportBeanDefinitionRegistrar处理器,可以对于spring的BeanDefinitionMap进行操作,可以修改Bean的描述,此时还没有变成对象,这里是把创建注入的类型,创建了构造方法中需要的接口,最后取Bean的名字:payServiceTest,一个BeanDefinition描述。

public class MyImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(PayMapper.class);
		AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
		//TODO: 注入类型
		beanDefinition.setBeanClass(MyFactoryBean.class);
		//TODO: 注入构造方法
		beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("com.luoyan.dao.mapper.PayMapper");
		//TODO: 放入到beanDefinitionMap中
		registry.registerBeanDefinition("payServiceTest",beanDefinition);
	}
}

第四步

自定义注解,把@Import(MyImportDefinitionRegistrar.class)注解,MyImportDefinitionRegistrar类放入到sprinig中运行。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyImportDefinitionRegistrar.class)
public @interface LuoyanImportBeanDefinitionRegistrar {
}

第五步

配置类:需要使用自定义注解@LuoyanImportBeanDefinitionRegistrar,把后置处理器的代码内容执行。

@Configuration
@ComponentScan("com.luoyan")
@MapperScan("com.luoyan.dao.mapper")
@LuoyanImportBeanDefinitionRegistrar
public class AppConfig {
	
}

第六步

启动类:

public static void main(String[] args) {
	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
	applicationContext.register(AppConfig.class);
	applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
	applicationContext.refresh();

	PayMapper payServiceTest = (PayMapper) applicationContext.getBean("payServiceTest");
	payServiceTest.list();
}

源码:

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

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

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			/**
			 *
			 * 因为@Import(xxx.class,xxxx.class)这里可以放多个
			 * importCandidates:表示被放在@Import注解中的class类的报名+类名。比如:com.shadow.imports.MyImportSelector.
			 * candidate:就表示com.shadow.imports.MyImportSelector
			 */
			for (SourceClass candidate : importCandidates) {
				/**
				 * ImportSelector,判断这个MyImportSelector.class是否实现了ImportSelector类
				 */
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					//得到Import的类loadClass
					Class<?> candidateClass = candidate.loadClass();
					//反射实现一个对象
					//这个instantiateClass()方法底层比较复杂
					/******************************instantiateClass()这个方法很重要*************************************/
					//new出来当前实现了ImportSelector接口的类对象
					ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
							this.environment, this.resourceLoader, this.registry);
					Predicate<String> selectorFilter = selector.getExclusionFilter();
					if (selectorFilter != null) {
						exclusionFilter = exclusionFilter.or(selectorFilter);
					}
					if (selector instanceof DeferredImportSelector) {
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						/**
						 * 这里重点
						 * 普通类就是加了@component类
						 */
						//得到所有字符串.//循环引用这类用的是递归,就是说你配置类上有了@Impont,但是你实现了ImportSelector类的类上还是有@Impont
						//TODO: selector表示你实现ImportSelector接口的类的对象.
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						//把名字传入得到importSourceClasses,把这个类添加到annotation这个变量中去了asSourceClasses()这个方法.
						//importClassNames=com.shadow.service.TestService3
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
						//然后又进行循环判断了.继续调用processImports()方法,刚刚进来的时候也是这个方法.
						//递归,这里第二次调用processImports.
						//如果是一个普通类,会进else
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
					}
				}
				/**
				 * ImportBeanDefinitionRegistrar实现了这个接口的类放到了addImportBeanDefinitionRegistrar()方法
				 * importBeanDefinitionRegistrarsMap当中去了。
				 * 而
				 * 实现ImportSelector接口的类却放到了configurationClassesMap当中去了。
				 * 所以在解析这些类的时候使用了不同的方法存放this.reader.loadBeanDefinitions(configClasses);
				 *
				 */
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional com.luoyan.bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									this.environment, this.resourceLoader, this.registry);
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				/**
				 * 普通的
				 */
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					/**
					 * 否则,加入到importStack后调用processConfigurationClass进行处理
					 * processConfiguration里面主要就是把类放到configrationClasses
					 * 可以看到普通类再扫描出来的时候就被注册了
					 * 如果importSelector,回显放到configurationClasses后面进行注册
					 * 注意这里的processConfigurationClass前面已经解释过这个方法了
					 */
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					//processConfigurationClass()这个方法,是继续判断当前普通类是否加了@Configuration注解
					//candidate.asConfigClass(configClass)这个方法,是把通过@Import注解得到的类,执行方法后,得到返回回来的类字符串,反射出来的类.放入到this.importedBy.add(importedBy);集合中
					//真正导入到spring的BeanDefinitionMap中的时候使用到
					processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
				}
			}
		}
		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();
		}
	}
}

源码:

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
	registrars.forEach((registrar, metadata) ->
			/**
			 * 这个registrar.registerBeanDefinitions就是自己实现了ImportBeanDefinitionRegistrar接口
			 * 的类中逻辑,注册到beanMap中的方法
			 */
			registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringSecurit盐值加密的密码验证以及强密码验证过程

    SpringSecurit盐值加密的密码验证以及强密码验证过程

    在密码加密过程中,盐值的使用可以增强密码的安全性,如果忘记存储盐值,将无法验证密码,强密码应包含数字、字母和特殊字符,长度应在8到30位之间,以提高账户安全
    2023-03-03
  • 构建springboot自动生成mapper文件和dao接口项目的步骤和配置方法

    构建springboot自动生成mapper文件和dao接口项目的步骤和配置方法

    这篇文章主要介绍了构建springboot自动生成mapper文件和dao接口项目的步骤和配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Mybatis中${param}与#{param}的区别说明

    Mybatis中${param}与#{param}的区别说明

    这篇文章主要介绍了Mybatis中${param}与#{param}的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • idea maven 构建本地jar包及pom文件的过程

    idea maven 构建本地jar包及pom文件的过程

    这篇文章主要介绍了idea maven 构建本地jar包及pom文件的过程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • Java中的图形界面重绘方式

    Java中的图形界面重绘方式

    这篇文章主要介绍了Java中的图形界面重绘方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • MyBatis中resultMap和resultType的区别详解

    MyBatis中resultMap和resultType的区别详解

    这篇文章主要介绍了MyBatis中resultMap和resultType的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • springboot使用nacos的示例详解

    springboot使用nacos的示例详解

    这篇文章主要介绍了springboot使用nacos的示例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • MyBatis如何使用(三)

    MyBatis如何使用(三)

    这篇文章主要介绍了MyBatis如何使用(三)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • Java创建型模式之建造者模式详解

    Java创建型模式之建造者模式详解

    建造者模式,是一种对象构建模式 它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。本文将通过示例讲解建造者模式,需要的可以参考一下
    2023-02-02
  • SpringBoot JPA实现查询多值

    SpringBoot JPA实现查询多值

    这篇文章主要为大家详细介绍了SpringBoot JPA实现查询多值,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08

最新评论