当前位置: 首页 > news >正文

spring事物源码分析

今天的任务是剖析源码,看看Spring 是怎么运行事务的,并且是基于当前最流行的SpringBoot。还有,我们之前剖析Mybatis 的时候,也知道,Mybatis 也有事务,那么,他俩融合之后,事务是交给谁的?又是怎么切换的?今天这几个问题,我们都要从源码中找到答案。

1. Spring 的事务如何运行?
如果各位使用过SpringBoot ,那么就一定知道如何在Spring中使用注解,比如在一个类或者一个方法上使用 @Transactional 注解,在一个配置类上加入一个 @EnableTransactionManagement 注解代表启动事务。而这个配置类需要实现 TransactionManagementConfigurer 事务管理器配置接口。并实现 annotationDrivenTransactionManager 方法返回一个包含了 配置好数据源的 DataSourceTransactionManager 事务对象。这样就完成了事务配置,就可以在Spring使用事务的回滚或者提交功能了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个 saveList 方法就在Spring事务的控制之下,如果发生了异常,就会回滚事务。如果各位知道更多的Spring的事务特性,可以在注解中配置,比如什么异常才能回滚,比如超时时间,比如隔离级别,比如事务的传播。就更有利于理解今天的文章了。

我们基于一个 Junit 测试用例,来看看Spring的事务时如何运行的。
在这里插入图片描述
在测试用例中执行该方法,参数时一个空的List,这个Sql的运行肯定是失败的。我们主要看看他的运行过程。我们讲断点打在该方法上。断点进入该方法。

在这里插入图片描述
注意,dataCollectionShareService 对象已经被 Cglib 代理了,那么他肯定会走 DynamicAdvisedInterceptor 的 intercept 方法,我们断点进入该方法查看,这个方法我们已经很属性了,该方法中,最重要的事情就是执行通知器或者拦截器的方法,那么,该代理有通知器吗?
在这里插入图片描述
有一个通知器。是什么呢?
在这里插入图片描述
一个事务拦截器,也就是说,如果通知器链不为空,就会依次执行通知器链的方法。那么 TransactionInterceptor 到底是什么呢?
在这里插入图片描述
该类实现了通知器接口,也实现类 MethodInterceptor 接口,并实现了该接口的 invoke 方法,在 DynamicAdvisedInterceptor 的 intercept 方法中,最终会调用每个 MethodInterceptor 的 invoke 方法,那么,TransactionInterceptor 的 invoke 方法是如何实现的呢?
在这里插入图片描述
invoke 方法中会调用自身的 invokeWithinTransaction 方法,看名字,该方法和事务相关。该方法参数是由目标方法,目标类,一个回调对象构成。 那么我们就进入该方法查看,该方法很长:

/*** General delegate for around-advice-based subclasses, delegating to several other template* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}* as well as regular {@link PlatformTransactionManager} implementations.* @param method the Method being invoked* @param targetClass the target class that we're invoking the method on* @param invocation the callback to use for proceeding with the target invocation* @return the return value of the method, if any* @throws Throwable propagated from the target invocation*/protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// If the transaction attribute is null, the method is non-transactional.final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;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 exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}else {// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus 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.return new ThrowableHolder(ex);}}finally {cleanupTransactionInfo(txInfo);}}});// Check result: It might indicate a Throwable to rethrow.if (result instanceof ThrowableHolder) {throw ((ThrowableHolder) result).getThrowable();}else {return result;}}catch (ThrowableHolderException ex) {throw ex.getCause();}}}

该方法主要逻辑:

  • 获取事务属性,根据事务属性,获取事务管理器
  • 判断属性是否空,或者事务管理器是否不是 CallbackPreferringPlatformTransactionManager 类型,如果是该类型,则会执行事务管理器的 execute 方法。
  • 生成一个封装了事务管理器,事务属性,方法签名字符串,事务状态对象 的 TransactionInfo 事务信息对象。该对象会在事务回滚或者失败时起作用。
  • 调用目标对象方法或者是下一个过滤器的方法。
  • 如果方法由异常则执行 completeTransactionAfterThrowing 方法,调用事务管理器的回滚方法。如果没有异常,调用 commitTransactionAfterReturning 提交方法。最后返回返回值。

