1 -》MapperScannerRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar)给容器注入
2 -》MapperScannerConfigurer 并设置 basePackage, mapperFactoryBeanClass (默认org.mybatis.spring.mapper.MapperFactoryBean)等属性。MapperFactoryBean implements BeanFactoryPostProcessor(间接继承)、 InitializingBean等接口,创建完成对象后,spring会自动执行对用的后置处理器方法和初始化方法
3 -》postProcessBeanDefinitionRegistry 创建 ClassPathMapperScanner 对象,设置 scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);等属性,最后调用 scanner.scan()
4 -》先调用父类的doScan,在调用 私有方法 processBeanDefinitions(beanDefinitions);
调用父类的 super.doScan(basePackages) 扫面包下面的 mapper 接口并创建对应的 BeanDefinition,注意此时的 BeanDefinition 的 beanClass 是具体的mapper类
调用 私有方法 processBeanDefinitions(beanDefinitions); 重新设置 每一个beanDefinition 的 beanClass 为 org.mybatis.spring.mapper.MapperFactoryBean
至此每一个Mapper接口的工厂类设置为 org.mybatis.spring.mapper.MapperFactoryBean 并成功注入到spring 容器中
MapperFactoryBean
spring 初始化 InitializingBean 的实现类 后会回调 afterPropertiesSet 方法,进而调用 DaoSupport 的 checkDaoConfig 方法,MapperFactoryBean 重写了 父类的 checkDaoConfig 方法:
Configuration configuration = getSqlSession().getConfiguration(); // 通过SqlSession获取Configuration对象
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface); // 将mapper接口添加到mybatis configuration中,在addMapper方法中,mybatis会通过jdk动态代理产生代理对象
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
FactoryBean 接口实现类,spring在getBean时会,调用对象的 getObject 返回实例。
在使用mapper进行依赖注入时,Spring会调用容器中对应 MapperFactoryBean 的 getObject() 方法,最中从mybatis中取出mapper接口的代理对象
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
由第一步可以看出 org.apache.ibatis.session.Configuration 的 addMapper 方法进行代理对象的创建,先看 SqlSessionFactoryBean 如何创建 Configuration 对象的
1.1、InitializingBean 接口 afterPropertiesSet 方法在 SqlSessionFactoryBean对象创建成功后被调用:
@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");
this.sqlSessionFactory = buildSqlSessionFactory();
}
buildSqlSessionFactory() 方法会通过,已经设置好的属性: dataSource、configLocation(classpath:mybatis-config.xml)、mapperLocations(classpath:com/bdqn/dao/**/*.xml)等创建 org.apache.ibatis.session.Configuration targetConfiguration,this.sqlSessionFactoryBuilder.build(targetConfiguration)构建出 SqlSessionFactory
1.2、FactoryBean 接口实现类,spring在getBean时会,调用对象的 getObject 返回实例,即 SqlSessionFactory
2.1 通过源码可以看出调用了成员变量 org.apache.ibatis.binding.MapperRegistry 的 .addMapper(type); 关联 上面第 7 步 代码如下:
public void addMapper(Class type) {
//判断是否是接口
if (type.isInterface()) {
//判断是否已经注册过
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//将mapper接口注册到map集合中
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 解析接口上的注解或者加载mapper配置文件生成mappedStatement
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2.2 》MapperProxyFactory 分析
public class MapperProxyFactory {
// 被代理的接口
private final Class mapperInterface;
// 方法进行缓存
private final Map methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return mapperInterface;
}
public Map getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
//采用JDK的动态代理Proxy代理模式生成接口代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//MapperProxy 实现 InvocationHandler接口
final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
//生程代理
return newInstance(mapperProxy);
}
}
2.3》MapperProxy 分析:invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// MapperMethod 执行业务操作
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
MapperMethod 对象,处理SqlSession接口调用CRUD操作后产生的结果集。
CURD execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}