Java 连接MySQL二三事

111 阅读10分钟

Java 连接MySQL二三事

1、JDBC

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    try {
        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2、获取数据库连接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "1234");
        // 3、创建SQL语句
        String sql = "select * from t limit 1";
        // 4、创建statement对象
        stmt = conn.createStatement();
        // 5、执行SQL
        ResultSet rs = stmt.executeQuery(sql);
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            System.out.println(id + " " + name);
        }
    } catch(Exception e) {
        
    } finally {
        // 6、释放资源
        if (null != stmt) {
            try {
                stmt.close();
            } catch(Exception e) {
            
            }
            try {
                conn.close();
            } catch(Exception e) {
                
            }
        }
    }
}

如上就是一个最简单的使用jdbc连接MySQL的demo,整个过程可以分为以下步骤

  1. 加载注册jdbc驱动
  2. 获取mysql连接
  3. 组装SQL语句
  4. 创建Statement对象
  5. 执行SQL
  6. 释放资源 statement对象以及连接

上诉几步,第一步(Class.forName("com.mysql.cj.jdbc.Driver");)其实可以去除,因为在mysql-connector 5.0版本之后,引入了SPI。会自动将驱动加载注册进去。

SPI具体执行加载注册驱动的过程: image.png 1、在META-INF/services/目录下有个一个与java.sql.Driver文件名的文件(与interface的权限名相同),文件内容为具体实现类的全限定名;
2、通过DriverManager源码可以发现

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

// loadInitialDrivers 关键代码片段
AccessController.doPrivileged(new PrivilegedAction<Void>() {
    public Void run() {
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> driverIterator = loadedDrivers.iterator();
        try {
            while(driverIterator.hasNext()) {
                driverIterator.next();
            }
        } catch(Exception e) {
            // do nothing
        }
        return null;
    }
});
// driverIterator.hasNext() -> ServiceLoader.hasNextService() 关键代码片段
try {
    String fullName = PREFIX + service.getName();// 这句其实就是/META-INF/services/java.sql.Driver
    if (loader == null) {
        configs = ClassLoader.getSystemResources(fullName);
    } else {
        configs = loader.getResources(fullName);
    }
} catch (IOException x) {
    ……
}
// 可以发现这步主要就是将所有含/META-INF/services/java.sql.Driver的文件都找出来
// driverIterator.next() -> ServiceLoader.nextService() 关键代码片段
Class<?> c = null;
try {
    c = Class.forName(cn, false, loader);// cn = 上一步获取的实现类的全限定名
} catch (ClassNotFoundException e) {
    ……
}
……
try { 
    S p = service.cast(c.newInstance());
    providers.put(cn, p);
    return p;
} catch (Throwable x) {
    ……
}
// 这一步会实现驱动类的注册加载,同时将其实例化

第二步获取MySQL连接 DriverManager.getConnect()关键代码片段

for (DriverInfo aDriver : registeredDrivers) {
    if (isDriverAllowed(aDriver, callerCL)) {
        try {
            Connection con = aDriver.driver.connect(url, info);
            ……
            return con;
        } catch (SQLException ex) {
            ……
        }
    }
}
// 而registeredDrivers来自于具体实现类初始化时,会注册进来
// 以com.msyql.cj.jdbc.Driver为例,关键代码片段
static {
    try {
        DriverManager.registerDriver(new Driver());//注册到registeredDrivers中
    } catch (SQLException e) {
        ……
    }
}

2、Spring JdbcTemplate

Spring中的JdbcTemplate主要就是解决了上诉JDBC直连MySQL,注册驱动 -> 获取连接 -> 创建Statement对象 -> 组装SQL -> 执行SQL -> 解析ResultSet -> 关闭资源,这一套繁琐的动作。 让使用者的重心在于拼接SQL,以及组装结果对应的Bean.

