1.@MapperScanアノテーション分析
@MapperScan(basePackages = "com.ziroom.springboot.springbootsourcetest.mapper")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
MapperScanアノテーションには、MapperScannerRegistrarオブジェクトをロードするために使用される@Importアノテーションが含まれています
@Importはspringbootで非常に重要です。主な機能は、指定されたクラスをSpringコンテナにロードすることです。
MapperScannerRegistrar、javaは、ImportBeanDefinitionRegistrarインターフェースも実装します。このインターフェースの実装メソッドは、BeanDefinitionsを登録するために使用されます。@ Importアノテーションを使用すると、ImportBeanDefinitionRegistrarインターフェースのメソッドregisterBeanDefinitionsが呼び出されます。
2.MapperScannerRegistrarの実装
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
この部分は主にアノテーション**@MapperScan**を取得するためのものです
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
... //省略部分代码
// 2、设置属性
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
//3、设置扫描的包
List<String> basePackages = new ArrayList<>();
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
//4、当前注解所在的包路径
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
// 5、将当前封装的BeanDefinition对象注册
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
BeanDefinitionBuilder
このオブジェクトは、春のオブジェクトの説明であり、オブジェクトのすべての情報が含まれています
1. MapperScannerConfigurerオブジェクトのBeanDefinitionBuilderオブジェクトを作成します。これには、mybatisと統合スプリングのすべての構成情報が含まれています。
basePackage
sqlSessionFactory
sqlSessionTemplate
…
2. AnnotationMetadata AnnoMetaは、アノテーションMapperScanのカプセル化です。次のコードの多くは、annoMetaのデータをBeanDefinitionBuilderオブジェクトにカプセル化し、MapperScannerConfigurerオブジェクトをインスタンス化するときに属性を割り当てます。
3.マッパーでスキャンしたパッケージを設定します
4.スキャンされたパッケージパスがアノテーション@MapperScanに設定されていない場合、アノテーションが配置されているパッケージのパスがデフォルトで使用されます。
5.現在カプセル化されているBeanDefinitionオブジェクトを登録します
3.MapperScannerConfigurerのBeanDefinitionのロード
このクラスは、BeanDefinitionが登録された後に実行されるインターフェースBeanDefinitionRegistryPostProcessorのpostProcessBeanDefinitionRegistryメソッドを実装します。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
...
// 关键步骤,对mapper进行扫描
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
このメソッドは、主にClassPathMapperScannerオブジェクトを作成し、指定されたマッパーをスキャンするためのものです。ここでの重要なステップは最後のステップです。
4.スキャンマッパー–ClassPathMapperScanner
BeanDefinitionHolder
BeanDefinitionおよびBeanName情報を含むBeanDefinitionのラッパークラス
1.クラスClassPathMapperScannerは、SpringクラスClassPathBeanDefinitionScannerを継承し、 doScanメソッドを書き換えます。
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用的是父类ClassPathBeanDefinitionScanner的方法
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;
}
2.親クラスのdoScanを呼び出すと、すべてのマッパーがスキャンされ、これらのマッパーがBeanDefinitionHolderにアセンブルされます。コレクションを返すときに、BeanDefinitionはプロキシオブジェクトを生成し、コンテナーに登録します(これはインスタンスではありません)。 、またはBeanDefinitionオブジェクト)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
... 省略部分代码
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 代理对象
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册
registerBeanDefinition(definitionHolder, this.registry);
}
return beanDefinitions;
}
最後に、スキャンされたすべてのマッパーのBeanDefinitionHolderコレクションを返します
3.ここで数回のジャンプ呼び出しを行ってプロキシオブジェクトを取得し、AOP関連のプロキシパッケージを入力します
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
} else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition)targetDefinition);
}
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
registry.registerBeanDefinition(targetBeanName, targetDefinition);
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
4.スキャンされたマッパーのBeanDefinitionHolderコレクションを返した後、各BeanDefinitionHolderを挿入して構成します。これは、この前にインターフェース関連の情報のみが記録され、実装クラスといくつかの属性値を挿入する必要があるためです。
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
boolean scopedProxy = false;
String beanClassName = definition.getBeanClassName();
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass); // 设置实现类
definition.setLazyInit(lazyInitialization);
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
}
}
重要なステップは、BeanClassをmapperFactoryBeanClassに設定することです
5、マッパーのインスタンス化
インターフェイスプロキシオブジェクトがインスタンス化されると、実際にはインスタンス化されたMapperFactoryBeanになります。このクラスの最上位の親クラスはSpringのDaoSupportであり、InitializingBeanインターフェイスを実装します。つまり、オブジェクトがインスタンス化されるときにafterPropertiesSetメソッドが実行されます。
public abstract class DaoSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
public DaoSupport() {
}
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;
protected void initDao() throws Exception {
}
}
ここで、checkDaoConfigメソッドはテンプレートモードを介して呼び出されます
checkDaoConfigメソッドはMapperFactoryBeanクラスに実装されています。ここで、スキャンされたマッパーインターフェイスがmyabtisに追加されます。
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 添加mapper
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
this.mapperInterfaceによってスキャンされたクラスオブジェクト
6.まとめ
mybatisとスプリングの統合は、実際にはマッパーをスプリング管理に引き渡します。つまり、マッパーをIOCコンテナーに注入します。
BeanDefinition是对扫描到的bean的描述,每个类都有一个BeanDefinition对象一一对应,spring中所有的Bean实例化都是通过该对象生成的
在整合的过程中会出现多种BeanDefinition的包装类,都是用来生产spring Bean描述对象BeanDefinition的
1. @MapperScanアノテーションが読み込まれると、@Importアノテーションがトリガーされます
2.MapperScannerRegistrarクラスのregisterBeanDefinitionsメソッドをトリガーします
3. MapperScannerConfigurerオブジェクトはregisterBeanDefinitionsメソッドに登録され、登録通知メソッドpostProcessBeanDefinitionRegistryは、MapperScannerConfigurer登録がトリガーされた後にトリガーされます。
4. ClassPathMapperScannerクラスは、Springスキャンクラスから継承するpostProcessBeanDefinitionRegistryメソッドで作成されます。
5. ClassPathMapperScannerのスキャンメソッドは、指定されたパッケージの下でマッパーをスキャンすると同時に、マッパーのプロキシオブジェクトMapperFactoryBeanを挿入します。
6.最後に、マッパーによってアセンブルされたBeanDefinitionオブジェクトを登録して、スプリング管理を完了します。
7.以下の操作は、mybatisのみを使用する場合と同じであり、マッパーはアノテーションを介して取得されます
7.補足
- ここでは注釈の形で説明しますが、実際にはXMLの方法があります
- mybatisで使用するSqlSessionFactoryのようなオブジェクトは、springと統合するときにxmlを介して構成され、springbootプロジェクトで自動的にアセンブルされます。