MapperScannerConfigurer类主要成员变量如下:
private String basePackage;
private boolean addToConfig = true;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
private Class extends Annotation> annotationClass;
private Class> markerInterface;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
spring加载配置文件,成员变量spring为其装配,该类实现BeanDefinitionRegistryPostProcessor(extends BeanFactoryPostProcessor) 接口,postProcessBeanDefinitionRegistry会被Spring先于postProcessBeanFactory这个方法执行,
具体执行方法如下:
@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);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
processPropertyPlaceHolders默认不设置为false,由ClassPathMapperScanner进行扫描,调用其父类ClassPathBeanDefinitionScanner的scan方法,
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if(this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
this.doScan调用的是ClassPathMapperScanner的重写方法:
/**
* Calls the parent search that will search and register all the candidates.
* Then the registered objects are post processed to set them as
* MapperFactoryBeans
*/
@Override
public Set doScan(String... basePackages) {
Set 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;
}
方法内调用父类ClassPathBeanDefinitionScanner的doScan方法解析目标包下的所有bean并注册到容器中:
protected Set doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length;
for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
Set candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator();
while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if(candidate instanceof AbstractBeanDefinition) {
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
}
if(candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
if(this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
最后ClassPathMapperScanner调用processBeanDefinitions方法对注册到容器中的beanDefinitions进行二次封装(beanClass=mapperFactoryBean和sqlSessionFactory等配置)
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
这里的getSqlSession().getMapper(this.mapperInterface);方法默认是DefaultSqlSession调用
@Override
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
configuration下再调用MapperRegistry的getMapper方法:
@SuppressWarnings("unchecked")
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
knowMappers中封装的bean实际是sqlSessionFactory在spring进行装配的时候预先加载好的,
classpath*:/config/mappers/system/*.xml
classpath*:/config/mappers/*.xml
到此,确定mapperProxyFactory.newInstance(sqlSession) mybatis动态代理的实现路径
动态代理实现如下:
/**
* @author Lasse Voss
*/
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) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
MapperMethod是整个代理机制的核心类,对Sqlsession当中的操作进行了封装,类里面有俩个成员:SqlCommand类和MethodSignature类。从名字上我们大概的能想到一个可能跟SQL语句有关系,一个可能跟要执行的方法有关系。事实也是如此。
上面代码使用一个内部类SqlCommand来封装底层的增删改查操作,确切来讲这一部分的内容跟XxxMapper的XML配置文件里面的select节点、delete节点等有关。我们都会知道节点上有id属性值。那么MyBatis框架会把每一个节点(如:select节点、delete节点)生成一个MappedStatement类。要找到MappedStatement类就必须通过id来获得。有一个细节要注意:代码用到的id = 当前接口类 + XML文件的节点的ID属性。其源码如下:
//一个内部类 封装了具体执行的动作
public static class SqlCommand {
//xml标签的id
private final String name;
//insert update delete select的具体类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class> mapperInterface, Method method) throws BindingException {
//拿到全名 比如 org.mybatis.example.BlogMapper.selectBlog
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
//获取MappedStatement对象 这个对象封装了XML当中一个标签的所有信息 比如下面
//
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // 这里是一个BUG
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
//为空抛出异常
if (ms == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
name = ms.getId();
type = ms.getSqlCommandType();
//判断SQL标签类型 未知就抛异常
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
}
//内部类 封装了接口当中方法的 参数类型 返回值类型 等信息
public static class MethodSignature {
//是否返回多调结果
private final boolean returnsMany;
//返回值是否是MAP
private final boolean returnsMap;
//返回值是否是VOID
private final boolean returnsVoid;
//返回值类型
private final Class> returnType;
//mapKey
private final String mapKey;
//resultHandler类型参数的位置
private final Integer resultHandlerIndex;
//rowBound类型参数的位置
private final Integer rowBoundsIndex;
//用来存放参数信息
private final SortedMap params;
//是否存在命名参数
private final boolean hasNamedParameters;
//在这里对上面的属性进行初始化 就不一一详细说明了 具体实现细节可以看下面的代码。
public MethodSignature(Configuration configuration, Method method) throws BindingException {
this.returnType = method.getReturnType();
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.hasNamedParameters = hasNamedParams(method);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
...
有什么不对的地方,欢迎指教,谢谢