// 一个JdbcTemplate使用的简单demo
String sql = "select * from t limit 1";
TBean t = jdbcTemplate.queryForObject(sql, TBean.class);
// 相比较于JDBC直连的写法,使用jdbc的方式还是比较简洁的
// 跟下源码 
public <T> T JdbcTempalte.execute(StatementCallback<T> action, boolean closeResources) {
    // 部分关键代码片段
    Connection con = DataSourceUtils.getConnection(this.obtainDataSource());// 获取连接
    Statement stmt = null;
    try {
        stmt = con.createStatement();// 创建Statement对象
        ……
        T result = action.doInStatement(stmt);// 执行SQL,组装返回结果
    } catch (SQLException e) {
        ……
    } finally {
        if (closeResources) {
            JdbcUtils.closeStatement(stmt);// 关闭Statement
            DataSourceUtils.releaseConnection(con, this.getDataSource());// 关闭连接或者归还连接到连接池
        }
    }
}
// StatementCallback<T> action实现代码片段
public T doInStatement(Statement stmt) {
    ResultSet rs = null;
    Object r;
    try {
        rs = stmt.executeQuery(sql);// 执行SQL
        r = rse.extractData(rs);// 组装resultSet结果到对应bean中
    } finally {
    }
    return r;
}

3、Spring @Transcation

3.1、JDBC 直接使用事务

public static void main(String[] args) {
    Connection con = null;
    PrepareStatement pstmt1 = null;
    PrepareStatement pstmt2 = null;
    try {
        // 1、注册驱动 (可省)
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2、获取MySQL连接
        con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "1234");
        // 3、开启事务(关闭自动事务,开启手动事务)
        con.setAutoCommit(false);
        // 4、组装SQL
        String sql1 = "update t set code = code + 1 where id = ?";
        String sql2 = "update t set code = code - 1 where id = ?";
        // 5、创建PrepareStatement对象
        pstmt1 = con.prepareStatement(sql1);
        pstmt2 = con.prepareStatement(sql2);
        // 6、提交事务
        con.commit();
    } catch (Exception e) {
        if (null != con) {
            // 出现异常,回滚事务
            con.rollback();
        }
    } finlly {
        // 关闭资源
        if (null != pstmt1) {
            pstmt1.close();
        }
        if (null != pstmt2) {
            pstmt2.close();
        }
        if (null != con) {
            con.close();
        }
    }
}

3.2、编程式事务

// 1、初始化一个数据源
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:localhost:3306/test?useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("1234");
// 2、定义一个JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 3、定义一个事务管理器
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
// 4、定义事务属性(指定事务级别等)
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 5、开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
    // 6、执行sql
    jdbcTemplate.update("update t set code = code + 1 where id = 1");
    jdbcTemplate.update("update t set code = code - 1 where id = 2");
    // 7、提交事务
    transactionManager.commit(transactionStatus);
} catch (Exception e) {
    // 8、回滚事务
    transactionManager.rollback(transactionStatus);
}

代码分析
DataSourceTransactionManager.getTransaction(transactionDefinition);

// 关键代码片段
getTransaction() -> tartTransaction() -> doBegin()
在doBegin中,发现如下代码片段
……
// 获取MySQL连接,并置入ThreadLocal中
Connection newCon = this.obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(), true);
……
if (con.getAutoCommit()) {
    ……
    con.setAutoCommit(false);// 熟悉的味道,开启手动事务
}

transactionManager.commit(transactionStatus);

// 关键代码片段
commit() -> processCommit() -> doCommit()
在doCommit()中可以发现con.commit(),事务提交的关键代码

transactionManager.rollback(transactionStatus);

rollback() -> processRollback() -> doRollback()
在doRollback()中也可以发现con.ro lback(), 事务回滚的代码

上诉代码比较繁杂,需要自己手动管理开启事务、commit和异常回滚,Spring,还提供了TransactionTemplate,模板方法模式,将手动开启事务、commit、rollback,封装在一起,只需专注于SQL执行部分;

// 1、初始化一个数据源
BasicDataSource dataSource = new BasicDataSource();
// 2、定义一个JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 3、定义一个事务管理器
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
// 4、定义事务属性(指定事务级别等)
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 5、定义一个TransactionTemplate
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager, transactionDefinition);
// 6、执行sql
transactionTemplate.executeWithoutResult((status) -> {
    jdbcTemplate.update("update t set code = code + 1 where id = 1");
    jdbcTemplate.update("update t set code = code + 1 where id = 2");
});

探究executeWithoutResult -> exceute()