可以说,该方法就是Spring 事务的核心调用,根据目标方法是否有异常进行事务的回滚。

那么,我们需要一行一行的看看该方法实现。

首先看事务的属性。

2. TransactionAttribute 事务属性
invokeWithinTransaction 方法中调用了 自身的 getTransactionAttributeSource 方法返回一个TransactionAttributeSource 对象,并调用该对象的 getTransactionAttribute 方法,参数是目标方法和目标类对象。首先看 getTransactionAttributeSource 方法,该方法直接返回了抽象类 TransactionAspectSupport 中定义的 TransactionAttributeSource 属性。该属性的是什么时候生成的我们稍后再说。我们debug 后返回的是 TransactionAttributeSource 接口的实现类 AnnotationTransactionAttributeSource ,看名字,注解事务属性资源,名字起的好很重要啊。我们进入该类查看。
在这里插入图片描述
这是该类的继承机构图。我们重点还是关注该类的 getTransactionAttribute 方法,该方法有抽象类 AbstractFallbackTransactionAttributeSource 也就是 AnnotationTransactionAttributeSource 的父类完成。我们看看该方法。
在这里插入图片描述
该方法大部分都是缓存判断,最重要的一行代码楼主已红框标出。computeTransactionAttribute 方法,计算事务属性。进入该方法查看:
在这里插入图片描述
该方法是返回事务属性的核心方法,首先,根据 class 和 method 对象,生成一个完整的method 对象,然后调用 findTransactionAttribute 方法,参数就是该 method 对象,findTransactionAttribute 方法是抽象方法,由子类实现,可见 computeTransactionAttribute 是个模板方法模式。那么我们就看看他的子类 AnnotationTransactionAttributeSource 是如何实现的。该方法调用了自身的 determineTransactionAttribute 方法。该方法实现入下:
在这里插入图片描述
该方法会判断该 Method 对象是否含有注解。并循环 AnnotationTransactionAttributeSource 对象的 annotationParsers 注解解析器集合,对该方法进行解析。如果解析成功,则返回该注解元素。我想我们也已经猜到了,这个注解解析器解析的就是 @Transactional 注解。

3. @Transactional 注解解析器 SpringTransactionAnnotationParser

我们说AnnotationTransactionAttributeSource 对象中又多个解析器。那么这些解析器是什么时候生成的呢?构造方法中生成的。
在这里插入图片描述
该构造方法由一个布尔属性,然后创建一个链表,也创建一个 SpringTransactionAnnotationParser 对象添加进链表中。这样就完成了解析器的创建。构造方法什么时候调用的呢?我们稍后再讲。

我们看看注解解析器是怎么解析方法对象的。
在这里插入图片描述
首先根据指定的 Transactional 注解和给定的方法,调用工具方法 getMergedAnnotationAttributes ,获取方法上的注解属性。然后调用重载方法 parseTransactionAnnotation 。
在这里插入图片描述
可以看到,该方法首先创建了一个 RuleBasedTransactionAttribute 对象,然后一个个解析注解中的元素,并将这些元素设置到 RuleBasedTransactionAttribute 对象中,注意,其中有个 RollbackRuleAttribute 的集合,存储着该注解属性的回滚相关的属性。最后添加到 RuleBasedTransactionAttribute 的RollbackRules 集合中。

到这里,就完成了解析器的解析。返回了一个 RuleBasedTransactionAttribute 对象。

