Spring整合MyBatis底层原理
项目结构图
项目代码
-
build.gradle需要进入的依赖
// testImplementation(platform("org.junit:junit-bom:5.9.1")) // testImplementation("org.junit.jupiter:junit-jupiter") implementation("org.aspectj:aspectjweaver:1.9.7") annotationProcessor("org.aspectj:aspectjweaver:1.9.7") implementation("javax.annotation:javax.annotation-api:1.3.2") annotationProcessor("javax.annotation:javax.annotation-api:1.3.2") implementation("javax.inject:javax.inject:1") annotationProcessor("javax.inject:javax.inject:1") implementation(project(":spring-context")) implementation(project(":spring-jdbc")) implementation(project(":spring-tx")) implementation(project(":spring-webmvc")) // mvc implementation(project(":spring-context-support")) // freemarker implementation("mysql:mysql-connector-java:5.1.40") implementation("org.mybatis:mybatis:3.5.7") implementation("org.mybatis:mybatis-spring:1.3.2")
-
自定义注解XiaogeScan
package com.xiaoge.springmybatis.annotation; import com.xiaoge.springmybatis.springintegrationmybatis.XiaogeBeanDefinitionRegister; import org.springframework.context.annotation.Import; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ @Retention(RetentionPolicy.RUNTIME) @Import(XiaogeBeanDefinitionRegister.class) // import注解是可以这样用的 public @interface XiaogeScan { String value() default ""; }
-
定义三个mapper
package com.xiaoge.springmybatis.mapper; import org.apache.ibatis.annotations.Select; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public interface UserMapper { @Select("select user") String selectById(Integer id); }
package com.xiaoge.springmybatis.mapper; import org.apache.ibatis.annotations.Select; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public interface MemberMapper { @Select("select member") String selectById(Integer id); }
package com.xiaoge.springmybatis.mapper; import org.apache.ibatis.annotations.Select; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public interface OrderMapper { @Select("select order") String selectById(Integer id); }
-
定义一个service
package com.xiaoge.springmybatis.service; import com.xiaoge.springmybatis.mapper.MemberMapper; import com.xiaoge.springmybatis.mapper.OrderMapper; import com.xiaoge.springmybatis.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ @Component public class UserService { @Autowired private UserMapper userMapper; // 代理对象赋值给这个属性, spring属性注入, 对象 @Autowired private OrderMapper orderMapper; @Autowired private MemberMapper memberMapper; public void test() { System.out.println(userMapper); System.out.println(userMapper.getClass().getSimpleName()); System.out.println(orderMapper); System.out.println(orderMapper.getClass().getSimpleName()); System.out.println(memberMapper); System.out.println(memberMapper.getClass().getSimpleName()); userMapper.selectById(1); orderMapper.selectById(1); memberMapper.selectById(1); } }
-
重点整合mybatis
package com.xiaoge.springmybatis.springintegrationmybatis; import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * TODO spring 整合 mybatis * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public class SpringIntegrationMybatis implements FactoryBean { // todo FactoryBean 本身就是一个特殊bean private Class mapper; // 灵活设置属性 public SpringIntegrationMybatis(Class mapper) { this.mapper = mapper; } @Override public Object getObject() throws Exception { // mybatis 代理对象 Object o = Proxy.newProxyInstance(SpringIntegrationMybatisTwo.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { System.out.println(method.getName()); return method.invoke(this, args); } System.out.println(method.getName()); return null; } }); return o; // bean } @Override public Class<?> getObjectType() { return mapper; } }
-
重点怎么把扫描到的mapper生成BeanDefinition的
package com.xiaoge.springmybatis.springintegrationmybatis; import com.xiaoge.springmybatis.annotation.XiaogeScan; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import java.beans.Introspector; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * TODO 该类通过 import 注解导入, 它会去执行 该类下面的方法, 原因可以去看@Import注解源码 * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public class XiaogeBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { // 获取我们扫描注解上面的路径 Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(XiaogeScan.class.getName()); String path = (String) annotationAttributes.get("value"); // 扫描 List<Class> mappers = new ArrayList<>(); doScan(path, mappers); mappers.forEach(mapper -> { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(); AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); // 给构造方法添加参数 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper); // new SpringIntegrationMybatisTwo(mapper) // 设置类型 beanDefinition.setBeanClass(SpringIntegrationMybatis.class); // 注册BeanDefinition (beanName是根据spring源码用到的方法生成的) registry.registerBeanDefinition(Introspector.decapitalize(mapper.getSimpleName()), beanDefinition); }); } /** * 扫描当前包下所有带@Mapper注解的接口 */ private void doScan(String path, List<Class> mappers) { // 获取路径 path = path.replace(".", "/"); // 获取资源路径 ClassLoader classLoader = XiaogeBeanDefinitionRegister.class.getClassLoader(); URL resource = classLoader.getResource(path); // 拿到当前目录下面所有的文件 File files = new File(resource.getFile()); // 当前文件是不是目录 if (files.isDirectory()) { // 获取目录下所有文件 for (File file : files.listFiles()) { // 获取文件绝对路径 String fileName = file.getAbsolutePath(); // 获取com->.class结尾的名称 String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")); // 获取权限定类名 className = className.replace("\\", "."); try { // 加载class Class<?> clazz = classLoader.loadClass(className); mappers.add(clazz); } catch (ClassNotFoundException e) { } } } } }
-
配置类
package com.xiaoge.springmybatis; import com.xiaoge.springmybatis.annotation.XiaogeScan; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.ComponentScan; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ @ComponentScan @XiaogeScan("com.xiaoge.springmybatis.mapper") public class AppConfig { }
-
启动类
package com.xiaoge.springmybatis; import com.xiaoge.springmybatis.mapper.UserMapper; import com.xiaoge.springmybatis.service.UserService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * TODO * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public class SpringIntegrationMybatisMain { public static void main(String[] args) { // 创建 ApplicationContext 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = applicationContext.getBean(UserService.class); userService.test(); System.out.println(applicationContext.getBean("userMapper")); } } // 执行结果 toString com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@2aa5fe93 $Proxy11 toString com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5c1a8622 $Proxy12 toString com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5ad851c9 $Proxy13 selectById selectById selectById toString com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@2aa5fe93
mybatis-spring源码它是怎么整合的
-
先通过MapperScan找到对应的MapperScannerRegistrar
-
主要看这个类的这个方法registerBeanDefinitions
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 获取MapperScan注解相关信息, 就是MapperScan里面我们会配置路径什么啊等 AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); // 获取spring扫描器 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // this check is needed in Spring 3.1 if (resourceLoader != null) { scanner.setResourceLoader(resourceLoader); } // 解析MapperScan annotationClass 属性 Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } // 解析MapperScan markerInterface 属性 Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } // 解析MapperScan nameGenerator 属性 Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); } // 解析MapperScan factoryBean 属性 Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); } // 解析MapperScan sqlSessionTemplateRef 属性 scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); // 解析MapperScan sqlSessionFactoryRef 属性 scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); // 添加扫描包路径 List<String> basePackages = new ArrayList<String>(); for (String pkg : annoAttrs.getStringArray("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } // 添加扫描包路径 for (String pkg : annoAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } // 添加class权限定类名 for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } // 添加一些过滤器, 因为spring扫描到了也不一定注册到BeanDefinitionMap中它还需要通过过滤器条件 scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); }
@Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { // 扫描该包, 拿到是所有的BeanDefinition Set<BeanDefinitionHolder> 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; }
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; // 循环beanDefinitions for (BeanDefinitionHolder holder : beanDefinitions) { // 拿到对应的beanDefinitions definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // 给beanDefinition设置相关属性 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // 设置bean类型 definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { 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) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } // 设置它的依赖注入类型 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }
package org.mybatis.spring.mapper; import static org.springframework.util.Assert.notNull; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.session.Configuration; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.beans.factory.FactoryBean; /** * 可以看到它跟我们写的那个差不多, 都是实现了FactoryBean * mapper都是class类型 private Class<T> mapperInterface; * 看getObject方法getMapper它是怎么代理mapper的 */ public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { //intentionally empty } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * {@inheritDoc} */ @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } } /** * {@inheritDoc} */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } /** * {@inheritDoc} */ @Override public Class<T> getObjectType() { return this.mapperInterface; } /** * {@inheritDoc} */ @Override public boolean isSingleton() { return true; } //------------- mutators -------------- /** * Sets the mapper interface of the MyBatis mapper * * @param mapperInterface class of the interface */ public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * Return the mapper interface of the MyBatis mapper * * @return class of the interface */ public Class<T> getMapperInterface() { return mapperInterface; } /** * If addToConfig is false the mapper will not be added to MyBatis. This means * it must have been included in mybatis-config.xml. * <p/> * If it is true, the mapper will be added to MyBatis in the case it is not already * registered. * <p/> * By default addToCofig is true. * * @param addToConfig */ public void setAddToConfig(boolean addToConfig) { this.addToConfig = addToConfig; } /** * Return the flag for addition into MyBatis config. * * @return true if the mapper will be added to MyBatis in the case it is not already * registered. */ public boolean isAddToConfig() { return addToConfig; } }
@Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
这里就用了动态代理
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); } }
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }