通过MapperScannerConfigurer配置Mybatis的一点研究

来源:互联网 发布:数组发筛选100以内素数 编辑:程序博客网 时间:2024/06/07 17:36

今天通过MapperScannerConfigurer来自动扫描存放Mapper接口的包来自动获得Mapper在Spring中的注册,顺带研究了一下Mybatis是如何实现不在接口上面加注解也能识别的到接口的,同时探究了注册进入Spring的Mapper映射器和***Mapper.xml是如何进行对应的。

首先来一段配置代码,配置好自动扫描路径,sqlSessionFactory和dataSource就可以了。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="com.recom.dao.in" /></bean><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">   <property name="dataSource" ref="dataSource"/>   <property name="mapperLocations" value="classpath:/recom-stastic-conf/ArticleInfoDailyStatMapper.xml"/>   <property name="configLocation" value="classpath:/sqlmap/MapperConfig.xml"/></bean><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"          destroy-method="close">        ****省略配置内容************</bean>

这个适合,Mybatis会扫描com.recom.dao.in 下面的所有接口(interface)类型的接口,由于没有指定要扫描的接口类型或者注解类型,默认的情况下MapperScannerConfigurer类中

protected void registerDefaultFilters() {      boolean acceptAllInterfaces = true;      // if specified, use the given annotation and / or marker interface      if (MapperScannerConfigurer.this.annotationClass != null) {        addIncludeFilter(new AnnotationTypeFilter(MapperScannerConfigurer.this.annotationClass));        acceptAllInterfaces = false;      }      // override AssignableTypeFilter to ignore matches on the actual marker interface      if (MapperScannerConfigurer.this.markerInterface != null) {        addIncludeFilter(new AssignableTypeFilter(MapperScannerConfigurer.this.markerInterface) {          @Override          protected boolean matchClassName(String className) {            return false;          }        });        acceptAllInterfaces = false;      }      if (acceptAllInterfaces) {        // default include filter that accepts all classes        addIncludeFilter(new TypeFilter() {          public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {            return true;          }        });      }    }

当没有指定接口和注解类型的时候,默认把所有接口类型加入扫描的includeFilter里面。
最后在 ClassPathScanningCandidateComponentProvider 的
public Set findCandidateComponents(String basePackage) 方法里面

try {MetadataReader metadataReader = his.metadataReaderFactory.getMetadataReader(resource);                        if (isCandidateComponent(metadataReader)) {                            ScannedGenericBeanDefinition sbd =                             new ScannedGenericBeanDefinition(metadataReader);                            sbd.setResource(resource);                            sbd.setSource(resource);                            if (isCandidateComponent(sbd)) {                                if (debugEnabled) {                    logger.debug("Identified candidate component class: " + resource);                                }                                candidates.add(sbd);                            }

将BeanDefinition 加入候选列表。这样就完成了对Mapper映射器接口的扫描加载。

当调用Mapper接口的方法时,是IOC通过代理反射完成的,
调用如下接口的方法。

public interface ArticleInfoDailyStatDao {    List<ArticleStatsInfo> queryArticleStatsInfo(String after);}

反射会执行invoke方法,实际调用的是final Object result = mapperMethod.execute(args);

public class MapperProxy implements InvocationHandler, Serializable {  private static final long serialVersionUID = -6424540398559729838L;  private SqlSession sqlSession;  private <T> MapperProxy(SqlSession sqlSession) {    this.sqlSession = sqlSession;  }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    if (method.getDeclaringClass() == Object.class) {      return method.invoke(this, args);    }    final Class<?> declaringInterface = findDeclaringInterface(proxy, method);    final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);    final Object result = mapperMethod.execute(args);    if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {      throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");    }    return result;  }

跟进去看一下org.apache.ibatis.binding.MapperMethod
初始化的时候会加载很多参数

public MapperMethod(Class<?> declaringInterface, Method method, SqlSession sqlSession) {    paramNames = new ArrayList<String>();    paramPositions = new ArrayList<Integer>();    this.sqlSession = sqlSession;    this.method = method;    this.config = sqlSession.getConfiguration();    this.hasNamedParameters = false;    this.declaringInterface = declaringInterface;    this.objectFactory = config.getObjectFactory();    setupFields();    setupMethodSignature();    setupCommandType();    validateStatement();  }

其中 setupFields(),解释了Mapper接口的方法被调用之后,执行的指令的内容:
当前接口的完全路径 + 当前执行的方法名。

private void setupFields() {    this.commandName = declaringInterface.getName() + "." + method.getName();  }

这也要求配置的****Mapper.xml 里面的namespace一定要写完整写正确才可以匹配的到

<mapper namespace="com.recom.dao.in.ArticleInfoDailyStatDao">*****省略其他内容 <resultMap id="ArticleInfoDailyResultMap"               type="com.recom.domain.ArticleStatsInfo"> ****************         </resultMap>  ****************<select id="queryArticleStatsInfo" resultMap="ArticleInfoDailyResultMap">
1 0
原创粉丝点击