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的能力。

java 复制代码
//接口所有抽象方法,合并看就一个注册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

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

ImportBeanDefinitionRegistrar实现类

java 复制代码
//注意这里不能加注解,要通过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实现类
}
java 复制代码
//测试
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

java 复制代码
public interface UserServiceTestInterface {
	public void list();
}
java 复制代码
//因为是接口,但是spring的容器里面是不允许注入接口的,只能是接口的实现类。如果我们写个去实现接口,那就没有什么意义了,没必要搞得那么复杂,这次我们通过代理类来完成对接口的实现。

public class MyInvocationHandler implements InvocationHandler {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("代理类逻辑代码");
		return null;
	}
}
java 复制代码
//注意这里不能加注解,要通过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 {
}

测试

java 复制代码
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中

java 复制代码
//业务接口
public interface UserServiceTestInterface {
	public void list();
}

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

java 复制代码
//创建代理类,代理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中。

java 复制代码
//注意这里不能加注解,要通过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);
	}
}

测试

java 复制代码
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();
}

相关推荐
LuckyLay13 分钟前
Spring学习笔记_34——@Controller
spring·controller
ApiHug2 小时前
ApiSmart x Qwen2.5-Coder 开源旗舰编程模型媲美 GPT-4o, ApiSmart 实测!
人工智能·spring boot·spring·ai编程·apihug
背水2 小时前
初识Spring
java·后端·spring
闲人一枚(学习中)3 小时前
spring -第十四章 spring事务
java·数据库·spring
wclass-zhengge3 小时前
SpringCloud篇(注册中心 - Eurea)
后端·spring·spring cloud
小蒜学长5 小时前
springboot基于SpringBoot的企业客户管理系统的设计与实现
java·spring boot·后端·spring·小程序·旅游
海无极6 小时前
EDUCODER头哥 SpringBoot 异常处理
java·spring boot·spring
.生产的驴6 小时前
SpringBootCloud 服务注册中心Nacos对服务进行管理
java·spring boot·spring·spring cloud·tomcat·rabbitmq·java-rabbitmq
Wx-bishekaifayuan8 小时前
springboot市社保局社保信息管理与分析系统-计算机设计毕业源码03479
java·css·spring boot·spring·spring cloud·servlet·guava
一叶飘零_sweeeet8 小时前
Eureka、Zookeeper 与 Nacos:服务注册与发现功能大比拼
spring·zookeeper·eureka·nacos