回到 拦截器的 invokeWithinTransaction 方法中,此时已经获取了 属性对象。根据方法,也就是说,如果返回值是null,说明该方法没有事务注解,在 getTransactionAttribute 方法中,也会将该方法作为key ,NULL_TRANSACTION_ATTRIBUTE 作为 value,放入缓存,如果不为null,那么就将 TransactionAttribute 作为 value 放入缓存。

有了事务属性,再获取事务管理器。也就是 determineTransactionManager 方法。

4. 事务管理器。

在这里插入图片描述
我们注意到,调用了自身的 determineTransactionManager 方法,返回了一个 PlatformTransactionManager 事务管理器。这个事务管理器就是我们在我们的配置类中写的:
在这里插入图片描述
那么这个事务管理器是什么呢?事务管理器就是真正执行事务回滚或提交的执行单位,我们看看该类:

继承图:
在这里插入图片描述
结构图:
在这里插入图片描述
红框标注的方法就是执行正在事务逻辑的方法,其中又封装了数据源,也就是 JDBC 的 Connection 。比如 doCommit 方法:
在这里插入图片描述
我们看看determineTransactionManager 是如何获取事务管理器的。
在这里插入图片描述
该方法步骤入下:

  1. 如果事务属性为null 或者 容器工厂为null,则返会自身的 transactionManager 事务管理器。
  2. 如果都不为null,则获取事务属性的限定符号,根据限定符从容器中获取 事务管理器。
  3. 如果没有限定符,则根据事务管理器的BeanName从容器中获取。
  4. 如果都没有,则获取自身的事务管理器,如果自身还没有,则从缓存中取出默认的。如果默认的还没有,则从容器中获取PlatformTransactionManager 类型的事务管理器,最后返回。

这里重点是自身的事务管理器从何而来?我们先按下不表。

到这里,我们已经有了事务管理器。就需要执行 invokeWithinTransaction 下面的逻辑了。回到 invokeWithinTransaction 方法,我们的返回值肯定满足第一个if 条件,因为我们的事务管理器不是 CallbackPreferringPlatformTransactionManager 类型的。进入if 块。
在这里插入图片描述
首先创建一个事务信息对象。该类是什么呢?

属性:
在这里插入图片描述
构造方法:
在这里插入图片描述
该类包含了一个 事务管理器,事务属性,事务方法字符串。

接着执行回调类InvocationCallback 的 proceedWithInvocation 方法,该方法会执行下一个通知器的拦截方法(如果有的话),最后执行目标方法,这里,目标方法被 try 住了,如果发生异常,则执行completeTransactionAfterThrowing 方法,并抛出异常,在 finally 块中执行清理工作。如果成功执行,则执行
commitTransactionAfterReturning 方法。最后返回目标方法返回值。

我们重点看看 completeTransactionAfterThrowing 方法和 commitTransactionAfterReturning 方法。

5. TransactionInterceptor 的 completeTransactionAfterThrowing 方法(事务如何回滚)。

在这里插入图片描述
该方法主要内容在红框中,首先判断该事务对象是否和该异常匹配,如果匹配,则回滚,否则,则提交。那么,是否匹配的逻辑是怎么样的呢?我们的事务属性是什么类型的?RuleBasedTransactionAttribute ,就是我们刚刚创建解析注解后创建的。那么我就看看该类的 rollbackOn 方法:
在这里插入图片描述
首先,循环解析注解时添加进集合的回滚元素。并递归调用RollbackRuleAttribute 的 getDepth 方法,如果这个异常的名字和注解中的异常名字匹配,则返回该异常的回滚类型。最后判断,如果没有匹配到,则调用父类的 rollbackOn 方法,如果匹配到了,并且该属性类型不是 NoRollbackRuleAttribute 类型,返回true。表示匹配到了,可以回滚。那么父类的 rollbackOn 方法肯定就是默认的回滚方法了。

