Spring中的ImportBeanDefinitionRegistrar接口详解

 更新时间:2023年09月20日 08:45:00   作者:信仰_273993243  
这篇文章主要介绍了Spring中的ImportBeanDefinitionRegistrar接口详解,ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象,注册到Spring容器中,功能类似于注解@Service @Component,需要的朋友可以参考下

接口功能介绍

描述:ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象,注册到Spring容器中,功能类似于注解@Service @Component。

很多三方框架集成Spring的时候,都会通过该接口,实现扫描指定的类,然后注册到spring容器中,比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通过该接口实现的自定义注册逻辑。

1、ImportBeanDefinitionRegistrar接口实现类,只能通过@Import注解的方式来注入,通常把@Import修饰在启动类或配置类。

2、使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,启动时会触发ImportBeanDefinitionRegistrar接口的方法,将其中要注册的类注册成bean。

3、实现该接口的类拥有注册bean的能力。

//接口所有抽象方法,合并看就一个注册BeanDefinition的方法
public interface ImportBeanDefinitionRegistrar {
	//把自定义的类封装成BeanDefinition对象,注册到Spring里面去
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
		registerBeanDefinitions(importingClassMetadata, registry);
	}
	//我们平时重写这个就可以了
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}
}

使用案例

1、案例1

自定义业务类UserServiceTest,通过ImportBeanDefinitionRegistrar将注册Spring容器中。在通过spring容器获取Bean=UserServiceTest

//业务类
public class UserServiceTest {
	/**
	 * 获取用户名称
	 * @return 用户名称
	 */
	public String getUserName(){
		return "测试";
	}
}

ImportBeanDefinitionRegistrar实现类

//注意这里不能加注解,要通过Import导入进去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	//业务类转成bd,注册到spring容器中注入
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//1、通过Bd工具类生成bd对象,只是这个Db对象比较纯洁没有绑定任何类
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
		GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
		//2、设置bd绑定的类型
		beanDefinition.setBeanClass(UserServiceTest.class);
		//3、注册到spring容器中
		registry.registerBeanDefinition("userServiceTest",beanDefinition);
	}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfigClassTest {
	//在配置类导入ImportBeanDefinitionRegistrar实现类
}
//测试
public static void main(String[] args) {
	AnnotationConfigApplicationContext AnnotationConfigApplicationContext =
			new AnnotationConfigApplicationContext(AppConfigClassTest.class);
	UserServiceTest userServiceTest = AnnotationConfigApplicationContext.getBean("userServiceTest", UserServiceTest.class);
	String userName = userServiceTest.getUserName();
	System.out.println(userName);
}

如果只是把业务类注册到Spring容器中我们通过其他注解就可以了,那么ImportBeanDefinitionRegistrar有没有更高级的玩法。

2、案例2

public interface UserServiceTestInterface {
	public void list();
}
//因为是接口,但是spring的容器里面是不允许注入接口的,只能是接口的实现类。如果我们写个去实现接口,那就没有什么意义了,没必要搞得那么复杂,这次我们通过代理类来完成对接口的实现。
public class MyInvocationHandler implements InvocationHandler {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("代理类逻辑代码");
		return null;
	}
}
//注意这里不能加注解,要通过Import导入进去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	 @Override
	 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //通过工具类生成一个bd,只是这个Db对象比较纯洁没有绑定任何类
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
		GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
		//设置Bean的类型MyInvocationHandler,类型是实现类的类型,不是接口类型。因为在实例化的时候,调用的是设置类型所以对应的构造方法。
		beanDefinition.setBeanClass(MyInvocationHandler.class);
		//注册到Spring容器中进去
		registry.registerBeanDefinition("userServiceTest",beanDefinition);
    }
}
//通过配置类,导入ImportBeanDefinitionRegistrar的实现类
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfigClassTest {
}

测试

public static void main(String[] args) {
	AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class);
	//通过name获取Bean,注意此时的bean类型不是UserServiceTestInterface类型,而是MyInvocationHandler
	Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest");
	if(userServiceTestInterface instanceof MyInvocationHandler){
		//代码实际会走到这里,把object转成带代理类。
		MyInvocationHandler u = (MyInvocationHandler) userServiceTestInterface;
		//生成接口UserServiceTestInterface的代理对象
		UserServiceTestInterface o = (UserServiceTestInterface) Proxy.newProxyInstance(MainTest.class.getClassLoader(), new Class[]{UserServiceTestInterface.class}, u);
	}
}

3、案例3

定义一个业务接口,通过FactoryBean+InvocationHandler来生成该接口的代理类,无需手动写业务接口的实现类,很多底层框架就是这样实现的。

  • InvocationHandler:主要是通过Invoke方法来拦截业务接口的方法
  • FactoryBean:主要是用来将生成的代理类。
  • ImportBeanDefinitionRegistrar:在这里的作用就是帮忙我们把自定义的FactoryBean注册到Spring中
//业务接口
public interface UserServiceTestInterface {
	public void list();
}

