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();
}

相关推荐
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-1 天前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫1 天前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh1 天前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode1 天前
Spring 依赖注入方式全景解析
java·后端·spring