上期中我们主要学习了Spring的动态bean注册,其中的主要接口是ImportBeanDefinitionRegistrar,在文中我们还主要学习接口的上游做了哪些事情。今天我们主要通过mybatis的mapper管理来学习一下该接口的下游方法调用过程。据此也尝试搞清楚mybatis的mapper的管理过程。
对此我们就从registerBeanDefinitions方法看起。
-
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
-
//拿到具体的注解并转化成成annotationAttributes
-
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
-
//实例化一个自定义的扫描器
-
ClassPathMapperScanner scanner =
new ClassPathMapperScanner(registry);
-
if (
this.resourceLoader !=
null) {
-
scanner.setResourceLoader(
this.resourceLoader);
-
}
-
//获取注解元素annotationClass
-
Class extends Annotation> annotationClass = annoAttrs.getClass(
"annotationClass");
-
if (!Annotation.class.
equals(annotationClass)) {
-
scanner.setAnnotationClass(annotationClass);
-
}
-
//获取要被扫描过程检测的接口,由markerinterface元素指定。
-
Class markerInterface = annoAttrs.getClass("markerInterface");
-
if (!Class.class.
equals(markerInterface)) {
-
scanner.setMarkerInterface(markerInterface);
-
}
-
//设置类名生成器
-
Class extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
-
if (!BeanNameGenerator.class.
equals(generatorClass)) {
-
scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
-
}
-
//设置mapper管理器,会将扫描的mapper类放到核心配置类configuration中
-
Class extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
-
if (!MapperFactoryBean.class.
equals(mapperFactoryBeanClass)) {
-
scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
-
}
-
//设置sqlsessionTemplate的名称
-
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
-
//设置sqlsessionfactory的名称
-
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
-
List basePackages =
new ArrayList();
-
//获取要扫描的包名称
-
String[] var10 = annoAttrs.getStringArray(
"value");
-
int var11 = var10.length;
-
-
-
int var12;
-
String pkg;
-
for(var12 =
0; var12 < var11; ++var12) {
-
pkg = var10[var12];
-
if (StringUtils.hasText(pkg)) {
-
basePackages.
add(pkg);
-
}
-
}
-
//从beasePackages中获取要扫描的包名
-
var10 = annoAttrs.getStringArray(
"basePackages");
-
var11 = var10.length;
-
-
-
for(var12 =
0; var12 < var11; ++var12) {
-
pkg = var10[var12];
-
if (StringUtils.hasText(pkg)) {
-
basePackages.
add(pkg);
-
}
-
}
-
//从basePackageClasses中获取要扫描的类的全路径,然后获取要扫描的包名
-
Class[] var15 = annoAttrs.getClassArray(
"basePackageClasses");
-
var11 = var15.length;
-
-
-
for(var12 =
0; var12 < var11; ++var12) {
-
Class clazz = var15[var12];
-
basePackages.
add(ClassUtils.getPackageName(clazz));
-
}
-
String mapperHelperRef = annoAttrs.getString(
"mapperHelperRef");
-
String[] properties = annoAttrs.getStringArray(
"properties");
-
if (StringUtils.hasText(mapperHelperRef)) {
-
scanner.setMapperHelperBeanName(mapperHelperRef);
-
}
else
if (properties !=
null && properties.length >
0) {
-
scanner.setMapperProperties(properties);
-
}
else {
-
try {
-
scanner.setMapperProperties(
this.environment);
-
}
catch (Exception var14) {
-
LOGGER.warn(
"只有 Spring Boot 环境中可以通过 Environment(配置文件,环境变量,运行参数等方式) 配置通用 Mapper,其他环境请通过 @MapperScan 注解中的 mapperHelperRef 或 properties 参数进行配置!如果你使用 tk.mybatis.mapper.session.Configuration 配置的通用 Mapper,你可以忽略该错误!", var14);
-
}
-
}
-
//设置可以被扫描到的标志
-
scanner.registerFilters();
-
//进行for循环遍历
-
scanner.doScan(StringUtils.toStringArray(basePackages));
-
}
-
-
在registerfilters方法中,就是通过设置需要被扫描的类的一些标记信息。要么是注解,要么是接口。
-
public void registerFilters() {
-
boolean acceptAllInterfaces =
true;
-
if (
this.annotationClass !=
null) {
-
//设置需要被扫描的注解,通过上面的注解上的元素设定
-
this.addIncludeFilter(
new AnnotationTypeFilter(
this.annotationClass));
-
acceptAllInterfaces =
false;
-
}
-
//设置需要被拦截的接口
-
if (
this.markerInterface !=
null) {
-
this.addIncludeFilter(
new AssignableTypeFilter(
this.markerInterface) {
-
protected boolean matchClassName(String className) {
-
return
false;
-
}
-
});
-
acceptAllInterfaces =
false;
-
}
-
//是否拦截所有的接口
-
if (acceptAllInterfaces) {
-
this.addIncludeFilter(
new TypeFilter() {
-
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
-
return
true;
-
}
-
});
-
}
-
//不需要被拦截的类型
-
this.addExcludeFilter(
new TypeFilter() {
-
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
-
String className = metadataReader.getClassMetadata().getClassName();
-
return className.endsWith(
"package-info") ?
true : metadataReader.getAnnotationMetadata().hasAnnotation(
"tk.mybatis.mapper.annotation.RegisterMapper");
-
}
-
});
-
}
通过上述两个步骤,我们知道mybatis先是通过@mapperscan注解设置一些值,然后通过设置需要被拦截的类的一些基本信息。然后就要开始for循环逐个扫描包路径了。
-
public SetdoScan(String... basePackages) {
-
//直接进行扫描,将符合条件的类beandefinition信息进行返回
-
Set beanDefinitions =
super.doScan(basePackages);
-
if (beanDefinitions.isEmpty()) {
-
this.logger.warn(
"No MyBatis mapper was found in '" + Arrays.toString(basePackages) +
"' package. Please check your configuration.");
-
}
else {
-
//对符合条件的bean进行一些填充
-
this.processBeanDefinitions(beanDefinitions);
-
}
-
return beanDefinitions;
-
}
寻找符合条件的bean。
-
public SetfindCandidateComponents(String basePackage) {
-
if (
this.componentsIndex !=
null && indexSupportsIncludeFilters()) {
-
return addCandidateComponentsFromIndex(
this.componentsIndex, basePackage);
-
}
-
else {
-
return scanCandidateComponents(basePackage);
-
}
-
}
-
//根据设置的拦截信息进行判断
-
private boolean indexSupportsIncludeFilters() {
-
for (TypeFilter includeFilter :
this.includeFilters) {
-
if (!indexSupportsIncludeFilter(includeFilter)) {
-
return
false;
-
}
-
}
-
return
true;
-
}
-
-
-
private boolean indexSupportsIncludeFilter(TypeFilter filter) {
-
if (filter instanceof AnnotationTypeFilter) {
-
Class
annotation = ((AnnotationTypeFilter) filter).getAnnotationType();
-
return (AnnotationUtils.isAnnotationDeclaredLocally(Indexed.
class,
annotation) ||
-
annotation.getName().startsWith(
"javax."));
-
}
-
if (filter instanceof AssignableTypeFilter) {
-
Class target = ((AssignableTypeFilter) filter).getTargetType();
-
return AnnotationUtils.isAnnotationDeclaredLocally(Indexed.
class, target);
-
}
-
return
false;
-
}
-
-
-
//这块判断是接口哦,他可以扫描接口
-
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
-
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
-
}
通过上述对bean是否符合条件进行判断之后,就开始对扫描到的bean,也就是我们的mapper进行处理了。如下所示:
-
private void processBeanDefinitions(SetbeanDefinitions) {
-
Iterator var3 = beanDefinitions.iterator();
-
-
-
while(var3.hasNext()) {
-
//拿到具体的mapper
-
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
-
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
-
if (
this.logger.isDebugEnabled()) {
-
this.logger.debug(
"Creating MapperFactoryBean with name '" + holder.getBeanName() +
"' and '" + definition.getBeanClassName() +
"' mapperInterface");
-
}
-
//设置构造函数
-
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
-
//设置该bean的name,mapperfactoryBean。这里设置主要是为了注入到mapperfacatorybean中
-
definition.setBeanClass(
this.mapperFactoryBean.getClass());
-
if (StringUtils.hasText(
this.mapperHelperBeanName)) {
-
definition.getPropertyValues().add(
"mapperHelper", new RuntimeBeanReference(
this.mapperHelperBeanName));
-
}
else {
-
if (
this.mapperHelper ==
null) {
-
this.mapperHelper = new MapperHelper();
-
}
-
-
-
definition.getPropertyValues().add(
"mapperHelper",
this.mapperHelper);
-
}
-
//设置addToConfig属性
-
definition.getPropertyValues().add(
"addToConfig",
this.addToConfig);
-
boolean explicitFactoryUsed =
false;
-
if (StringUtils.hasText(
this.sqlSessionFactoryBeanName)) {
-
//sqlSessionFactory,设置sqlSessionFactory类
-
definition.getPropertyValues().add(
"sqlSessionFactory", new RuntimeBeanReference(
this.sqlSessionFactoryBeanName));
-
explicitFactoryUsed =
true;
-
}
else
if (
this.sqlSessionFactory !=
null) {
-
definition.getPropertyValues().add(
"sqlSessionFactory",
this.sqlSessionFactory);
-
explicitFactoryUsed =
true;
-
}
-
//设置sqlSessionTemplate类名称
-
if (StringUtils.hasText(
this.sqlSessionTemplateBeanName)) {
-
if (explicitFactoryUsed) {
-
this.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) {
-
this.logger.warn(
"Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
-
}
-
-
-
definition.getPropertyValues().add(
"sqlSessionTemplate",
this.sqlSessionTemplate);
-
explicitFactoryUsed =
true;
-
}
-
-
-
if (!explicitFactoryUsed) {
-
if (
this.logger.isDebugEnabled()) {
-
this.logger.debug(
"Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() +
"'.");
-
}
-
-
-
definition.setAutowireMode(
2);
-
}
-
}
我们通过查看MapperFactoryBean的构造函数发现如下:
通过上述操作,我们知道我们的的mapper已经进入beandefinition中了,也就是mybatis需要的哪些接口。通过上述的一些分析。接口扫描和注册基本已经完成。现在就成了spring的整合问题。为此有了mybatis-spring的jar包用来整合。
sqlSessionFactoryBean类实现了接口initalizingbean。我们先看看这个接口做了哪些事情。
public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener;
我们看一下具体的实现
-
@Override
-
public void afterPropertiesSet() throws Exception {
-
notNull(dataSource,
"Property 'dataSource' is required");
-
notNull(sqlSessionFactoryBuilder,
"Property 'sqlSessionFactoryBuilder' is required");
-
state((configuration ==
null && configLocation ==
null) || !(configuration !=
null && configLocation !=
null),
-
"Property 'configuration' and 'configLocation' can not specified with together");
-
//创建sqlSessionFactory
-
this.sqlSessionFactory = buildSqlSessionFactory();
-
}
-
-
-
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
-
-
-
final Configuration targetConfiguration;
-
-
-
XMLConfigBuilder xmlConfigBuilder =
null;
-
//xml解析
-
if (
this.configuration !=
null) {
-
targetConfiguration =
this.configuration;
-
if (targetConfiguration.getVariables() ==
null) {
-
targetConfiguration.setVariables(
this.configurationProperties);
-
}
else
if (
this.configurationProperties !=
null) {
-
//设置xml路径等信息
-
targetConfiguration.getVariables().putAll(
this.configurationProperties);
-
}
-
}
else
if (
this.configLocation !=
null) {
-
//建一个解析器
-
xmlConfigBuilder = new XMLConfigBuilder(
this.configLocation.getInputStream(),
null,
this.configurationProperties);
-
targetConfiguration = xmlConfigBuilder.getConfiguration();
-
}
else {
-
LOGGER.debug(() ->
"Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
-
targetConfiguration = new Configuration();
-
Optional.ofNullable(
this.configurationProperties).ifPresent(targetConfiguration::setVariables);
-
}
-
-
-
Optional.ofNullable(
this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
-
Optional.ofNullable(
this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
-
Optional.ofNullable(
this.vfs).ifPresent(targetConfiguration::setVfsImpl);
-
-
-
if (hasLength(
this.typeAliasesPackage)) {
-
scanClasses(
this.typeAliasesPackage,
this.typeAliasesSuperType)
-
.forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
-
}
-
-
-
if (!isEmpty(
this.typeAliases)) {
-
Stream.of(
this.typeAliases).forEach(typeAlias -> {
-
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
-
LOGGER.debug(() ->
"Registered type alias: '" + typeAlias +
"'");
-
});
-
}
-
-
-
if (!isEmpty(
this.plugins)) {
-
Stream.of(
this.plugins).forEach(plugin -> {
-
targetConfiguration.addInterceptor(plugin);
-
LOGGER.debug(() ->
"Registered plugin: '" + plugin +
"'");
-
});
-
}
-
-
-
if (hasLength(
this.typeHandlersPackage)) {
-
scanClasses(
this.typeHandlersPackage, TypeHandler.
class).stream()
-
.filter(clazz -> !clazz.isInterface())
-
.filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
-
.filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) !=
null)
-
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
-
}
-
-
-
if (!isEmpty(this.typeHandlers)) {
-
Stream.of(this.typeHandlers).forEach(typeHandler -> {
-
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
-
LOGGER.debug(() ->
"Registered type handler: '" + typeHandler +
"'");
-
});
-
}
-
-
-
if (
this.databaseIdProvider !=
null) {
//fix #64 set databaseId before parse mapper xmls
-
try {
-
targetConfiguration.setDatabaseId(
this.databaseIdProvider.getDatabaseId(
this.dataSource));
-
}
catch (SQLException e) {
-
throw new NestedIOException(
"Failed getting a databaseId", e);
-
}
-
}
-
-
-
Optional.ofNullable(
this.cache).ifPresent(targetConfiguration::addCache);
-
-
-
if (xmlConfigBuilder !=
null) {
-
try {
-
xmlConfigBuilder.parse();
-
LOGGER.debug(() ->
"Parsed configuration file: '" +
this.configLocation +
"'");
-
}
catch (Exception ex) {
-
throw new NestedIOException(
"Failed to parse config resource: " +
this.configLocation, ex);
-
}
finally {
-
ErrorContext.instance().reset();
-
}
-
}
-
-
-
targetConfiguration.setEnvironment(new Environment(
this.environment,
-
this.transactionFactory ==
null ? new SpringManagedTransactionFactory() :
this.transactionFactory,
-
this.dataSource));
-
-
-
if (
this.mapperLocations !=
null) {
-
if (
this.mapperLocations.length ==
0) {
-
LOGGER.warn(() ->
"Property 'mapperLocations' was specified but matching resources are not found.");
-
}
else {
-
for (Resource mapperLocation :
this.mapperLocations) {
-
if (mapperLocation ==
null) {
-
continue;
-
}
-
try {
-
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
-
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
-
//开始解析了
-
xmlMapperBuilder.parse();
-
}
catch (Exception e) {
-
throw new NestedIOException(
"Failed to parse mapping resource: '" + mapperLocation +
"'", e);
-
}
finally {
-
ErrorContext.instance().reset();
-
}
-
LOGGER.debug(() ->
"Parsed mapper file: '" + mapperLocation +
"'");
-
}
-
}
-
}
else {
-
LOGGER.debug(() ->
"Property 'mapperLocations' was not specified.");
-
}
-
-
-
return
this.sqlSessionFactoryBuilder.build(targetConfiguration);
-
}
-
-
-
//进行xml转类
-
public void parse() {
-
if (!configuration.isResourceLoaded(resource)) {
-
-
-
configurationElement(parser.evalNode(
"/mapper"));
-
configuration.addLoadedResource(resource);
-
//将转出来的类转变为实体
-
bindMapperForNamespace();
-
}
-
-
-
parsePendingResultMaps();
-
parsePendingCacheRefs();
-
parsePendingStatements();
-
}
解析xml的
-
private
void bindMapperForNamespace() {
-
String
namespace = builderAssistant.getCurrentNamespace();
-
if (
namespace !=
null) {
-
Class boundType =
null;
-
try {
-
boundType = Resources.classForName(
namespace);
-
}
catch (ClassNotFoundException e) {
-
//ignore, bound type is not required
-
}
-
if (boundType !=
null) {
-
if (!configuration.hasMapper(boundType)) {
-
// Spring may not know the real resource name so we set a flag
-
// to prevent loading again this resource from the mapper interface
-
// look at MapperAnnotationBuilder#loadXmlResource
-
configuration.addLoadedResource(
"namespace:" +
namespace);
-
//在config中添加到configuration
-
configuration.addMapper(boundType);
-
}
-
}
-
}
-
-
在addMapper方法中
-
public
void addMapper(Classtype) {
-
if (
type.isInterface()) {
-
if (hasMapper(
type)) {
-
throw
new BindingException(
"Type " +
type +
" is already known to the MapperRegistry.");
-
}
-
boolean loadCompleted =
false;
-
try {
-
//创建一个代理类
-
knownMappers.put(
type,
new MapperProxyFactory<>(
type));
-
MapperAnnotationBuilder parser =
new MapperAnnotationBuilder(config,
type);
-
//缓存起来
-
parser.parse();
-
loadCompleted =
true;
-
}
finally {
-
if (!loadCompleted) {
-
knownMappers.remove(
type);
-
}
-
}
-
}
-
}
这里的parse方法执行结束之后,我们的xml就进入knownMappers 保存。
Map, MapperProxyFactory> knownMappers = new HashMap<>();
但是这个addmapper又是在哪里调用的?
我们发现在MapperFactoryBean中有相关的操作。但是没有发现是谁调用了checkDaoConfig接口,所有去父类看一下。
最后在父类中找到了initalizingBean的接口。于是有和spring有了关系。
-
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 {
-
}
-
}
而这里的mapperInterface则是我们spring接口扫描的结果。我们还看到在addmapper方法的时候有判断这个接口对应的xml是否已经创建成功了。
同时,我们也发现MapperFactoryBean类也实现了FactoryBean,也就是这里的getObject方法。我们以前学习FactoryBean的时候说如果spring的getBean在内部找不到具体的类的话,就会从其他实现了FactoryBean的接口中获取。那么就这也就是我们开发中使用注解@Autowired注入了一个接口的原因所在。
通过对MapperFactoryBean的学习,我们发现这才是最后的大boss,除此那么既然发生了聚合,那么肯定需要一些基础的东西。于是我们发现
也就是说MapperFactoryBean的时候需要SqlSessionFactory,然后通过sqlsessionFactory创建SqlSessionTemplate。最终调用的还是sqlSessionfactionbean中的configuration,configuration又调用了mapperRegistry。而最终也是通过JDK代理实现的。
-
@SuppressWarnings("unchecked")
-
protected T newInstance(MapperProxymapperProxy) {
-
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
-
}
-
-
-
public T newInstance(SqlSession sqlSession) {
-
final MapperProxymapperProxy =
new MapperProxy<>(sqlSession, mapperInterface, methodCache);
-
return newInstance(mapperProxy);
-
}
通过上述的分析,我们大概得可以知道mybatis的过程。首先通过spring接口扫描,然后再将扫描到的接口注入到mapperfactorybean中。而sqlSessionFactoryBean也是和spring相关系的,其通过配置文件找到需要解析的xml文件,然后实例化成代理类,并交由knownMappers进行管理。当mapperfactorybean初始化的时候,将扫描到的接口和xml解析的具体实体进行一一对应起来并放入到mapperRegistry中,等待spring注入的时候通过getBean方法进行实例化。能够注入的原因是mapperfactorybean实现了factoryBean接口。
最后我们大概得说一下,这些流程中的关键类,mapperfactorybean是主要的类。其中聚合sqlsessiontemplate,sqlsessiontemplate是从sqlsessionfactory创建而来。sqlsessionfactory又是从sqlsessionfactorybean中而来,sqlsessionfactorybean主要是解析xml文件并将其保存到knownMappers中的。
至此mybatis的主体逻辑大概想清楚了,那么sql的执行过程又是怎么样的?我们下期再看这个问题吧!
参考文献:
https://www.cnblogs.com/hei12138/p/mybatis-spring.html
转载:https://blog.csdn.net/tianjingle_blog/article/details/111503564