一、本文内容分类
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();
}