if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } }
public <T> voidaddMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { thrownewBindingException("Type " + type + " is already known to the MapperRegistry."); } booleanloadCompleted=false; try { knownMappers.put(type, newMapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilderparser=newMapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
/** * {@link Transaction} that lets the container manage the full lifecycle of the transaction. * Delays connection retrieval until getConnection() is called. * Ignores all commit or rollback requests. * By default, it closes the connection but can be configured not to do it. * * @author Clinton Begin * * @see ManagedTransactionFactory */
/** * Constructs a Spring managed {@code SqlSession} with the given * {@code SqlSessionFactory} and {@code ExecutorType}. * A custom {@code SQLExceptionTranslator} can be provided as an * argument so any {@code PersistenceException} thrown by MyBatis * can be custom translated to a {@code RuntimeException} * The {@code SQLExceptionTranslator} can also be null and thus no * exception translation will be done and MyBatis exceptions will be * thrown * * @param sqlSessionFactory a factory of SqlSession * @param executorType an executor type on session * @param exceptionTranslator a translator of exception */ publicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required");
/** * AOP Alliance MethodInterceptor for declarative transaction * management using the common Spring transaction infrastructure * ({@link org.springframework.transaction.PlatformTransactionManager}/ * {@link org.springframework.transaction.ReactiveTransactionManager}). * * <p>Derives from the {@link TransactionAspectSupport} class which * contains the integration with Spring's underlying transaction API. * TransactionInterceptor simply calls the relevant superclass methods * such as {@link #invokeWithinTransaction} in the correct order. * * <p>TransactionInterceptors are thread-safe. * * @author Rod Johnson * @author Juergen Hoeller * @see TransactionProxyFactoryBean * @see org.springframework.aop.framework.ProxyFactoryBean * @see org.springframework.aop.framework.ProxyFactory */ @SuppressWarnings("serial") publicclassTransactionInterceptorextendsTransactionAspectSupportimplementsMethodInterceptor, Serializable {
/** * Create a new TransactionInterceptor. * <p>Transaction manager and transaction attributes still need to be set. * @see #setTransactionManager * @see #setTransactionAttributes(java.util.Properties) * @see #setTransactionAttributeSource(TransactionAttributeSource) */ publicTransactionInterceptor() { }
@Override @Nullable public Object invoke(MethodInvocation invocation)throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
// If the transaction attribute is null, the method is non-transactional. TransactionAttributeSourcetas= getTransactionAttributeSource(); finalTransactionAttributetxAttr= (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); finalTransactionManagertm= determineTransactionManager(txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfotxInfo= createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); }
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatusstatus= txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
// Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfotxInfo= createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already). if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// Bind the connection holder to the thread. if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } }
TransactionInfotxInfo=newTransactionInfo(tm, txAttr, joinpointIdentification); if (txAttr != null) { // The transaction manager will flag an error if an incompatible tx already exists. txInfo.newTransactionStatus(status); }
// We always bind the TransactionInfo to the thread, even if we didn't create // a new transaction here. This guarantees that the TransactionInfo stack // will be managed correctly even if no transaction was created by this aspect. txInfo.bindToThread(); return txInfo; }
到这里思路就很清晰了,代理为我们做的事情就是生成了一个叫做 TransactionInfo 的东西,里面的 TransactionManager 可以使得 spring 去对最底层的 connection 对象做一些回滚,提交操作。TransactionStatus 则保存挂起的事务的信息,以及当前事务的一些状态,如下图:
// If the transaction attribute is null, the method is non-transactional. TransactionAttributeSourcetas= getTransactionAttributeSource(); finalTransactionAttributetxAttr= (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); finalTransactionManagertm= determineTransactionManager(txAttr); PlatformTransactionManagerptm= asPlatformTransactionManager(tm); finalStringjoinpointIdentification= methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfotxInfo= createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); }
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatusstatus= txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
privatevoidbindToThread() { // Expose current TransactionStatus, preserving any existing TransactionStatus // for restoration after this transaction is complete. this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); }
privatevoidrestoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); }
4、如果需要,则提交当前事务
5、返回切面值
4.5 最后一块拼图,spring 如何与 sqlSession 产生关联:
我们在第三章讲到,mybatis有一个叫做 defualtSqlSessionFactory 的类,负责创建 sqlSession,但是它和 spring 又是怎么产生关联的呢?
[root@CentOS7-Node1 parallels]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 231d40e811cd 2 weeks ago 126MB
创建Pod
直接在master节点上运行一个镜像,并且启动2台机器。
1 2 3 4 5 6 7
[root@CentOS7-Node1 parallels]# kubectl run my-nginx --image=nginx --replicas=2 --port=80 kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead. deployment.apps/my-nginx created [root@CentOS7-Node1 parallels]# kubectl get pod NAME READY STATUS RESTARTS AGE my-nginx-75897978cd-87dnh 1/1 Running 0 4m36s my-nginx-75897978cd-nwnrm 1/1 Running 0 4m36s
// 3rd party plugin repositories can be configured in settings.gradle plugins { id "io.spring.dependency-management" version "1.0.5.RELEASE" apply false id "org.jetbrains.kotlin.jvm" version "1.2.71" apply false id "org.jetbrains.dokka" version "0.9.15" id "org.asciidoctor.convert" version "1.5.8" }
static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResourceresource=newClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { thrownewIllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); } }
if (logger.isDebugEnabled()) { Stringvalue=this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); }
if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } }
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { thrownewApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContextwac= (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protectedvoidinitStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); // 初始化HandlerMapping initHandlerMappings(context); // 初始化HandlerAdapter initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
➜ Desktop git clone https://gitlab.cyblogs.com/root/testdemo.git Cloning into 'cyblogs-blog'... fatal: unable to access 'https://gitlab.cyblogs.com/root/testdemo.git/': The requested URL returned error: 502 ➜ Desktop git clone https://gitlab.cyblogs.com/cyblogs/cyblogs-blog.git Cloning into 'cyblogs-blog'... fatal: unable to access 'https://gitlab.cyblogs.com/cyblogs/cyblogs-blog.git/': The requested URL returned error: 502
secret "kubernetes-dashboard-certs" created serviceaccount "kubernetes-dashboard" created role "kubernetes-dashboard-minimal" created rolebinding "kubernetes-dashboard-minimal" created deployment "kubernetes-dashboard" created service "kubernetes-dashboard" created
服务安装完毕后可以查看部署的容器与服务:
1 2 3 4 5 6 7 8
➜ kubernetes kubectl get deployments --namespace kube-system NAME READY UP-TO-DATE AVAILABLE AGE coredns 2/2 2 2 4d3h kubernetes-dashboard 1/1 1 1 3d8h ➜ kubernetes kubectl get services --namespace kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 4d3h kubernetes-dashboard ClusterIP 10.96.229.197 <none> 443/TCP 4d2h
➜ kubernetes kubectl get secret -n=kube-system NAME TYPE DATA AGE ... default-token-sznp4 kubernetes.io/service-account-token 3 4d3h ... # 这里只列出default-token-sznp4