execute()关键代码片段
TransactionStatus status = this.transactionManager.getTransaction(this);// 开启事务
try {
    Object result = action.doInTransaction(status);// 执行SQL的回调
} catch (Exception e) {
    this.rollbackOnException(status, e);// 就是transactionManager.rollback();
}
this.transactionManager.commit(status);// 提交事务

虽然比之前的例子,稍微简化点,还是比较繁琐,一般用的都是声明式事务,下文来介绍下声明式事务。

3.3、声明式事务

3.3.1、XML配置方式

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="jndi" />
    </bean>
    <bean id="jndi" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;generateSimpleParameterMetadata=true&amp;rewriteBatchedStatements=true"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <bean id="userService" class="springstudy.UserServiceImpl"/>
</beans>
@Override
@Transactional
public void sayHi() {
    System.out.println("hi");
}

image.png 可以发现userService对应的bean实例,其实是个代理类。下面分析下这个代理类是如何生成的,以及怎么实现事务管理。

  • 最开始的类-TxNamespaceHandler
    因为是XML配置方式,依据spring-tx.xsd,找到spring.handlers文件,可以最开始启动类TxNamespaceHandler。如下图所示 image.png
// 在TxNamespaceHandler的init方法可以发现与xml中annotation-driven关联的bean定义解析器
    registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
  • AnnotationDrivenBeanDefinitionParser
// 这个类的作用用来解析XML配置,具体parse方法,核心为下面这句
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parseContext);
// configureAutoProxyCreator() 关键代码片段
public static void configureAutoProxyCreator(Element element, ParserContext parseContext) {
    // 注册InfrastructureAdvisorAutoProxyCreator的bean定义,这个类是后面创建代理类的关键
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
……
RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transacation.annotation.AnnotationTransactionAttributeSource");
……
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
……
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
……
}
// 上诉核心为BeanFactoryTransactionAttributeSourceAdvisor,AnnotationTransactionAttributeSource、TransactionInterceptor为其的属性

上诉一通操作,主要就准备了两个类InfrastructureAdvisorAutoProxyCreator、BeanFactoryTransactionAttributeSourceAdvisor两个类。

  • InfrastructureAdvisorAutoProxyCreator image.png 查看InfrastructureAdvisorAutoProxyCreator的类图可以发现该类实现了BeanPostProcesser接口,基本可以判定会在bean实例化之后,做后置增强时应该会调用到该类。具体分析而言在AbstractAutowireCapableBeanFactory.doCreateBean() -> AbstractAutowireCapableBeanFactory.initializeBean() -> AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization()
// applyBeanPostProcessorsAfterInitialization()关键代码片段
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
    Object current = processor.postProcessAfterInitialization(result, beanName);
    ……
}
// 回到InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization方法
// postProcessAfterInitialization -> wrapIfNecessary -> getAdvicesAndAdvisorsForBean -> findEligibleAdvisors
// AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 关键代码片段
…… findEligibleAdvisors() {
    // 将Advisor的bean实例找出来,其中就包括BeanFactoryTransactionAttributeSourceAdvisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 找出适用于beanClass的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    ……
}
// 进一步跟下去
// findAdvisorsThatCanApply -> AopUtils.findAdvisorsThatCanApply -> AopUtils.canApply
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    ……
    // 查看BeanFactoryTransactionAttributeSourceAdvisor的类图可以发现实现的时PointCutAdvisor,所以执行这一步
    else if (advisor instanceof PointCutAdvisor pca) {// java17的新语法
        return canApply(pca.getPointCut(), targetClass, hasIntroductions);
    }
    ……
}
// AopUtils.canApply(PointCut pc, Class<?> tragetClass, boolean hasIntroductions) 关键代码片段
public static boolean canApply(PointCut pc, Class<?> tragetClass, boolean hasIntroductions) {
    ……
    MethodMacther methodMacther = pc.getMethodMactcher();
    ……
    for (Class<?> clazz : classes) {// classes就是targetClass加上其对应interface
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (…… ? …… : methodMacther.matches(method, targetClass)) {
                return true
            }
        }
    }
    ……
}
  • BeanFactoryTransactionAttributeSourceAdvisor
    前面代码寻找适合的advisor过程中methodMacther.matches(method, targetClass),methodMacther具体而言就是BeanFactoryTransactionAttributeSourceAdvisor.getPointCut().getMethodMatcher()。