这是父类的 rollbackOn 方法:
在这里插入图片描述
该方法判断,该异常如果是 RuntimeException 类型异常或者 是 Error 类型的,就回滚。这就是默认的回滚策略。
我们的方法肯定是匹配的 RuntimeException 异常,就会执行下面的方法。
在这里插入图片描述
可以看到,这行代码就是执行了我们的事务管理器的 rollback 方法,并且携带了事务状态对象。该方法实现在抽象类 AbstractPlatformTransactionManager 中,调用了自身的 processRollback 方法做真正的实现。

在这里插入图片描述
该方法首先切换事务状态,其实就是关闭SqlSession。
在这里插入图片描述
然后调用 doRollback 方法。
在这里插入图片描述

首先,从状态对象中获取数据库连接持有对象,然后获取数据库连接,调用 Connection 的 rollback 方法,也就是我们学习JDBC 时使用的方法。最后修改事务的状态。

到这里,事务的回滚就结束了。

那么,事务时如何提交的呢?

6. TransactionInterceptor 的 commitTransactionAfterReturning 方法(事务如何提交)。
在这里插入图片描述

该方法简单的调用了事务管理器的 commit 方法。

AbstractPlatformTransactionManager 的 commit 方法。

在这里插入图片描述

首先判断了事务的状态,如果状态不匹配,则调用回滚方法。如果状态正常,执行 processCommit 方法。该方法很长,楼主只截取其中一段:
在这里插入图片描述

首先,commit 之前做一些状态切换工作。最重要的是执行 doCommit 方法,如果异常了,则回滚。那么 DataSourceTransactionManager 的 doCommit 是如何执行的呢?
在这里插入图片描述

可以看到,底层也是调用 JDBC 的 Connection 的 commit 方法。

到这里,我们就完成了数据库的提交。

7. 事务运行之前做了哪些工作?
从前面的分析,我们已经知道了事务是如何运行的,如何回滚的,又是如何提交的。在这是交互型的框架里,事务系统肯定做了很多的准备工作,同时,我们留下了很多的疑问,比如事务管理器从何而来? TransactionAttributeSource 属性何时生成?AnnotationTransactionAttributeSource 构造什么时候调用?

我们一个个的来解释。

在Spring 中,有一个现成的类,ProxyTransactionManagementConfiguration,我们看看该类:
在这里插入图片描述

看到这个类,应该可以解开我们的疑惑,这个类标注了配置注解,会在IOC的时候实例化该类,而该类中产生了几个Bean,比如事务拦截器 TransactionInterceptor,创建了 AnnotationTransactionAttributeSource 对象,并向事务拦截器添加了事务管理器。最后,将事务拦截器封装成通知器。那么,剩下最后一个问题就是,事务管理器从何而来?答案是他的父类 AbstractTransactionManagementConfiguration :

在这里插入图片描述

该类也是个配置类,自动注入了 TransactionManagementConfigurer 的配置集合,而并且寻找了配置 EnableTransactionManagement 注解的类,而我们在我们的项目中就是按照这个标准来实现的:
在这里插入图片描述

我们关联这两个类就能一目了然,Spring在启动的时候,会加载这两个配置类,在对 AbstractTransactionManagementConfiguration 的 setConfigurers 方法进行注入的时候,会从容器中找到对应类型的配置,并调用配置类的 annotationDrivenTransactionManager 方法,也就是我们实现的方法,获取到我们创建的 DataSourceTransactionManager 类。这样,我们的事务拦截器相关的类就完成了在Spring中的依赖关系。

但是,这个时候Spring中的事务运行还没有搭建完成。比如什么时候创建类的代理?根据什么创建代理,因为我们知道,Spring 中的事务就是使用AOP来完成的,必须使用动态代理或者 Cglib 代理来对目标方法进行拦截。

这就要复习我们之前的Spring IOC 的启动过程了。Spring 在创建bean的时候,会对每个Bean 的所有方法进行遍历,如果该方法匹配系统中任何一个拦截器的切点,就创建一个该Bean的代理对象。并且会将对应的通知器放入到代理类中。以便在执行代理方法的时候进行拦截。

