在mybatis中,mapper都是像如下一个个的接口:java
public interface UserMapper { long countByExample(UserDTOExample example); int deleteByExample(UserDTOExample example); int deleteByPrimaryKey(Integer id); int insert(UserDTO record); int insertSelective(UserDTO record); List<UserDTO> selectByExample(UserDTOExample example); UserDTO selectByPrimaryKey(Integer id); int updateByExampleSelective(@Param("record") UserDTO record, @Param("example") UserDTOExample example); int updateByExample(@Param("record") UserDTO record, @Param("example") UserDTOExample example); int updateByPrimaryKeySelective(UserDTO record); int updateByPrimaryKey(UserDTO record); }
public interface UserMapperExt extends UserMapper { List<UserDTO> findUserListByName(String username); }
可是在使用的时候,都是经过spring bean注入的方式使用的,以下:spring
@RestController @RequestMapping("user") public class UserController { @Autowired private UserMapperExt userMapperExt; @GetMapping("get-userInfo") public String getUserInfo() { List<UserDTO> userList = userMapperExt.findUserListByName("ZHANGSAN"); return "SUCCESS"; } }
那么,mybatis的mapper接口(例如:接口UserMapperExt)是怎么样被实例化为spring bean的呢?sql
mybatis mapper接口被初始化为spring bean大致分三步:mybatis
加载mybatis mapper bean的注册器MapperScannerRegistrar---》MapperScannerRegistrar加载@MapperScan指定包路径下面的接口为bean并注册到容器中---》将mybatis mapper bean与动态代理实现MapperProxy关联起来。app
流程图以下:ide
启动类代码以下:this
package com.iwill.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @ComponentScan("com.iwill.mybatis") @MapperScan(value = {"com.iwill.mybatis.dao.mapper.ext", "com.iwill.mybatis.dao.mapper.gen"}) @EnableTransactionManagement(proxyTargetClass = true) public class MyBatisApplication { public static void main(String[] args) { SpringApplication.run(MyBatisApplication.class, args); } }
在加载MyBatisApplication的配置注解时,MyBatisApplication上的注解会被解析出来,以下:spa
它会解析注解上面的@Import,并把对应的value属性值放到imports中,imports会被返回。.net
其中,MapperScan接口的定义以下:3d
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class{ ...... }
能够看出,@Import的值为MapperScannerRegistrar.class,能够得知,注解@MapperScan使用MapperScannerRegistrar去加载mapper bean。
getImports源代码以下:
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; }
再返回到上一步,getImports会被processImports方法调用:
在processImports中,MapperScannerRegistrar会被放到importBeanDefinitionRegistrars列表中:
在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法中,会从bean注册器中加载bean:
这里就包括了上一步被加载进来的MapperScannerRegistrar。loadBeanDefinitionsFromRegistrars的实现以下:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); }
这里会去调用实现类的registerBeanDefinitions方法去加载bean。前面说过,MapperScannerRegistrar实现了接口ImportBeanDefinitionRegistrar,具备方法:registerBeanDefinitions。所以,加载mapper bean就正式开始了。
MapperScannerRegistrar的registerBeanDefinitions方法实现以下:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(mapperScanAttrs, registry); } }
上述代码会去读取注解@MapperScan的值,虽然@MapperScan有不少属性能够配置,这里,咱们只配置了value属性,其值为:
@MapperScan(value = {"com.iwill.mybatis.dao.mapper.ext", "com.iwill.mybatis.dao.mapper.gen"})
方法registerBeanDefinitions里面会设置扫描器(扫描器为ClassPathMapperScanner)以及配置读取类的包路径和注册扫描规则:
basePackages.addAll( Arrays.stream(annoAttrs.getStringArray("value")) .filter(StringUtils::hasText) .collect(Collectors.toList())); basePackages.addAll( Arrays.stream(annoAttrs.getStringArray("basePackages")) .filter(StringUtils::hasText) .collect(Collectors.toList())); basePackages.addAll( Arrays.stream(annoAttrs.getClassArray("basePackageClasses")) .map(ClassUtils::getPackageName) .collect(Collectors.toList())); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages));
也便是说:注解@MapperScan的value、basePackages、basePackageClasses等属性配置的包路径都会被扫描。
配置的扫描过滤器(扫描规则scanner.registerFilters())以下:
咱们没有在@MapperScan里面指定须要扫描的标记指定注解的类(annotationClass属性来指定),因此acceptAllInterfaces会为true,即会扫描包路径下的全部接口,可是package-info结尾的去掉。
doScan方法会扫描出符合指定规则的类,而且设置bean的一些属性:
findCandidateComponents方法就是找出备选的bean,其内部调用scanCandidateComponents,scanCandidateComponents实现以下:
其工做过程:扫描指定包路径下面的全部.class文件,而后循环判断是否符合过滤规则。isCandidateComponent的实现以下:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
若是这一步判断经过了,那么就会进行第二步判断:
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { AnnotationMetadata metadata = beanDefinition.getMetadata(); return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))); }
这一步会过滤掉不是接口的类。找到符合条件的接口,就会new一个ScannedGenericBeanDefinition,放入到Set<BeanDefinition>进行返回。返回到doScan方法,里面会对bean进行一些属性设置:beanName、scope(默认是singleton)等。
而后会注册到DefaultListableBeanFactory(就是将beanName与bean放到map中,防止重复加载)。扫描加载了mapper bean之后,processBeanDefinitions方法就会对这些bean进行处理:
这里设置了bean的beanClass,为后面的设置动态代理打下基础,beanClass的值为MapperFactoryBean(MapperFactoryBean实现了FactoryBean接口)。
至此,@MapperScan的流程就走完了,mapper接口被初始化为bean(非完整bean,还待进一步完善)
在spring容器初始化的refresh方法中,走到finishBeanFactoryInitialization时,会调用FactoryBean接口的getObject方法,针对mapper bean,会调用MapperFactoryBean的getObject方法。
最终会调用到MapperRegistry的getMapper方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) 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); } }
MapperProxyFactory的newInstance方法实现以下:
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
MapperProxy的实现以下:
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override 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 (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } ...... }
MapperProxyFactory的newInstance就返回了具体的动态代理类给mapper bean,这样,mapper bean就和具体的动态代理类绑定到了一块儿。