// 具体的methodMacther.matches() 一路跟下来代码如下
public boolean matches(Method method, Class<?> targetClass) {
    // 具体就是前面提及的AnnotationTransactionAttributeSource
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttibute(method, targetClass) != null);
}
  • AnnotationTransactionAttributeSource
// tas.getTransactionAttibute(method, targetClass)
// 一路跟下去,最后涉及的AnnotationTransactionAttributeSource.findTransactionAttribute(Method method) ->
// AnnotationTransactionAttributeSource.determineTransactionAttribute()
// determineTransactionAttribute() 关键代码片段
…… determineTransactionAttribute(……) {
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        // 此处的parser就是SpringTransactionAnnotationParser
        TransactionAttribute attr = parser.parserTransactionAnnotation(element);
        ……
    }
}
  • SpringTransactionAnnotationParser
…… parserTransactionAnnotation() {
        AnnotationAttributes attribute = AnnotatedElementUtils.findMergedAnnotationAttributes(element, 
            Transactional.class, false, false);// 终于看到Transactional注解了
        ……
}

至此终于用到了Transactional注解,前面一通输出,目的其实就一个找到判断bean实例是否有Transational注解,如果有找出适配Transactional注解的Advisor.
重新回到InfrastructureAdvisorAutoProxyCreator, 下一步创建AOP代理对象,具体方法AbstractAutoProxyCreator.createProxy -> ProxyFactory.getProxy -> DefaultAopProxyFactory.createAopProxy -> JdkDynamicAopProxy.getProxy

// JdkDynamicAopProxy.getProxy()
// config就是上一步获取的Advisor,其中与创建代理相关的就是TransactionInterceptor
new JdkDynamicAopProxy(config);

再看下具体执行的流程,JdkDynamicAopProxy.invoke方法

public Object invoke(……) {
    ……
    // 这一步的目的取出TransactionInterceptor
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    ……
    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    retVal = invocation.proceed();// 具体调用,
    ……
}

继续跟下去,会发现ReflectiveMethodInvocation.proceed -> TransactionInterceptor.invoke -> TransactionAspectSupport.invokeWithinTransaction

// TransactionAspectSupport.invokeWithinTransaction 关键代码片段
protected Object invokeWithinTransaction(……) {
    ……
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    ……
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);// 核心status = tm.getTransaction(txAttr);开启事务
    Object retVal;
    try {
	retVal = invocation.proceedWithInvocation();// 具体SQL执行
    } catch (Throwable ex) {
        // txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());事务回滚
	completeTransactionAfterThrowing(txInfo, ex);
	throw ex;
    }
    ……
    // txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());事务提交
    commitTransactionAfterReturning(txInfo);
    return retVal;
    ……
}

上诉代码就很熟悉了,跟第二节的编程式事务如出一辙

3.3.2、注解式

@Configuration
@EnableTransactionManagement
public class MainConfig {
    @Bean
    public DataSource ds() {
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
    
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource ds) {
	return new DataSourceTransactionManager(ds);
    }
    
    @Bean
    public UserSerive() {
        return new UserServiceImpl();
    }
}

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
    UserService service = (UserService) context.getBean("userService");
    service.sayHi();
}

注解式更加简洁,主要由EnableTransactionManagement注解,替换了XML那一坨配置,具体加载过程如下 image.png 分析Spring IOC的过程,初始加载代码在ConfigurationClassParser.doProcessConfigurationClass

…… doProcessConfigurationClass(……) {
    ……
    // 处理@Import注解,这步中会初始化TransactionManagementConfigurationSelector
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    ……
}

…… processImports(……) {
    ……
    // 初始化TransactionManagementConfigurationSelector
    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
    ……
    // 此处就是TransactionManagementConfigurationSelector.selectImports
    String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
    ……
}

TransactionManagementConfigurationSelector.selectImports() {
    switch (adviceMode) {
	case PROXY:
		return new String[] {AutoProxyRegistrar.class.getName(),
		ProxyTransactionManagementConfiguration.class.getName()};
	case ASPECTJ:
		return new String[] {determineTransactionAspectClass()};
	default:
		return null;
    }
}

