Spring-ImportBeanDefinitionRegistrar接口介绍

一、本文内容分类

1、接口功能
2、接口运用场景
3、使用案例
4、注意事项

二、接口功能介绍

描述: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实现类,将UserServiceTest注册到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容器中,我们通过其他注解就可以做到了,比如@Service、@Component,那么ImportBeanDefinitionRegistrar有没有更高级的玩法。

2、案例2

定义一个业务接口

public interface UserServiceTestInterface {
	public void list();
}

将UserServiceTestInterface接口注册到Spring容器中,这里和上面案例不一样,上面的案例是注册一个具体的类,到Spring容器中,那么对于接口怎么注入。

//spring的容器里面是不允许注入接口的,只能是接口的实现类。
//这次我们通过代理类来完成对接口的实现,再把这个代理类注册到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);
    }
}
//注意此时MyInvocationHandler还没有和UserServiceTestInterface接口关联上

//通过配置类,导入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用来拦截业务接口的方法。
关于FactoryBean的介绍可以看我这篇文章《FactoryBean是什么》

//创建代理类,目标是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();
}


ImportBeanDefinitionRegistrar是被谁处理了?

因为@Import是修饰在配置类里面的,所以在解析配置类的时候,也就是ConfigurationClassPostProcessor处理的时候,会解析@Import注解,然后会判断@Import导入类是否是ImportBeanDefinitionRegistrar的子类,执行重写的registerBeanDefinitions方法。这样我们的类就被加到BD集合里面去了。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
ImportBeanDefinitionRegistrarSpring Framework 中的一个接口,用于实现自定义的 Bean 注册逻辑。它可以通过在@Configuration 类上使用@Import 注解来注册一个实现了 ImportBeanDefinitionRegistrar 接口的类,从而向 Spring 容器中动态注册 Bean。 使用 ImportBeanDefinitionRegistrar 可以实现更加灵活和动态的 Bean 注册方式,可以根据特定的条件或逻辑来动态地注册一些 Bean。通常情况下,我们可以将一些复杂的 Bean 注册逻辑封装在 ImportBeanDefinitionRegistrar 的实现类中,通过编程的方式来注册 Bean,而不是直接使用 @Bean 注解。 具体使用 ImportBeanDefinitionRegistrar 的步骤如下: 1. 创建一个实现 ImportBeanDefinitionRegistrar 接口的类,该类需要实现 registerBeanDefinitions 方法。 2. 在该类中通过编程的方式注册需要的 Bean,可以使用 BeanDefinitionRegistry 的相关方法来进行注册,例如 registerBeanDefinition。 3. 在@Configuration 类中使用 @Import 注解导入该实现类。 示例代码如下: ```java // 自定义 ImportBeanDefinitionRegistrar 实现类 public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 编程方式注册 Bean BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); registry.registerBeanDefinition("myBean", beanDefinition); } } // 配置类 @Configuration @Import(MyBeanDefinitionRegistrar.class) public class AppConfig { // ... } // 测试类 public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyBean myBean = context.getBean(MyBean.class); // 使用 myBean } } ``` 在上述示例中,MyBeanDefinitionRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,并在 registerBeanDefinitions 方法中编程方式注册了一个名为 "myBean" 的 Bean。然后在 AppConfig 配置类上使用 @Import 注解导入了 MyBeanDefinitionRegistrar,从而实现了动态注册 Bean 的功能。最后,在 Main 测试类中通过 ApplicationContext 获取到了注册的 myBean,并进行使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信仰_273993243

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值