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,整个过程可以分为以下步骤
- 加载注册jdbc驱动
- 获取mysql连接
- 组装SQL语句
- 创建Statement对象
- 执行SQL
- 释放资源 statement对象以及连接
上诉几步,第一步(Class.forName("com.mysql.cj.jdbc.Driver");)其实可以去除,因为在mysql-connector 5.0版本之后,引入了SPI。会自动将驱动加载注册进去。
SPI具体执行加载注册驱动的过程:
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&characterEncoding=utf8&generateSimpleParameterMetadata=true&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");
}
可以发现userService对应的bean实例,其实是个代理类。下面分析下这个代理类是如何生成的,以及怎么实现事务管理。
- 最开始的类-TxNamespaceHandler
因为是XML配置方式,依据spring-tx.xsd,找到spring.handlers文件,可以最开始启动类TxNamespaceHandler。如下图所示
// 在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
查看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那一坨配置,具体加载过程如下
分析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就行。能实现这步实现,还是在于使用了代理,如下图所示,逐步分析下这个代理是如何被创建的。
- MapperScan
查看源码,关注两个地方@Import(MapperScannerRegistrar.class)以及 factoryBean() default MapperFactoryBean.class - MapperScannerRegistrar
这个类需要关注的在于registerBeanDefinitions()中这句
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
- MapperScannerConfigurer
需要关注得是postProcessBeanDefinitionRegistry
ClassPathMapperScanner scanner = new ClassPathMapperScanner();
……
// 这一步得作用在于将MapperScan注解中的路径下所有的Mapper接口都找出来并注册bean定义
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
- 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被实例化的过程
- SqlSessionFactoryBean
此类关注afterPropertiesSet -> buildSqlSessionFactory
new DefaultSqlSessionFactory(config);//实例化sqlSessionFactory - 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即可,进一步简洁了重复代码