自定义FactoryBean这样我们控制Bean的创建的过程,实现InvocationHandler用来拦截业务接口的方法。

//创建代理类,代理UserServiceTestInterface接口,UserServiceTestInterface接口方法在执行前后都会被invoke方法拦截
//FactoryBean可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程
public class MyFactoryBean implements FactoryBean, InvocationHandler {
	//为了使这个类更好地扩展。创建更多的接口,我们定义一个参数,让他们通过参数传递进来。
	private Class classs;
	//添加一个有参的构造方法。
	public MyFactoryBean(Class classs){
		this.classs = classs;
	}
	//拦截Class的所有方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("diaoyonlejiekou123");
		return null;
	}
	//返回bean的对象。spring会自动把它add到容器里面去。
	@Override
	public Object getObject() throws Exception {
		Class[] clazzs = new Class[]{classs};//目标类集合。
		//通过proxy来得到代理对象。本来最有一个参数需要穿代理类对象,但因为本类实现了InvocationHandler,所以只需传this
		Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this);
		return proxy;//返回的这个对象,会把加到spring的容器中。
	}
	//返回要添加到容器里bean的类型
	@Override
	public Class<?> getObjectType() {
		return this.classs;
	}
}

自定义ImportBeanDefinitionRegistrar实现类,把我们自定义的FactoryBean注册到Spring中。

//注意这里不能加注解,要通过Import导入进去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//通过工具类生成一个bd,只是这个Db对象比较纯洁没有绑定任何类
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
		//为什么要转成GenericBeanDefinition这种类型。因为GenericBeanDefinition有更多修改bd属性的方法。后面我会介绍为什么要修改属性。
		GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
		//这里很重要。getConstructorArgumentValues是为了获取该bd的所有构造方法,因为我们重写了有参构造方法,所有我们需要带参数过去 
		//不然spring没法帮我们实例化,addGenericArgumentValue是添加参数,该代码会执行两步
		//第一步是匹配对应的构造方法,第二步是实例化。
		beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserServiceTestInterface.class.getName());
		//因为代理对象类型的,实例化的时候走的是代理类的构造方法
		beanDefinition.setBeanClass(MyFactoryBean.class);
		//注册bd
		registry.registerBeanDefinition("userServiceTest",beanDefinition);
	}
}

测试

public static void main(String[] args) {
	AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class);
	//通过name获取Bean
	Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest");
	//针对这种场景Bean的类型是,通过FactoryBean的getObjectType方法返回的。
	UserServiceTestInterface u = (UserServiceTestInterface) userServiceTestInterface;
	u.list();
}

​到此这篇关于Spring中的ImportBeanDefinitionRegistrar接口详解的文章就介绍到这了,更多相关ImportBeanDefinitionRegistrar接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java EasyExcel利用填充模版动态生成多个sheet页

    Java EasyExcel利用填充模版动态生成多个sheet页

    这篇文章主要为大家详细介绍了Java EasyExcel如何利用填充模版动态生成多个sheet页,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-12-12
  • 关于cron表达式每天整点执行一次的问题

    关于cron表达式每天整点执行一次的问题

    这篇文章主要介绍了关于cron表达式每天整点执行一次的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • SpringBoot集成极光推送完整实现代码

    SpringBoot集成极光推送完整实现代码

    本文主要介绍了SpringBoot集成极光推送完整实现代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • Java实现打字游戏

    Java实现打字游戏

    这篇文章主要为大家详细介绍了Java实现打字游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • springboot使用Logback把日志输出到控制台或输出到文件

    springboot使用Logback把日志输出到控制台或输出到文件

    这篇文章给大家介绍springboot项目使用日志工具Logback把日志不仅输出到控制台,也可以输出到文件的操作方法,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2020-10-10
  • 通过Java实现设置Word文档页边距的方法详解

    通过Java实现设置Word文档页边距的方法详解

    页边距是指页面的边线到文字的距离。通常可在页边距内部的可打印区域中插入文字和图形等。今天这篇文章将为您展示如何通过编程方式,设置Word 文档页边距,感兴趣的可以了解一下
    2023-02-02
  • 深入Synchronized和java.util.concurrent.locks.Lock的区别详解

    深入Synchronized和java.util.concurrent.locks.Lock的区别详解

    本篇文章是对Synchronized和java.util.concurrent.locks.Lock的区别进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • 解决IDEA克隆代码后在右下角没有git分支的问题

    解决IDEA克隆代码后在右下角没有git分支的问题

    这篇文章主要介绍了解决IDEA克隆代码后在右下角没有git分支的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • java中关于移位运算符的demo与总结(推荐)

    java中关于移位运算符的demo与总结(推荐)

    下面小编就为大家带来一篇java中关于移位运算符的demo与总结(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • SpringBoot HATEOAS用法简介(入门)

    SpringBoot HATEOAS用法简介(入门)

    这篇文章主要介绍了SpringBoot HATEOAS用法简介(入门),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10

最新评论