在 ProxyTransactionManagementConfiguration里面有可能发现几个熟悉的面孔,BeanFactoryTransactionAttributeSourceAdvisor、AnnotationTransactionAttributeSource、TransactionInterceptor,后续的流程就跟 3.3.1节XML配置一样了

4、Mybatis

4.1、Mybatis小demo

@MapperScan("xxx.xx.bb")// mybaits启动加载注解
public classs xxxConfig {
    @Bean
    public DataSource dataSource() {
        ……
    }
    
    @Bean
    public DataSourceTransactionManager transactionManager() {
        ……
    }
    
    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }
    
}

@Repository
public interface AbcMapper { //dao层接口
    Student getStudentById(int id);
}
<? xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xxx.AbcMapper">
    <select id="getStudentById" returnType="xxx.Student">
        select id, name, age from student where id = #{id}
    </select>
</mapper>

4.2、MyBatis工作原理

mybatis相比于上面的JdbcTemplate,又进一步收敛简化了很多代码,只需要定义一个dao层接口,编写SQL就行。能实现这步实现,还是在于使用了代理,如下图所示,逐步分析下这个代理是如何被创建的。 image.png

  1. MapperScan
    查看源码,关注两个地方@Import(MapperScannerRegistrar.class)以及 factoryBean() default MapperFactoryBean.class
  2. MapperScannerRegistrar
    这个类需要关注的在于registerBeanDefinitions()中这句
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
  1. MapperScannerConfigurer
    需要关注得是postProcessBeanDefinitionRegistry
ClassPathMapperScanner scanner = new ClassPathMapperScanner();
……
// 这一步得作用在于将MapperScan注解中的路径下所有的Mapper接口都找出来并注册bean定义
scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  1. ClassPathMapperScanner 这个类关注doScan -> processBeanDefinitions
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    ……
    //  beanClassName就是上一步解析出来的Mapper的全路径限定名
    String beanClassName = definition.getBeanClassName(); 
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
    // 此处的mapperFactoryBeanClass就是MapperFactoryBean.class, 此处已经将之前Mapper bean定义,beanClass替换为了MapperFactoryBean,此为生成代理的关键
    definition.setBeanClass(this.mapperFactoryBeanClass);
    ……
}

至此package下mapper bean定义注册结束,下面就是bean被实例化的过程

  1. SqlSessionFactoryBean
    此类关注afterPropertiesSet -> buildSqlSessionFactory
    new DefaultSqlSessionFactory(config);//实例化sqlSessionFactory
  2. MapperFactoryBean
    关注getObject
public T getObject() {
    return getSqlSession().getMapper(this.mapperInterface);
}
// getSqlSession -> setSqlSessionFactory -> createSqlSessionTemplate
`new SqlSessionTemplate(sqlSessionFactory);`// sqlSessionFactory就是上一步创建的DefaultSqlSessionFactory实例
// getMapper -> MapperRegistry.getMapper
// 源码如下
public <T> T getMapper() {
    // 这个地的mapperProxyFactory创建在MapperFactoryBean实例化时,见下面的源码
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        // 到这步,maopper对应的代理类实例也就被创建了传来
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}
// mapperProxyFactory.newInstance(sqlSession);源码如下
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}
// MapperProxyFactory的创建源自MapperFactoryBean.afterPropertiesSet() -> 
// MapperFactoryBean.checkDaoConfig -> Configuration.addMapper -> MapperRegistry.addMapper
// 部分源码如下
public <T> void addMapper(Class<T> type) {
    ……
    knownMappers.put(type, new MapperProxyFactory<>(type));
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    // 这一步用来解析Mapper对应的XML配置
    parser.parse();
    ……
}

至此代理bean,以及xml解析的过程都有了,下面就是看到具体调用时,mybatis如何实现?
7. MapperProxy 因为是JDK代理类,这一步的实现主要就看InvocationHandler的实现类,也就是上面提及的MapperProxy

// MapperProxy -> MapperMethod -> MapperMethod.execute()具体SQL执行的方法

综上,mybatis通过代理的方式,jdbcTemplate具体操作封装到了代理类中(mybatis没有使用Spring的JdbcTemplate,自己实现了类似的的SqlSessionTemplate),只需要定义dao层的interface和sql脚本XML即可,进一步简洁了重复代码