-
背景
-
BeanFactoryPostProcessor
-
BeanDefinitionRegistryPostProcessor
-
ImportBeanDefinitionRegistrar
-
总结
-
参考
背景
假设我们有这种需求,像Mybaits需要将这些接口注入到Spring容器中
public interface OneTestDao {
@Select("SELECT name FROM user WHERE id = 1")
String query();
}
public interface TwoTestDao {
@Select("SELECT name FROM user WHERE id = 2")
String query();
}
首先我们想将我们的自己的Bean(比如代理对象)注入到Spring容器中,有什么方式呢?
一般都是通过Spring扫描Resouce资源然后解析为BeanDefinition,才能从getBean时解析BeanDefinition实例化对象放入此单例缓存中.但是我们这里的是接口,没法直接注入到Spring容器中。
不过Spring提供了一些扩展接口来供我们在Bean加载、初始化、加载完提供了一些接口,供我们扩展。
比如
BeanFactoryPostProcessor
来看看源码
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
可以看到这是一个函数式接口,实现这个接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,然后作一些修改操作,例如我可以将bean的scope从singleton改为prototype,也可以添加一些bean,具体能操作bean的方法全部在beanFactory中。我们可以看看beanFactory有哪些方法

其中提供的registerSingleton方法就可以让我注册bean
,使用方式也很简单
@Component
public class TestPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerSingleton("test", new String("test"));
}
}
这样我们就像spring容器中注册了一个String类型的bean,beanName为 test
这里在这个接口执行前其实spring BeanDefinition已经扫描完成了,我们使用这种方式还不不太好,相当于强制暴力的去修改BeanDefinition属性,可能修改不当会有一些bug。有没有什么方法在扫描的时候添加BeanDefinition呢?也是有的
主要提供了这两个接口:
BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
可以看到BeanDefinitionRegistryPostProcessor
也是继承了BeanFactoryPostProcessor
,但是我们在使用BeanDefinitionRegistryPostProcessor
只需要实现他的postProcessBeanDefinitionRegistry
方法即可, postProcessBeanFactory
方法放一个空实现即可,这样让一个类只做一件事,满足单一原则
使用方式也很简单
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition testBean = new RootBeanDefinition(Test.class);
//新增Bean定义
registry.registerBeanDefinition("test", testBean);
}
}
这样就完成了test
Bean的注册
ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
ImportBeanDefinitionRegistrar
可以看到除了像BeanDefinitionRegistryPostProcessor
提供了BeanDefinitionRegistry
参数,还提供了一个AnnotationMetadata
参数,该参数可以获取指定注解的属性。一般来说BeanDefinitionRegistryPostProcessor
用来解析外部资源配置,ImportBeanDefinitionRegistrar
解析注解配置
如果对mybatis源码了解多的就可以发现mybatis整合spring其中的两个配置类
MapperScannerConfigurer
就是实现了BeanDefinitionRegistryPostProcessor
,
MapperScannerRegistrar
就是实现了ImportBeanDefinitionRegistrar
说了这么多,我们还没有聊今天的主角FactoryBean
来看看源码
public interface FactoryBean<T> {
//获取bean
@Nullable
T getObject() throws Exception;
@Nullable
//获取classType
Class<?> getObjectType();
//是否单例
default boolean isSingleton() {
return true;
}
}
三个方法也是比较简单,实现FactoryBean
的Bean本事 就是一个Bean
, 不同的是 Spring 对其做了特殊处理,在试图获取时FactoryBean
时,获取的是getObject()
返回的对象。
既然获取FactoryBean
对象的时候,获取的是getObject()
方法的bean对象,那么我们是不是可以基于FactoryBean
设置出一个获取所有mapper代理对象的bean呢?
public class MyFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MyFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, new MyFactoryBeanHandler());
}
@Override
public Class<?> getObjectType() {
return clazz;
}
}
-
MyFactoryBeanHandler
public class MyFactoryBeanHandler implements InvocationHandler
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 仅仅打印注解上的sql 假装执行代理对象执行sql
Select annotation = method.getAnnotation(Select.class);
if (annotation == null) return null;
Class<?> returnType = method.getReturnType();
System.out.println("-------> " + Arrays.toString(annotation.value()));
return null;
}
}
这样我们在getObject()
的时候就可以返回我们的代理对象了,但是有个问题我们的class类型是通过构造方法传入的。如果我们使用
beanDefinition.setBeanClass(MyFactoryBean.class);
注入对象MyFactoryBean
我们能使用getObject()
获取到代理对象吗?明显是不能的,因为我们没有传入clazz属性
那我们的class属性哪里来呢?从BeanDefinition
中获取
beanDefinition.getBeanClassName()
这样就可以获取到我之前的接口OneTestDao
类名
然后通过设置构造方法的参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue()
最后代码就变成了如下
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
到这里就能实例化我们自己的FactoryBean
,从而在获取我们的Dao接口时获取的都是代理对象,这就是Spring整合Mybatis的核心实现。也是FactoryBean
的一个应用场景,如果你知道了这些,再去看spring整合Mybatis源码就会发现整个过程特别清晰,也就理解了FactoryBean
的作用
总结
所以说FactoryBean
本质就是用来给我们实例化、或者动态的注入一些比较复杂的Bean,比如像一些接口的代理对象。这里简单说一下和BeanFactory
的区别,最主要的一个区别是FactoryBean
是动态注入Bean和BeanFactory
主要是负责Bean
实例化、定位、配置应用程序中的对象及建立这些对象间的依赖.三言两语无法说清,后续有机会重点分析BeanFactory
再来细说吧
参考
博客
原文始发于微信公众号(小奏技术):Spring FactoryBean作用及应用场景源码分析(Spring整合Mybatis的核心)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30427.html