具体代码步骤楼主贴一下:

在对bean 进行初始化的时候会执行 AutowireCapableBeanFactory 接口的 applyBeanPostProcessorsAfterInitialization 的方法,其中会遍历容器中所有的bean后置处理器,后置处理器会调用 postProcessAfterInitialization 方法对bean进行处理。
在这里插入图片描述

在处理过程中,对bean 进行包装,也就是代理的创建,调用 getAdvicesAndAdvisorsForBean 方法,该方法会根据bean的信息获取到对应的拦截器并创建代理,创建代理的过程我们之前已经分析过了,不再赘述。
在这里插入图片描述

寻找匹配拦截器过程:首先找到所有的拦截器,然后,根据bean的信息进行匹配。
在这里插入图片描述

匹配的过程就是,找到目标类的所有方法,遍历,并调用拦截器的方法匹配器对每个方法进行匹配。方法匹配器就是事务拦截器中的 BeanFactoryTransactionAttributeSourceAdvisor 类,该类封装了 AnnotationTransactionAttributeSource 用于匹配事务注解的匹配器。
在这里插入图片描述
在这里插入图片描述

最终调用方法匹配器中封装的注解解析器解析方法,判断方法是否含有事务注解从而决定是否生成代理:
在这里插入图片描述

到这里,就完成了所有事务代理对象的创建。

项目中的每个Bean都有了代理对象,在执行目标方法的时候,代理类会查看目标方法是否匹配代理中拦截器的方法匹配器中定义的切点。如果匹配,则执行拦截器的拦截方法,否则,直接执行目标方法。这就是含有事务注解和不含有事务注解方法的执行区别。

到这里,我们还剩下最后一个问题,我们知道,在分析mybatis 的时候,mybatis 也有自己的事务管理器,那么他们融合之后,他们的事务管理权在谁的手上,又是根据什么切换的呢?

8. mybatis 和 Spring 的事务管理权力之争

我们之前说过,在Spring中,mybatis 有 SqlSessionTemplate 代理执行,其实现类动态代理的 InvocationHandler 方法,那么最重要的方法就是 invoke 方法,其实这个方法我们已经看过了,今天再看一遍:
在这里插入图片描述

我们今天重点关注是否提交(报错肯定回滚),其中红框标出来的 if 判断,就是判断这个事务到底是Spring 来提交,还是 mybatis 来提交,那么我们看看这个方法 isSqlSessionTransactional :
在这里插入图片描述
该方法从Spring 的容器中取出持有 SqlSession 的 持有类,判断Spirng 持有的 SqlSession 和 Mybatis 持有的是否是同一个,如果是,则交给Spring,否则,Mybatis 自己处理。可以说很合理。

总结

今天的这篇文章可以说非常的长,我们分析了 SpringBoot 的事务运行过程,事务环境的搭建过程,mybatis 的事务和 Spring 事务如何协作。知道了整个事务其实是建立在AOP的基础之上,其核心类就是 TransactionInterceptor,该类就是 invokeWithinTransaction 方法是就事务处理的核心方法,其中封装了我们创建的 DataSourceTransactionManager 对象,该对象就是执行回滚或者提交的执行单位 其实,TransactionInterceptor 和我们平时标注 @Aspect 注解的类的作用相同,就是拦截指定的方法,而在
TransactionInterceptor 中是通过是否标有事务注解来决定的。如果一个类中任意方法含有事务注解,那么这个方法就会被代理。而Mybatis 的事务和Spring 的事务协作则根据他们的SqlSession 是否是同一个SqlSession 来决定的,如果是同一个,则交给Spring,如果不是,Mybatis 则自己处理。

