Spring事务(3)-TransactionInterceptor实际事务执行

news/2024/5/17 3:07:41/文章来源:https://blog.csdn.net/NF_ALONG/article/details/130194479

Spring事务(2)-EnableTransactionManagement实现源码解析 中介绍了Spring事务开启和代理的实现,现在了解实际事务执行TransactionInterceptor

TransactionInterceptor

TransactionInterceptor类图

在这里插入图片描述

  1. MethodInterceptor:AOP代理后方法执行拦截,例如切面中Before、After、AfterReturn、AfterThrow、Around处理实现。
  2. TransactionAspectSupport:事务性方面的基类,获取当前事务、事务状态、PlatformTransactionManager等信息。

TransactionInterceptor#invoke

	/*** 此方法是事务执行的入口*/@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {//计算出目标类型,就是被代理的类,可能是null。TransactionAttributeSource应该通过目标方法和目标类得到Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// 执行事务逻辑//调用父类的invokeWithinTransaction方法实现事务的逻辑//在方法执行成功之后,将会执行回调函数,这里的逻辑就是继续向后执行invocation的proceed方法return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}

TransactionAspectSupport#invokeWithinTransaction

执行流程:

  1. 获取事务属性源,也就是 transactionAttributeSource属性,这个属性源在创建bean定义的时候就被设置了。
  2. 调用属性源的getTransactionAttribute方法获取适用于该方法的事务属性,如果TransactionAttribute为null,则该方法为非事务方法。对于AnnotationTransactionAttributeSourceTransactionAttributeRuleBasedTransactionAttribute
  3. 确定给定事务属性所使用的特定事务管理器
  4. 如果事务属性为null,或者获取事务管理器不是回调事务管理器
  5. 根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象,将会解析各种事务属性,应用不同的流程,比如创建新事物、加入事务、抛出异常等
  6. invocation参数传递的是一个lambda表达式,传递的是一个方法调用invocation::proceed将会继续向后调用链中的下一个拦截器
  7. 处理执行过程中抛出的异常,可能会有事务的回滚或者提交
@Nullableprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {/** TODO 其中事务传播行为的特点(这些特点,仔细分析代码是可以看出来的。一般先了解特点,带着问题再看源码会容易些):*  PROPAGATION_REQUIRED 如果当前存在事务,假如当前事务。如果不存在事务,新建事务。*  PROPAGATION_REQUIRES_NEW 如果当前存在事务,则挂起当前事务,新建一个事务。如果不存在事务,新建一个事务。*  PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前不存在事务,则和PROPAGATION_REQUIRED一样新建事务。使用频率高*      特点:外围事务回滚,嵌套事务全部回滚。嵌套事务回滚,如果在外围中捕获了,则仅仅回滚嵌套事务。*  PROPAGATION_MANDATORY 以事务方式运行,如果当前不存在事务,则抛出异常*  PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常*  PROPAGATION_SUPPORTEDS 支持事务。如果当前存在事务,加入当前事务。如果不存在事务,则以非事务方式运行。*  PROPAGATION_NOT_SUPPORTED 以非事务方式运行。如果当前存在事务,挂起当前事务。*//*** 1 获取事务属性源,也就是transactionAttributeSource属性,这个属性源在创建bean定义的时候就被设置了。*/// 事务属性的获取//基于注解的属性源是AnnotationTransactionAttributeSource//基于XML标签的属性源是NameMatchTransactionAttributeSource// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas = getTransactionAttributeSource();/** 2 调用属性源的getTransactionAttribute方法获取适用于该方法的事务属性,如果TransactionAttribute为null,则该方法为非事务方法。** 对于AnnotationTransactionAttributeSource,它的getTransactionAttribute* 对于NameMatchTransactionAttributeSource,它的getTransactionAttribute就是通过方法名进行匹配。*/final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);/** 3 确定给定事务属性所使用的特定事务管理器。*/final PlatformTransactionManager tm = determineTransactionManager(txAttr);//获取方法的信息,也就是此前设置的DefaultTransactionAttribute中的descriptor,或者方法的全路径类名,主要用于记录日志final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);/** 如果事务属性为null,或者获取事务管理器不是回调事务管理器,那么走下面的逻辑* 这是最常见的标准声明式事务的逻辑,比如DataSourceTransactionManager*/if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {/** 5 根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象* 将会解析各种事务属性,应用不同的流程,比如创建新事物、加入事务、抛出异常等*/TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {/** 6 invocation参数传递的是一个lambda表达式,传递的是一个方法调用invocation::proceed* 因此这里的proceedWithInvocation实际上是执行invocation的proceed方法* 即这里将会继续向后调用链中的下一个拦截器,最后还会导致目标方法的调用(执行业务逻辑),然后会倒序返回*/retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {/** 7 处理执行过程中抛出的异常,可能会有事务的回滚或者提交*/completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// TODO 清除事务信息,将当前事务清除,如果存在旧的事务对象,将旧的事务对象设置为当前持有的事务//  存储在TransactionAspectSupport.transactionInfoHolder中,这是一个静态TrheadLocal常量。cleanupTransactionInfo(txInfo);}// TODO 提交事务commitTransactionAfterReturning(txInfo);return retVal;}/** 如果txAttr不为null并且事务管理器属于CallbackPreferringPlatformTransactionManager,走下面的逻辑* 只有使用WebSphereUowTransactionManager时才会走这个逻辑,不常见,甚至几乎见不到*/else {final ThrowableHolder throwableHolder = new ThrowableHolder();// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}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;}}}

TransactionAspectSupport#createTransactionIfNecessary

判断是否需要创建事务。

	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// 如果未指定事务名称,则使用当前方法的全路径名作为事务的名称。if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// TODO 重点:获取TransactionStatus对象//  无论被代理对象的方法是否需要事务,在通过代理对象调用方法时,总会创建一个TransactionStatus,讲一些信息绑定到//  ThreadLocal的缓存变量中。这是为了在执行完方法的时候,可以回溯到调用方法存在的事务对象。类似责任链。//  如果挂起了存在的事务,则当前方法执行的事务状态对象中会存储SuspendedResourcesHolder,及挂起的事务信息,//  当方法执行完后,将这个挂起从新设置到当前事务中来。status = tm.getTransaction(txAttr);}else {if (logger.isDebugEnabled()) {logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");}}}// TODO 重点:将上面获取到的对象封装成TransactionInfo对象return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}

AbstractPlatformTransactionManager#getTransaction

根据当前事务和事务的传播性判断使用当前事务、创建新事务、挂起事务。

	@Overridepublic final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {/** 获取当前事务,该方法由具体的事务管理器子类自己实现,比如DataSourceTransactionManager、JtaTransactionManager* 一般都使用DataSourceTransactionManager这个事务管理器** 对于DataSourceTransactionManager这里获取的实际上是一个数据库的事务连接对象,即DataSourceTransactionObject*/Object transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}// 判断是否已经存在事务。如果当前事务对象已经持有了链接,并且事务是活跃状态,则表示已经存在事务。// 如果当前已经存在事务,按照已存在事务的方式判断传播行为,进行处理if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.// TODO 如果当前已存在事务,会走到此方法逻辑进行处理。return handleExistingTransaction(definition, transaction, debugEnabled);}// TODO 以下是针对前一个方法未开启事务的情况的处理。// 检测事务的超时时间设置是否正确// Check definition settings for new transaction.if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// 如果传播属性是PROPAGATION_MANDATORY,且当前不存在事务,则抛出异常// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 如果传播属性是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED时,会走到这里else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 因为以上三种事务传播属性的特性,这里会挂起(如果当前存在事务)SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 创建事务状态对象DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// TODO 重点:开启事务。按照当前测试代码注册的事务管理器,使用的是DataSourceTransactionManagerdoBegin(transaction, definition);// 如果是新开启的事务,设置事务管理器相关属性。其实走到这里的代码,一定是新启的事务。// 因为前面已经判断了,如果存在事务,就走handleExistingTransaction方法prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// 以非事务方式运行 NEVER SUPPORTS NOT_SUPPORTED,在外围未开启事务时,会走这里。// Create "empty" transaction: no actual transaction, but potentially synchronization.if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + definition);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}/*** 为现有事务创建一个TransactionStatus* Create a TransactionStatus for an existing transaction.*/private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {// 如果当前方法传播属性为从不开启事务,直接抛出异常。// PROPAGATION_NEVER特性决定,如果当前存在事务,则抛出异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}// PROPAGATION_NOT_SUPPORTED特性:如果当前存在事务,挂起当前事务,以非事务方式运行if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}// 挂起事务Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 返回事务状态对象return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}// TODO 重点:PROPAGATION_REQUIRES_NEW特性:无论当前是否存在事务,都新建一个事务。//  如果存在事务,将会把当前事务挂起,被挂起的事务会存储到新的事务状态对象中。if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {if (debugEnabled) {logger.debug("Suspending current transaction, creating new transaction with name [" +definition.getName() + "]");}// 挂起当前事务,由于PROPAGATION_REQUIRES_NEW的特性,需要使用新的事务,所以要将当前事务挂起,当新的事务执行完毕时,会恢复这个挂起的事务。SuspendedResourcesHolder suspendedResources = suspend(transaction);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// TODO 可以看到,这里创建事务对象时,构造函数中的参数newTransaction为true。DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// PROPAGATION_REQUIRES_NEW表示总是新启一个事务,这里会新开启事务。doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}}// TODO PROPAGATION_NESTED嵌套事务,如果存在事务,则在嵌套事务中运行。如果不存在事务,则新建一个事务。//  嵌套事务是用savePoint来实现的。具体过程://  1.新建事务状态对象时,会设置一个保存点(或者叫回滚点)。//  2.如果业务方法执行抛出异常,则会被事务切面捕获,如果存在保存点,则会回滚值保存点。//  3.正常执行完业务方法,如果这个方法对应的事务状态对象中具有保存点,则会擦除这个保存点。if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {if (!isNestedTransactionAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +"specify 'nestedTransactionAllowed' property with value 'true'");}if (debugEnabled) {logger.debug("Creating nested transaction with name [" + definition.getName() + "]");}// 是否使用保存点,在测试中用到的事务管理器来看,总是返回trueif (useSavepointForNestedTransaction()) {// Create savepoint within existing Spring-managed transaction,// through the SavepointManager API implemented by TransactionStatus.// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);// 创建savePoint,保存点(或者叫回滚点)status.createAndHoldSavepoint();return status;}else {// Nested transaction through nested begin and commit/rollback calls.// Usually only for JTA: Spring synchronization might get activated here// in case of a pre-existing JTA transaction.boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.if (debugEnabled) {logger.debug("Participating in existing transaction");}// 这里是一些校验代码if (isValidateExistingTransaction()) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {Constants isoConstants = DefaultTransactionDefinition.constants;throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] specifies isolation level which is incompatible with existing transaction: " +(currentIsolationLevel != null ?isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :"(unknown)"));}}if (!definition.isReadOnly()) {if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] is not marked as read-only but existing transaction is");}}}boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 创建事务状态对象,如果是新创建的事务,则设置事务管理器的相关属性。return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);}

TransactionAspectSupport#prepareTransactionInfo

根据TransactionAttribute属性和状态对象准备TransactionInfo

	protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {// 创建事务信息对象,如果方法是具有事务属性的,将事务状态对象封装到事务信息对象中。TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);if (txAttr != null) {if (logger.isTraceEnabled()) {logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");}//那么设置事务状态,这里就表示为当前方法创建了一个事务txInfo.newTransactionStatus(status);}else {if (logger.isTraceEnabled()) {logger.trace("Don't need to create transaction for [" + joinpointIdentification +"]: This method isn't transactional.");}}// 将事务信息对象绑定到当前线程。这时会将旧的事务信息对象值,暂存到另一个变量 oldTransactionInfo 中。txInfo.bindToThread();return txInfo;}

TransactionAspectSupport#completeTransactionAfterThrowing

出现异常时,事务处理方法。

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {// 如果存在事务状态对象if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}// 判断事务属性不为空并且满足回滚规则,就进行回滚,否则进行事务提交if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {// TODO 重点:具体的回滚代码txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}else {// We don't roll back on this exception.// Will still roll back if TransactionStatus.isRollbackOnly() is true.try {// 如果抛出的异常,与回滚的异常定义不匹配,则提交事务。也就是说,抛出异常了,也不一定回滚。它存在一个异常类型匹配。txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by commit exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}}}}/*** RuleBasedTransactionAttribute#rollbackOn 方法** 采用"Winning rule"机制来判断对当前异常是否需要进行回滚* 这是最简单的规则,即选择异常的继承层次结构中最接近当前异常的规则。* 如果需要回滚,则返回true,否则返回false** @see TransactionAttribute#rollbackOn(java.lang.Throwable)*/@Overridepublic boolean rollbackOn(Throwable ex) {if (logger.isTraceEnabled()) {logger.trace("Applying rules to determine whether transaction should rollback on " + ex);}//rollbackRules中最匹配的回滚规则,默认为nullRollbackRuleAttribute winner = null;//回滚规则匹配成功时的匹配异常栈深度,用来查找最匹配的那一个回滚规则int deepest = Integer.MAX_VALUE;//如果rollbackRules回滚规则集合不为null,那么判断回滚规则是否匹配if (this.rollbackRules != null) {for (RollbackRuleAttribute rule : this.rollbackRules) {// 递归调用异常的父类,如果异常名称中包含规则中封装的异常名称,则返回。int depth = rule.getDepth(ex);// 如果不满足规则,会返回-1,如果满足了规则,会把这个规则赋值到winner临时变量中。if (depth >= 0 && depth < deepest) {//那么deepest赋值为当前异常栈深度,即找到最匹配的那一个deepest = depth;//winner设置为当前回滚规则实例winner = rule;}}}if (logger.isTraceEnabled()) {logger.trace("Winning rollback rule is: " + winner);}// 如果没有配置回滚异常或不回滚异常,则会走默认的。默认回滚运行时异常及Error:// (ex instanceof RuntimeException || ex instanceof Error);// User superclass behavior (rollback on unchecked) if no rule matches.if (winner == null) {logger.trace("No relevant rollback rule found: applying default rules");return super.rollbackOn(ex);}// 判断规则如果是不回滚规则,则返回false,表示不需要回滚。// 判断规则如果不是不回滚规则,返回true,表示需要回滚return !(winner instanceof NoRollbackRuleAttribute);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_289555.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA社区版搭建Tomcat服务器并创建web项目

IDEA社区版搭建Tomcat服务器并创建web项目 目标 创建Web项目的目录结构可以启动Tomcat服务器编写Servlet并访问成功 问题 IDEA社区版没有创建Web工程的选项IDEA社区版没有Tomcat插件 实现步骤 针对以上两个问题&#xff0c;分步解决 问题一&#xff1a;IDEA社区版没有创建…

深入认识VirtualPrivateNetwork

目录 一、认识什么是认证&#xff1f; 1.什么是数据认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段? 2.什么是身份认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段? 二、认识什么是VPN 1.什么VPN技术? 2.VPN技术有哪些分类? 3.IPSEC技术…

我的Qt作品(18)模仿Qt Creator IDE写了一个轻量级的视觉框架

Qt Creator的源码比较庞大。前几年我陆陆续续读过里面的源码。也写了几篇博文&#xff1a; https://blog.csdn.net/libaineu2004/article/details/104728857 https://blog.csdn.net/libaineu2004/article/details/89407333 最近一直想找机会&#xff0c;借用这个IDE的皮&…

mapreduce基础: 手写wordcount案例

文章目录 一、源代码二、运行截图 一、源代码 WordCountMapper类 package org.example.wordcount;import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper;impo…

DNS服务器配置与使用【CentOS】

从本质上说&#xff0c;DNS是一个分布数据库&#xff0c;是一个树形结构&#xff08;不是网状&#xff09;——层次结构 DNS查找过程就是 回溯的过程&#xff08;递归、迭代&#xff09; www.xxx.edu.cn&#xff08;属于四层结构&#xff09; 查询DNS&#xff1a;域名到IP地址的…

【Maven 入门】第二章、Maven核心程序解压与配置

一、Maven 官网地址 首页&#xff1a; Maven – Welcome to Apache Maven(opens new window) 下载页面&#xff1a; Maven – Download Apache Maven(opens new window) 本文以maven-3.3.8为例 具体下载地址&#xff1a;https://dlcdn.apache.org/maven/maven-3/3.8.8/bina…

Linux学习记录—— 이십일 进程间通信(3)信号量和消息队列

文章目录 1、消息队列2、信号量1、了解概念2、信号量理解 3、接口4、理解IPC 1、消息队列 两个进程ab之间系统维护一个队列结构&#xff0c;a进程往队列里放信息&#xff0c;信息编号为1&#xff0c;b进程往队列里放信息&#xff0c;信息编号为2&#xff1b;之后开始读取数据的…

OrCAD原理图检查

OrCAD原理图检查 FPGA或处理器芯片原理图封装检查OrCad元件Part Reference与Reference位号不同检查所有器件是否与CIS库元件匹配用CIS库中的元器件替换已存在器件方法1方法2 DRC检查修改页码Annotate重排位号利用Intersheet References功能进行off-page索引检查封装、厂家、型号…

追梦之旅【数据结构篇】——看看小白试如何利用C语言“痛”撕堆排序

追梦之旅【数据结构篇】——看看小白试如何利用C语言“痛”撕堆排序 ~&#x1f60e; 前言&#x1f64c;堆的应用 —— 堆排序算法&#xff1a;堆排序算法源代码分享运行结果测试截图&#xff1a; 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60…

安装配置 JupyterLab ubuntu20.04

目录 ​编辑 &#xff08;1&#xff09;安装 &#xff08;2&#xff09;配置 &#xff08;1&#xff09;生成配置文件 &#xff08;2&#xff09;生成jupyterlab的登录密码 &#xff08;3&#xff09;修改 jupyter 的配置文件 &#xff08;4&#xff09;安装 jupyterlab…

leetcode每日一题——美团笔试题【1】

今天分享两道算法题&#xff0c;自己刚开始练习&#xff0c;可能在解法上不是最佳的&#xff0c;但是只提供一些自己的思路&#xff0c;欢迎大家多多指教~ 第一题 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a;输入: s "lee…

算法的时间复杂度和空间复杂度(2)

计算斐波那契递归Fib的时间复杂度&#xff1f; long long Fib(size_t N) { if(N < 3) return 1; return Fib(N-1) Fib(N-2); } 因为递归先递推后回归&#xff0c;看起来规律像等比数列&#xff0c;也可以用错位相减法&#xff0c;因为斐波那契数列到第二项就不会再计算了&a…

【Spring Boot】SpringBoot设计了哪些可拓展的机制?

文章目录 前言SpringBoot核心源码拓展Initializer拓展监听器ApplicationListenerBeanFactory的后置处理器 & Bean的后置处理器AOP其他的拓展点 前言 当我们引入注册中心的依赖&#xff0c;比如nacos的时候&#xff0c;当我们启动springboot&#xff0c;这个服务就会根据配置…

2023/4/20总结

项目 网上关于listview的资料太少了&#xff0c;在网上的那些资料里面&#xff0c;了解到以下这些。 如果希望listview后期能更改或者更新&#xff0c;那么需要使用到 ObservableList 它可以观察到&#xff0c;listview的改动。 需要特别注意一点的是&#xff1a;写俩者的…

第 三 章 UML 类图

文章目录 前言一、依赖关系&#xff08;虚线箭头&#xff09;二、泛化关系&#xff1a;继承&#xff08;实线空心箭头&#xff09;三、实现关系&#xff08;虚线空心箭头&#xff09;四、关联关系&#xff08;一对一为实线箭头&#xff0c;一对多为实线&#xff09;五、聚合关系…

java贸易企业工作信息管理与利润返现系统sxA5进销存程序

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1 课题背景 1 1.2 研究现状 1 本章小结 1 第2章 可行性分析 2 2.1 经济可行性 2 2.2 技术可行性 2 2.3 操作可行性 2 2.4 业务流程分析 3 本章小结 3 第3章 需求分析 4 3.1 需求分析 4 …

Nuxt.js - 超详细实现路由 “伪静态“,将浏览器网页路径 URL 链接后面加上 .html 后缀名称(可以自定义任何结尾后缀名称)详细示例教程

前言 正常的项目,路由都是 /index | /user/add 这种,但有一个办法可以让其后面带上 .html,比如:/index.html。 本文 在 Nuxt.js 项目中,描述了如何实现伪静态详细教程,让页面路由后面都跟上一段自定义后缀名,比如 .html / .asp, 你可以按照本文的教程,最终得到伪静态…

react中如何系统化的处理时间操作?

在 Web 开发中&#xff0c;我们经常需要处理日期和时间的格式化。 在 React 中&#xff0c;这个过程变得更加容易和直观&#xff0c;因为我们可以使用一个叫做 moment 的 npm 包来帮助我们完成这个任务。 什么是 Moment? Moment.js是一个JavaScript库&#xff0c;用于处理日…

Vue2组件通信专题

组件通信专题 一、vue2中常用的6中组件通信方式 1. props 适用于的场景&#xff1a;父子组件通信 注意事项&#xff1a; 如果父组件给子组件传递数据&#xff08;函数&#xff09;&#xff1a;本质其实是子组件给父组件传递数据。 如果父组件给子组件传递数据&#xff08…

水质站房式在线监测系统集方案要点

水质在线自动监测系统是一套高度集成的一体化水质自动监测系统&#xff0c;其中包含水样采集处理、水质自动分析、数据采集传输、远程操作监控于一体的在线全自动监控系统。 本次方案整体系统采用一体化集成方式&#xff0c;辅助设备工艺制作精细&#xff0c;同时系统工艺流程…