[心得体会]spring事务源码分析
spring事务源码分析
1. 事务的初始化注册(从 @EnableTransactionManagement 开始)
@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement {
跟进这个 TransactionManagementConfigurationSelector 类
发现这个类是通过 Selector 引入的, 学过spring容器初始化源码分析会知道
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
selectImports这个方法最终会被调用
然后会发现抽象类实现了这个方法
并且在78行调用了一个新的selectImports方法
发现这个方法未实现, 说明在他的之类里面被实现了
上图就是实现的过程, 还有如果是aspectj的话调用determineTransactionAspectClass方法
总体初始化过程就是这样, 现在开始源码级别的分析
首先分析
org.springframework.context.annotation.AdviceModeImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 拿出了EnableTransactionManagement 注解的Class类
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
// 拿到了注解中的所有属性字段(这里所有都是默认的, 我们没有添加任何值)
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
// 去拿 mode 中的值, 这里拿出的是代理模式(这里默认存在两个模式, 一个代理模式, 另一个是aspectJ模式)
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
// 返回子类中实现的selectImports方法执行的结果(这里是代理模式所以默认是org.springframework.context.annotation.AutoProxyRegistrar和org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration 这两个类)
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
// 注册这两个类
return imports;
}
如果上面是AspectJ模式的话, 那么就会在两个类中选取一个
前提是根据 javax.transaction.Transactional 注解是否存在如果存在注册
org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration
否则注册
org.springframework.transaction.config.TransactionManagementConfigUtils#TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
这里暂时不分析AspectJ模式
那么现在留下了两个注入容器的类到底做了啥???
org.springframework.context.annotation.AutoProxyRegistrar
org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
(1) 分析 AutoProxyRegistrar做了什么?
发现这个类也是为了注册某些bean而使用的
org.springframework.context.annotation.AutoProxyRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isInfoEnabled()) {
String name = getClass().getSimpleName();
logger.info(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
上面的这个代码里面注册了一个类
org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator
使用
key: org.springframework.aop.config.internalAutoProxyCreator
value: org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator
借助Map注册到容器中
到这里就知道它的作用了, 就是注册了这个类 InfrastructureAdvisorAutoProxyCreator
(2) 分析ProxyTransactionManagementConfiguration做了什么?
注入的第二个类
org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
看下它都做了什么?
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableTx;
/**
* Default transaction manager, as configured through a {@link TransactionManagementConfigurer}.
*/
@Nullable
protected TransactionManager txManager;
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
// 获取EnableTransactionManagement注解的属性存入enableTx
this.enableTx = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
@Autowired(required = false)
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
}
TransactionManagementConfigurer configurer = configurers.iterator().next();
this.txManager = configurer.annotationDrivenTransactionManager();
}
@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}
}
@Configuration(proxyBeanMethods = false)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource,
TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(
TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
从上面来看, 就是注册了几个Bean
1) org.springframework.transaction.event.TransactionalEventListenerFactory
2) org.springframework.transaction.annotation.AnnotationTransactionAttributeSource(通知)
3) org.springframework.transaction.interceptor.TransactionInterceptor(切点)
4) org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor(Advisor对象)
(3) 分析事务的使用过程
(由于不知道如何抓取事务处理过程在哪里, 所以我们在service的方法上插入断点, 准备通过调用栈查看调用过程)
类似于这样
发现最先调用的是 JdkDynamicAopProxy 这个类
想看了下类图
发现了使用了动态代理的两个接口类
AopProxy
InvocationHandler
估计这个类和动态代理有关系
先看这个类中的一个方法
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
// 参数获取了很多的代理类存在的接口
for (Class<?> proxiedInterface : proxiedInterfaces) {
// 遍历接口
Method[] methods = proxiedInterface.getDeclaredMethods();
// 获取接口上的所有方法
for (Method method : methods) {
// 遍历方法
// 判断是否存在Equals方法
if (AopUtils.isEqualsMethod(method)) {
this.equalsDefined = true;
}
// 是否存在HashCode方法
if (AopUtils.isHashCodeMethod(method)) {
this.hashCodeDefined = true;
}
// 判断如果两个都存在这返回
if (this.equalsDefined && this.hashCodeDefined) {
return;
}
}
}
}
调用代理方法
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// 获取我们的serviceImpl实现类
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 判断是否存在equals
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
// 判断是否存在 hashCode
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 拿出serviceImpl类
target = targetSource.getTarget();
// 拿到serviceImpl的Class对象
Class<?> targetClass = (target != null ? target.getClass() : null);
//
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 关键的代码执行
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
进入上面的关键代码后
关注这个代码
再次进入下面这个方法
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
后就会看见关键的事务代码
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
// 这里我们不满足条件
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 满足条件
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 获取事务信息
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 调用事务修饰的方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 异常回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清除事务信息ThreadLocal
cleanupTransactionInfo(txInfo);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 事务的提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
2. 分析JdbcTemplate
分析这个, 就相当于分析如何初始化Jdbctemplate的初始化和使用还有销毁过程
(1) JdbcTemplate初始化
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
设置了数据源
afterPropertiesSet函数里面做了个简单的判断(isLazyInit 条件默认为true. 所以不需要判断这个)
@Override
public void afterPropertiesSet() {
if (getDataSource() == null) {
throw new IllegalArgumentException("Property 'dataSource' is required");
}
if (!isLazyInit()) {
getExceptionTranslator();
}
}
(2) JdbcTemplate使用
1) query
分析
@Test
public void testFindOne() throws Exception {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<>(Account.class), 1);
System.out.println(accounts.get(0));
}
queryForObject
@Override
@Nullable
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException {
List<T> results = query(sql, args, new RowMapperResultSetExtractor<>(rowMapper, 1));
return DataAccessUtils.nullableSingleResult(results);
}
和
query
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException {
return result(query(sql, args, new RowMapperResultSetExtractor<>(rowMapper)));
}
的区别
多了个 1
public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
return query(sql, newArgPreparedStatementSetter(args), rse);
}
// newArgPreparedStatementSetter(args)内部new了个ArgumentPreparedStatementSetter
protected PreparedStatementSetter newArgPreparedStatementSetter(@Nullable Object[] args) {
return new ArgumentPreparedStatementSetter(args);
}
public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
return query(new SimplePreparedStatementCreator(sql), pss, rse);
}
public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
return query(new SimplePreparedStatementCreator(sql), pss, rse);
}
public <T> T query(
PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {
Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query");
// psc ===> sql, pss ====> args, rse ===> Type
return execute(psc, new PreparedStatementCallback<T>() {
@Override
@Nullable
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
// 值把它设置进去, 这里面就是简单的数值设置
pss.setValues(ps);
}
// 执行sql语句获取结果集
rs = ps.executeQuery();
// 把参数绑定到结果中
return rse.extractData(rs);
}
finally {
// 结果集关闭
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}
// 分析这里, 获取线程绑定Connection的链接
Connection con = DataSourceUtils.getConnection(obtainDataSource());
PreparedStatement ps = null;
try {
// 获取PreparedStatement, 底层jdbc方式获取
ps = psc.createPreparedStatement(con);
// 设置最大行数、抓取数量和超时时间
applyStatementSettings(ps);
// 调用到doInPreparedStatement 执行lambda函数
T result = action.doInPreparedStatement(ps);
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
获取线程绑定链接
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
// 分析这个方法
Connection con = fetchConnection(dataSource);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
catch (RuntimeException ex) {
// Unexpected exception from external delegation call -> close Connection and rethrow.
releaseConnection(con, dataSource);
throw ex;
}
}
return con;
}
Connection con = fetchConnection(dataSource);
protected Connection getConnectionFromDriver(@Nullable String username, @Nullable String password) throws SQLException {
// 主要存储数据库账号和密码
Properties mergedProps = new Properties();
Properties connProps = getConnectionProperties();
if (connProps != null) {
mergedProps.putAll(connProps);
}
if (username != null) {
mergedProps.setProperty("user", username);
}
if (password != null) {
mergedProps.setProperty("password", password);
}
// 将账号和密码传到数据库引擎中去获取数据库链接, 分析这个方法
Connection con = getConnectionFromDriver(mergedProps);
if (this.catalog != null) {
con.setCatalog(this.catalog);
}
if (this.schema != null) {
con.setSchema(this.schema);
}
return con;
}
Connection con = getConnectionFromDriver(mergedProps);
protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
return DriverManager.getConnection(url, props);
}
上面的代码就看到了熟悉的代码
DriverManager.getConnection 新手入门写的Jdbc代码就有这句话
至此这个方法分析完毕 fetchConnection
开始分析
T result = action.doInPreparedStatement(ps);
执行lambda语句
pss.setValues(ps) 简单的设置值到sql里面进行查找
public void setValues(PreparedStatement ps) throws SQLException {
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
doSetValue(ps, i + 1, arg);
}
}
}
下面就这个可看可不看, 其实就是对类型进行比较, 然后设置value进去
private static void setValue(PreparedStatement ps, int paramIndex, int sqlType,
@Nullable String typeName, @Nullable Integer scale, Object inValue) throws SQLException {
if (inValue instanceof SqlTypeValue) {
((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName);
}
else if (inValue instanceof SqlValue) {
((SqlValue) inValue).setValue(ps, paramIndex);
}
else if (sqlType == Types.VARCHAR || sqlType == Types.LONGVARCHAR ) {
ps.setString(paramIndex, inValue.toString());
}
else if (sqlType == Types.NVARCHAR || sqlType == Types.LONGNVARCHAR) {
ps.setNString(paramIndex, inValue.toString());
}
else if ((sqlType == Types.CLOB || sqlType == Types.NCLOB) && isStringValue(inValue.getClass())) {
String strVal = inValue.toString();
if (strVal.length() > 4000) {
// Necessary for older Oracle drivers, in particular when running against an Oracle 10 database.
// Should also work fine against other drivers/databases since it uses standard JDBC 4.0 API.
if (sqlType == Types.NCLOB) {
ps.setNClob(paramIndex, new StringReader(strVal), strVal.length());
}
else {
ps.setClob(paramIndex, new StringReader(strVal), strVal.length());
}
return;
}
else {
// Fallback: setString or setNString binding
if (sqlType == Types.NCLOB) {
ps.setNString(paramIndex, strVal);
}
else {
ps.setString(paramIndex, strVal);
}
}
}
else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
if (inValue instanceof BigDecimal) {
ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
}
else if (scale != null) {
ps.setObject(paramIndex, inValue, sqlType, scale);
}
else {
ps.setObject(paramIndex, inValue, sqlType);
}
}
else if (sqlType == Types.BOOLEAN) {
if (inValue instanceof Boolean) {
ps.setBoolean(paramIndex, (Boolean) inValue);
}
else {
ps.setObject(paramIndex, inValue, Types.BOOLEAN);
}
}
else if (sqlType == Types.DATE) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Date) {
ps.setDate(paramIndex, (java.sql.Date) inValue);
}
else {
ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.DATE);
}
}
else if (sqlType == Types.TIME) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Time) {
ps.setTime(paramIndex, (java.sql.Time) inValue);
}
else {
ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIME);
}
}
else if (sqlType == Types.TIMESTAMP) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Timestamp) {
ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue);
}
else {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
}
}
// 这次执行的就是这样
else if (sqlType == SqlTypeValue.TYPE_UNKNOWN || (sqlType == Types.OTHER &&
"Oracle".equals(ps.getConnection().getMetaData().getDatabaseProductName()))) {
if (isStringValue(inValue.getClass())) {
ps.setString(paramIndex, inValue.toString());
}
else if (isDateValue(inValue.getClass())) {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
// Fall back to generic setObject call without SQL type specified.
ps.setObject(paramIndex, inValue);
}
}
else {
// Fall back to generic setObject call with SQL type specified.
ps.setObject(paramIndex, inValue, sqlType);
}
}
对象绑定到结果集
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
Assert.state(this.mappedClass != null, "Mapped class was not specified");
// 获取bean
T mappedObject = BeanUtils.instantiateClass(this.mappedClass);
// 放置到Bean包装中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
// 初始化Bean包装类
initBeanWrapper(bw);
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取总共有多少列的sql参数需要填写
int columnCount = rsmd.getColumnCount();
Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<>() : null);
for (int index = 1; index <= columnCount; index++) {
// 根据元数据和索引数列的位置确定这个索引的名字为 id
String column = JdbcUtils.lookupColumnName(rsmd, index);
// 将这个名字去掉空格然后所有字母修改为小写
String field = lowerCaseName(StringUtils.delete(column, " "));
// 根据名字获取PropertyDescriptor对象
PropertyDescriptor pd = (this.mappedFields != null ? this.mappedFields.get(field) : null);
if (pd != null) {
try {
// 根据结果集, 索引和PropertyDescriptor获取值, 分析内部代码①
Object value = getColumnValue(rs, index, pd);
if (rowNumber == 0 && logger.isDebugEnabled()) {
logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
"' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
}
try {
// 设置值到结果中, 查看源码分析②
bw.setPropertyValue(pd.getName(), value);
}
catch (TypeMismatchException ex) {
if (value == null && this.primitivesDefaultedForNullValue) {
if (logger.isDebugEnabled()) {
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
" and column '" + column + "' with null value when setting property '" +
pd.getName() + "' of type '" +
ClassUtils.getQualifiedName(pd.getPropertyType()) +
"' on object: " + mappedObject, ex);
}
}
else {
throw ex;
}
}
if (populatedProperties != null) {
populatedProperties.add(pd.getName());
}
}
catch (NotWritablePropertyException ex) {
throw new DataRetrievalFailureException(
"Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
}
}
else {
// No PropertyDescriptor found
if (rowNumber == 0 && logger.isDebugEnabled()) {
logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'");
}
}
}
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
"necessary to populate object of class [" + this.mappedClass.getName() + "]: " +
this.mappedProperties);
}
return mappedObject;
}
①分析 Object value = getColumnValue(rs, index, pd) 源码
protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {
// 结果集, 索引, 该索引的类型
return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());
}
根据类型去获取需要的value
public static Object getResultSetValue(ResultSet rs, int index, @Nullable Class<?> requiredType) throws SQLException {
if (requiredType == null) {
return getResultSetValue(rs, index);
}
Object value;
// 根据索引获取value
if (String.class == requiredType) {
return rs.getString(index);
}
else if (boolean.class == requiredType || Boolean.class == requiredType) {
value = rs.getBoolean(index);
}
else if (byte.class == requiredType || Byte.class == requiredType) {
value = rs.getByte(index);
}
else if (short.class == requiredType || Short.class == requiredType) {
value = rs.getShort(index);
}
else if (int.class == requiredType || Integer.class == requiredType) {
value = rs.getInt(index);
}
else if (long.class == requiredType || Long.class == requiredType) {
value = rs.getLong(index);
}
else if (float.class == requiredType || Float.class == requiredType) {
value = rs.getFloat(index);
}
else if (double.class == requiredType || Double.class == requiredType ||
Number.class == requiredType) {
value = rs.getDouble(index);
}
else if (BigDecimal.class == requiredType) {
return rs.getBigDecimal(index);
}
else if (java.sql.Date.class == requiredType) {
return rs.getDate(index);
}
else if (java.sql.Time.class == requiredType) {
return rs.getTime(index);
}
else if (java.sql.Timestamp.class == requiredType || java.util.Date.class == requiredType) {
return rs.getTimestamp(index);
}
else if (byte[].class == requiredType) {
return rs.getBytes(index);
}
else if (Blob.class == requiredType) {
return rs.getBlob(index);
}
else if (Clob.class == requiredType) {
return rs.getClob(index);
}
else if (requiredType.isEnum()) {
// Enums can either be represented through a String or an enum index value:
// leave enum type conversion up to the caller (e.g. a ConversionService)
// but make sure that we return nothing other than a String or an Integer.
Object obj = rs.getObject(index);
if (obj instanceof String) {
return obj;
}
else if (obj instanceof Number) {
// Defensively convert any Number to an Integer (as needed by our
// ConversionService's IntegerToEnumConverterFactory) for use as index
return NumberUtils.convertNumberToTargetClass((Number) obj, Integer.class);
}
else {
// e.g. on Postgres: getObject returns a PGObject but we need a String
return rs.getString(index);
}
}
else {
// Some unknown type desired -> rely on getObject.
try {
return rs.getObject(index, requiredType);
}
catch (AbstractMethodError err) {
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
}
catch (SQLFeatureNotSupportedException ex) {
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
}
catch (SQLException ex) {
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
}
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
// to the caller to convert them (e.g. through a ConversionService).
String typeName = requiredType.getSimpleName();
if ("LocalDate".equals(typeName)) {
return rs.getDate(index);
}
else if ("LocalTime".equals(typeName)) {
return rs.getTime(index);
}
else if ("LocalDateTime".equals(typeName)) {
return rs.getTimestamp(index);
}
// Fall back to getObject without type specification, again
// left up to the caller to convert the value if necessary.
return getResultSetValue(rs, index);
}
// Perform was-null check if necessary (for results that the JDBC driver returns as primitives).
return (rs.wasNull() ? null : value);
}
② bw.setPropertyValue(pd.getName(), value);
@Override
public void setValue(final @Nullable Object value) throws Exception {
// 这里就是获取setId这个方法
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(writeMethod);
return null;
});
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
writeMethod.invoke(getWrappedInstance(), value), acc);
}
catch (PrivilegedActionException ex) {
throw ex.getException();
}
}
else {
// 是否使用反射权限穿透
ReflectionUtils.makeAccessible(writeMethod);
// 获取对象和value, 然后设置到这个对象中
writeMethod.invoke(getWrappedInstance(), value);
}
}
这个wrappedObject就是上面的Bean, 如果设置它, 就相当于设置上面那个Bean
public final Object getWrappedInstance() {
Assert.state(this.wrappedObject != null, "No wrapped object");
return this.wrappedObject;
}
至此将值设置到结果的query方法分析完毕
2) update
现在分析update方法, 看下是否存在不同
public void testSave() throws Exception {
jdbcTemplate.update("insert into account(name, money) VALUES (?, ?)", "ccc", 1234f);
}
进入发现前面的源码都差不多
发现都差不多, 就中间的
protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss) {
// ...
int rows = ps.executeUpdate();
// ...
}
是不一样的
3.分析线程绑定Connection的方法
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
// ...
// 分析这段话
Connection con = DataSourceUtils.getConnection(obtainDataSource());
// ...
}
}
初始化的时候已经使用构造方法存入了DataSource
protected DataSource obtainDataSource() {
DataSource dataSource = getDataSource();
Assert.state(dataSource != null, "No DataSource set");
return dataSource;
}
public abstract class DataSourceUtils {
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
// 分析它
return doGetConnection(dataSource);
} catch (...) {
// ...
}
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
// 根据线程名字获取该线程中前一次获取的链接 Connection
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
// 判断前一次不存在链接或者它不是线程绑定的链接
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
// 直接拿出前一次缓存的Connection
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
// 链接持有者不存在链接, 则去抓取
// 内部代码就是简单的 Connection con = dataSource.getConnection();
// 上面注解的代码就是拿出一个新的Connection
Connection con = fetchConnection(dataSource);
// 判断Connection是否绑定了线程, (如果需要绑定线程的话, 需要在new Connection链接的时候调用
// TransactionSynchronizationManager.initSynchronization();
// 并且将这个Connection加入到Spring容器中 {@Bean}, 看 ① 的代码)
if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
// 对事务中的其他JDBC操作使用相同的连接, 线程绑定对象将在事务完成时通过同步删除
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
// 构造器的方式构造一个新的链接持有者, 并且将链接存入持有者体内
holderToUse = new ConnectionHolder(con);
}
else {
// 将链接设置到链接持有者中
holderToUse.setConnection(con);
}
// 链接持有者是链接索引自增
holderToUse.requested();
// 注册链接持有者
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
// 设置标志, 表示同步初始化器初始化完毕
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
// 绑定到资源对象
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
catch (RuntimeException ex) {
// 来自外部委托调用的意外异常——>关闭连接并重新抛出
// 如果发生异常, 这关闭链接
releaseConnection(con, dataSource);
throw ex;
}
}
return con;
}
①
@Bean
public Connection connection(DataSource dataSource) {
// 让这个线程通过 JdbcTemplate 获取的 Connection 是同一个
TransactionSynchronizationManager.initSynchronization();
return DataSourceUtils.getConnection(dataSource);
}
有一个线程, 则只要这个线程工作没做完, 那么Connection就会一直是同一个, 这样就保证了所有的业务是在同一个事务中
[心得体会]spring事务源码分析的更多相关文章
- spring事务源码分析结合mybatis源码(一)
最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
Spring中的数据访问,JdbcTemplate使用及源码分析 前言 本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成 对源码分析前,我希望先介绍一下Spring中数据访问的相关内容 ...
- spring事务源码分析结合mybatis源码(三)
下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-s ...
- Spring事务源码分析总结
Spring事务是我们日常工作中经常使用的一项技术,Spring提供了编程.注解.aop切面三种方式供我们使用Spring事务,其中编程式事务因为对代码入侵较大所以不被推荐使用,注解和aop切面的方式 ...
- Spring事务源码分析
首先看例子,这例子摘抄自开涛的跟我学spring3. @Test public void testPlatformTransactionManager() { DefaultTransactionDe ...
- spring事务源码分析结合mybatis源码(二)
让我们继续上篇,分析下如果有第二个调用进入的过程. 代码部分主要是下面这个: if (isExistingTransaction(transaction)) { return handleExisti ...
- spring事务源码研读1
转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void test ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- Spring AMQP 源码分析 02 - CachingConnectionFactory
### 准备 ## 目标 了解 CachingConnectionFactory 在默认缓存模式下的工作原理 ## 前置知识 <Spring AMQP 源码分析 01 - Impatie ...
随机推荐
- KVM虚拟化存储管理(4)
一.KVM存储虚拟化介绍 KVM 的存储虚拟化是通过存储池(Storage Pool)和卷(Volume)来管理的. Storage Pool 是宿主机上可以看到的一片存储空间,可以是多种型: Vol ...
- JQuery.Gantt开发指南(转)
说明 日前需要用到甘特图,以下转载内容源自网络. • 概述 1.JQuery.Gantt是一个开源的基于JQuery库的用于实现甘特图效果的可扩展功能的JS组件库. •前端页面 o 资源引用 首先我们 ...
- JDK5.0新特性1
目录 静态导入 自动装箱/拆箱 for-each循环 可变参数 枚举 JDK 5.0 新特性简介 JDK 5.0 的一个重要主题就是通过新增一些特性来简化开发,这些特性包括: 静态导入 自动装箱/拆箱 ...
- 几年前,为什么我撸了一套RabbitMQ客户端?
之前文章说过,如果使用 RabbitMQ,尽可能使用框架,而不要去使用 RabbitMQ 提供的 Java 版客户端. 细说起来,其实还是因为 RabbitMQ 客户端的使用有很多的注意事项,稍微不注 ...
- 项目中添加lib依赖
Project Structure-->Artifacts
- 如何挑选深度学习 GPU?
如何挑选深度学习 GPU? 深度学习是一个对计算有着大量需求的领域,从一定程度上来说,GPU的选择将从根本上决定深度学习的体验.因此,选择购买合适的GPU是一项非常重要的决策.那么2020年,如何选择 ...
- MongoDB学习笔记01:入门
MongoDB简介 MongoDB是一个开源.高性能.无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种.是最 像关系型数据库(MySQL)的非关系型数据库. ...
- Anno微服务Viper(控制面板) 支持在线部署
1.Anno简介? Anno是一个微服务框架引擎.入门简单.安全.稳定.高可用.全平台可监控.依赖第三方框架少.可在线升级部署. 2.Viper简介 Viper 是一个基于Anno微服务引擎开发的Da ...
- 【Android漏洞复现】StrandHogg漏洞复现及原理分析_Android系统上的维京海盗
文章作者MG1937 CNBLOG博客:ALDYS4 QQ:3496925334 0x00 StrandHogg漏洞详情 StrandHogg漏洞 CVE编号:暂无 [漏洞危害] 近日,Android ...
- 04:CSS(02)
溢出属性 p { height: 100px; width: 50px; border: 3px solid red; /*overflow: visible; !*默认就是可见 溢出还是展示*!*/ ...