通过阅读源码,我们已经弄清楚了SpirngBoot 整个事务的运行过程。实际上,Spring 的其他版本也大同小异。底层都是 TransactionInterceptor ,只不过入口不一样。我相信,在以后的工作中,如果遇到了Spring事务相关的问题,再也不会感到无助了,因为知道了原理,可以深入到源码中查看。

 原文:spring 事务源码分析_spring事物源码分析_洋洋yang羊的博客-CSDN博客

相关文章:

spring事物源码分析

今天的任务是剖析源码&#xff0c;看看Spring 是怎么运行事务的&#xff0c;并且是基于当前最流行的SpringBoot。还有&#xff0c;我们之前剖析Mybatis 的时候&#xff0c;也知道&#xff0c;Mybatis 也有事务&#xff0c;那么&#xff0c;他俩融合之后&#xff0c;事务是交给谁…...

炫龙游戏本Win10系统总是蓝屏崩溃怎么办?

炫龙游戏本Win10系统总是蓝屏崩溃怎么办&#xff1f;有用户使用的炫龙游戏本最近总是在运行的过程中出现自动蓝屏的情况&#xff0c;有的时候自己还在操作电脑&#xff0c;而屏幕却蓝屏了&#xff0c;导致自己的工作被中断了。那么这个情况要怎么去进行修复呢&#xff1f;来看看…...

华为OD机试题,用 Java 解【数字加减游戏】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…...

C++ 手写一个高性能json生成与解析器

文章目录 前言一、了解json格式二、统一数据类型:TJson三、解析json数据四、输出json数据五、实现便利的修改与访问六、性能优化七、源码下载与解析前言 由于C++标准库中并不存在解析json数据库,但json格式又非常的常见 如今绝大部分网络数据传输都采用的json数据传输格式,…...

java——了解反射

目录 什么是反射&#xff1f; 反射如何获取类信息&#xff1f; 小结&#xff1a; 什么是反射&#xff1f; 反射是用代码分析类信息的能力 类中有哪些信息&#xff1a;方法、对象、构造器、全局变量、父类、接口等.... 反射如何获取类信息&#xff1f; 三种方式 1.通过对象…...

The Sandbox 中的独特体验——《奥米加》

在过去几年间&#xff0c;The Sandbox 游戏变得越来越受欢迎。因为我们为玩家提供了在虚拟世界中探索、创造和游戏的自由&#xff0c;没有线性游戏的限制。DeQuest 工作室创作的《奥米加》也正是如此&#xff0c;绝对是一个前所未有的体验&#xff01; 先了解一下《奥米加》的故…...

76 Python写入csv文件时出现空行_newline参数解决

76 Python写入csv文件时出现空行_newline参数解决 文章目录76 Python写入csv文件时出现空行_newline参数解决1. 准备工作2. with open 语句没有newline参数3. with open 语句有newline参数4. 总结1. 准备工作 在电脑D盘新建一个【76】文件夹。 用VScode编辑器打开【76】文件夹…...

高等数学——定积分和不定积分

文章目录不定积分概念几何意义性质不定积分的基本公式三种主要积分法三类常见可积函数积分定积分概念几何意义性质积分上限的函数定积分的计算几何应用反常积分无穷区间的反常积分无界函数的反常积分不定积分 不定积分是导数的逆运算。 概念 原函数&#xff1a;设f(x)f(x)f(…...

imx6 usb增强信号强度

USB信号 参考&#xff1a;官方文档 USB信号完整性取决于许多因素&#xff0c;如电路设计、PCB布局、堆叠和阻抗。每个产品可能彼此不同&#xff0c;因此客户需要微调参数&#xff0c;以获得最佳的信号质量。 测试板已经路由出两个USB端口:一个OTG1&#xff0c;一个主机。每个端…...

深入理解性能压测工具原理

如果没有性能测试工具如何通过手工实现 如果没有性能测试工具&#xff0c;通过手工进行性能测试&#xff0c;这是一个值得我们思考的问题。这时候需要一个协调员发送指令&#xff0c;一个操作员进行操作&#xff0c;对系统施加压力&#xff0c;多个操作员代表着多个用户进行并…...

