本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
原理说明
作用
根据@MapperScan注解配置的包路径,扫描所有mapper接口,创建BeanDefinition对象,修改beanClass属性值为MapperFactoryBean,注册到Spring容器中,为后续Bean初始化做准备。
流程
- @MapperScan注解通过@Import方法导入 MapperScannerRegistrar 类,MapperScannerRegistrar实现了 ImportBeanDefinitionRegistrar 接口,覆写了registerBeanDefinitions方法,作用为手动注册某个Bean的BeanDefinition到容器中【DefaultListableBeanFactory=>beanDefinitionMap】。
- ImportBeanDefinitionRegistrar接口是Spring的扩展点之一,Spring容器启动时会回调所有实现了ImportBeanDefinitionRegistrar接口的实现类中的registerBeanDefinitions方法,完成自定义BeanDefinition注册。
- MapperScannerRegistrar的registerBeanDefinitions方法手动将 MapperScannerConfigurer 类通过这种方式将自己的BeanDefinition实例注册到了容器中,为下一步Bean初始化做准备。
- MapperScannerConfigurer类实现了 BeanDefinitionRegistryPostProcessor 接口,该接口也是Spring的扩展点之一,Spring容器启动时会回调所有实现了BeanDefinitionRegistryPostProcessor接口的实现类中的postProcessBeanDefinitionRegistry方法,进行BeanDefinition注册的后置处理,可以修改BeanDefinition对象。
- 在MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法中创建了ClassPathMapperScanner对象,该对象对@MapperScan注解中配置的包路径进行了扫描,为每个mapper接口创建对应的BeanDefinition实例,并修改所有实例中的 beanClass 属性值为 MapperFactoryBean ,autoWireMode为byType。
- 将所有mapper接口的BeanDefinition实例注册到Spring的容器中,为下一步实例化mapper接口做准备。
- MapperFactoryBean是一个很关键的类,MapperFactryBean集成了 SqlSessionDaoSupport 类,实现了FactoryBean接口,覆写了getObject()方法。
- FactoryBean 类型的Bean,在进行Bean初始化时,会通过调用自己的getObject()方法,获取对象;而MapperFactoryBean覆写后的getObject()方法,实际执行的是getSqlSession().getMapper(this.mapperInterface),通过此方法衔接到Mybaits,以JDK动态代理的方式,创建了一个代理对象, 最后将代理对象注册到了Spring容器中 。
时序图
Spring扩展点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
*/
// 通过@Import的方式注册Bean定义
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
Spring扩展点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
*/
// Bean定义注册完毕之后,进行后置处理
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
注解@MapperScacn定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
}
- 通过@Import注解,导入了MapperScannerRegistrar类
- value、basePackages作用一致,用于声明待扫描的mapper层包路径,支持多组。
- basePackageClasses用于指定扫描某个类所在包下的所有组件。(@SpringBootApplication标识的启动类)
- factoryBean:指定FactoryBean实现类,用于生成接口代理类,默认为MapperFactoryBean.class, 支持自定义。
MapperScannerRegistrar相关
该类实现了ImportBeanDefinitionRegistrar接口,在启动时回调registerBeanDefinitions方法注册 MapperScannerConfigurer.class 的BeanDefinition到容器中。
关于ImportBeanDefinitionRegistrar :Spring扩展点之一,启动时会回调被覆盖的registerBeanDefinitions方法,注册BeanDefinition到容器。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
// 建造者模式创建MapperScannerConfigurer Bean定义对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
MapperScannerConfigurer相关
该类实现了 BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor也是 Spring的扩展点之一 ,启动时回调被覆盖的 postProcessBeanDefinitionRegistry 方法。在回调方法中创建了ClassPathMapperScanner对象,并调用doScan(basePackages)方法对@MapperScacn中的包路径进行扫描创建、并修改BeanDefinition。
ClassPathMapperScanner相关
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
扫描mapper层所有接口的Bean定义,设置beanClass和autoWireMode。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 1.设置beanClass为MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
// 2.设置autoWireMode=byType
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
-
设置beanClass为MapperFactryBean
-
设置autoWireMode=byType
- No:即不启用自动装配。Autowire默认的值。 2. byName:通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入。 3. byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。 4. constructor:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用set方法注入,而是使用构造子注入。
public static final int AUTOWIRE_NO = 0;
public static final int AUTOWIRE_BY_NAME = 1;
public static final int AUTOWIRE_BY_TYPE = 2;
public static final int AUTOWIRE_CONSTRUCTOR = 3;
MapperFactoryBean相关
- 实现了FactoryBean接口,覆写了getObject( )方法,容器初始化时通过getObject()返回实际对象。
- 继承了SqlSessionDaoSupport类,有成员变量sqlSessionTemplate,通过autoWire=byType属性调用setSqlSessionFactory(sqlSessionFactory)初始化时,给sqlSessionTemplate赋值。
- getObject()方法通过Configuration对象获以JDK动态代理的方式获取代理类的实例。
- Configuration对象的mapperRegistry变量在Mybatis配置解析时赋值,mapperRegistry内部的knownMappers存储所有mapper接口的map信息,key为mapper接口的Class对象,value为持有对应Class对象的MapperProxyFactory实例,
- MapperProxyFactory是代理类工厂,用于生成代理对象MapperProxy的代理类,即为mapper接口的实际代理对象。
// MapperFactoryBean.java
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
// MapperProxyFactory.java
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}