Java的概述和运行方式

目录 一.Java是什么&#xff1f; 1.1Java的目前状况和学习需求 1.2Java的平台分类和特点 二.Java程序的运行方式 2.1 Java的程序结构 2.2 JDK、JRE、JVM的关系 2.3 Java运行详情 总结 &#x1f63d;个人主页&#xff1a;tq02的博客_CSDN博客-领域博主 &#x1f308;理想…...

【C语言】每日刷题 —— 牛客

前言 大家好&#xff0c;今天带来一篇新的专栏 c_牛客&#xff0c;不出意外的话每天更新十道题&#xff0c;难度也是从易到难&#xff0c;自己复习的同时也希望能帮助到大家&#xff0c;题目答案会根据我所学到的知识提供最优解。 &#x1f3e1;个人主页&#xff1a;悲伤的猪大…...

JavaEE课程实践-Servlet的部署(tomcat服务器)

目录 Servlet简述 tomcat服务器的安装和运行 Servlet的部署 部署具体步骤 一、创建maven工程 二、创建Servlet类 三、导入相应jar包 四、编写Servlet代码 五、运行maven项目&#xff0c;启动tomcat服务器 六、测试访问是否成功。 Servlet简述 Servlet 是 Java EE 技术…...

Java 中的拆箱和装箱

在 Java 中&#xff0c;每个基本数据类型都对应了一个包装类型&#xff0c;比如&#xff1a;int 的包装类型是 Integer&#xff0c;double 的包装类型是 Double…那么&#xff0c;基本数据类型和包装类型有什么区别呢&#xff1f; 大概有以下几点区别&#xff1a; 成员变量的…...

从0开始自制解释器——实现多位整数的加减法计算器

上一篇我们实现了一个简单的加法计算器&#xff0c;并且了解了基本的词法分析、词法分析器的概念。本篇我们将要对之前实现的加法计算器进行扩展&#xff0c;我们为它添加以下几个功能 计算减法能自动识别并跳过空白字符不再局限于单个整数&#xff0c;而是能计算多位整数 提…...

(12)C#传智:File类,泛型,字典,FileStream,StreamReader,多态

内容有点多&#xff0c;重点:泛型、字典&#xff0c;流与多态。 继续深入学习内容&#xff1a;List、Dictionary、using语句、FileStream 一、File类的继续学心 File.ReadAllLines(string path,Encoding,encoding)指定编码读取返回行字串数组 File.WriteAllText(string…...

Dubbo的服务暴漏与服务发现源码详解

服务暴漏 如果配置需要刷新则根据配置优先级刷新服务配置 如果服务已经导出&#xff0c;则直接返回 是否异步导出&#xff08;全局或者服务级别配置了异步&#xff0c;则需要异步导出服务&#xff09; 服务暴漏入口DefaultModuleDeployer#exportServices private void exp…...

Python 的IDE——PyCharm

IDE介绍与安装 介绍 集成开发环境&#xff08;IDE&#xff09; 集成开发环境(IDE,integrated Development Environment) —— 集成开发软件需要的所有工具&#xff0c;一般包括以下工具&#xff1a; 图形用户界面 代码编辑器(支持代码补全、自动缩进) 编译器/解释器 调试器…...

01 C语言使用链表实现队列(Queue、FIFO)模块

01 C语言使用链表实现队列&#xff08;Queue、FIFO&#xff09;模块 作者将狼才鲸创建日期2023-03-08Gitee源码仓库地址&#xff1a;C语言使用链表实现队列&#xff08;Queue、FIFO&#xff09;模块 Linux原生的队列KFIFO一次只能操作一个队列&#xff0c;操作变长元素时&…...

2.2操作系统-进程管理:前趋图、前趋图与PV操作

2.1操作系统-进程管理&#xff1a;前趋图\前趋图与PV操作前趋图前趋图与PV操作练习前趋图与PV操作&#xff0c;一般出现了&#xff0c;分值在2~3分左右&#xff0c;技巧性很强。 前趋图 前趋图是为了描述一个程序的各部分间的依赖关系&#xff0c;或者是一个大的计算的各个子…...

凤凰游攻略

凤凰游攻略1 装备&#x1f4e6;1.1 证件1.2 日常用品1.3 药品1.4 衣物1.5 洗漱用品2 交通&#x1f697;3 住宿&#x1f3e0;4 美食&#x1f355;5 拍照&#x1f4f7;5.1 租苗族服5.1.1 单租服装5.1.2 服装化妆5.2 一条龙旅拍6 路线&#x1f5fa;️景点&#x1f3d9;️7 注意⚠️…...

Nginx 高可用方案

准备工作 10.10.4.5 10.10.4.6 VIP&#xff1a;10.10.4.10 两台虚拟机。安装好Nginx 安装Nginx 更新yum源文件&#xff1a; rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm wget -O /etc/yum.repos.d/CentOS-Ba…...

Linux基本指令

文章目录 常用Linux命令常见Linux指令 1、ls指令 语法&#xff1a;ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及其他信息。常用选项&#xff1a; -a 列出目录下的所有文件&#xf…...

Linux系统基础命令(二)

一、浏览和切换目录 ls命令&#xff1a;列出文件和目录&#xff0c;主要用于列出文件和目录 CentOS的终端默认是有颜色标注的。一般来说&#xff1a;蓝色--->目录&#xff1b;绿色-->可执行文件&#xff1b;红色--->压缩文件&#xff1b;浅蓝色--->链接文件&#…...

【C++】C++11——简介|列表初始|简化声明|nullptr与范围for|STL中的变化

文章目录一、C11简介二、列表初始化三、简化声明四、nullptr与范围for五、STL中一些变化一、C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于TC1主要是对C98标准中的漏洞进行修复…...

Python -- 函数

文章目录1、一个简单的函数2、多参数函数3、返回值3.1、简单的返回3.2、返回列表和字典4、传入列表5、传入任意数量的实参5.1、以元组和字典的形式5.2、形参的排列顺序6、将函数储存在模块中1、一个简单的函数 函数用关键字def来定义&#xff0c;传参时不用指定参数类型 para&…...

Pytorch中utils.data 与torchvision简介

Pytorch中utils.data 与torchvision简介1 数据处理工具概述2 utils.data简介3 torchvision简介3.1 transforms3.2 ImageFolder1 数据处理工具概述 Pytorch涉及数据处理&#xff08;数据装载、数据预处理、数据增强等&#xff09;主要工具包及相互关系如下图所示&#xff0c;主…...

学习 Python 之 Pygame 开发魂斗罗(十)

学习 Python 之 Pygame 开发魂斗罗&#xff08;十&#xff09;继续编写魂斗罗1. 解决敌人不开火的问题2. 创建爆炸效果类3. 为敌人跳入河中增加爆炸效果4. 玩家击中敌人继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;九&#xff09;中&#xff0c;…...

Keepalive+LVS群集部署

KeepaliveLVS群集部署一、Keepalive概述1、什么是Keepalive2、Keepalive工作原理3、Keepalive主要模块及作用4、Keepalived 服务重要功能&#xff08;1&#xff09;管理 LVS 负载均衡软件&#xff08;2&#xff09;支持故障自动切换&#xff08;3&#xff09;实现 LVS 负载调度…...

数组、指针总结【面试题】

文章目录0. 补充知识数组笔试题1. 一维数组1.1 字符数组1.1.1 sizeof1.1.2 strlen1.2 二维数组2. 指针笔试题0. 补充知识 在进入数组与指针的练习时&#xff0c;我们先来复习以下以下的知识点&#xff0c;这可以帮助我们更好的理解下面练习 数组是一组能存放相同类型的类型的元…...