【源码】SpringBoot事务注册原理

前言

对于数据库的操作,可能存在脏读、不可重复读、幻读等问题,从而引入了事务的概念。

事务

1.1 事务的定义

事务是指在数据库管理系统中,一系列紧密相关的操作序列,这些操作作为一个单一的工作单元执行。事务的特点是要么全部成功,要么全部失败,不会出现部分完成的情况。如果事务中的任何一个操作失败,那么整个事务都会被回滚到开始之前的状态,以确保数据库的一致性和完整性。

1.2 事务的特性

事务具有4个特性:原子性、一致性、隔离性和持久性。通常称为ACID。

1)原子性(Atomicity)

事务是一个不可分割的工作单位,事务中包含的所有操作要么全部成功,要么全部失败。

2)隔离性(Isolation)

一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。

3)持久性(Durability)

一旦事务提交成功,则其所做的修改就会永久保存在数据库中,即使发生系统故障也不会丢失。

4)一致性(Consistency)

事务必须确保数据库从一个一致性状态转变到另一个一致性状态。

更准确的说,事务是通过原子性、隔离性和持久性,实现了事务的一致性。另外,一致性还需要额外的工作来保证,如转账,从A转给B,A的资金减少了,B的资金增加了。转账要么成功、要么失败,但不管成功还是失败,事务的一致性要求两人的资金之和必须一致,要么成功,A减少了、B增加了,要么失败,A和B的资金都不变。为了一致性,需要程序来开启事务,同时修改A和B的资金,从而才能保证一致性。

SpringBoot自动装载

在SpringBoot框架中,会自动引入TransactionAutoConfiguration。且在META-INF的spring-autoconfigure-metadata.properties有如下配置:

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration.ConditionalOnBean=org.springframework.transaction.TransactionManager

说明在自动引入TransactionAutoConfiguration时,必须存在TransactionManager的bean,且要先引入内部类EnableTransactionManagementConfiguration。

EnableTransactionManagementConfiguration

内部类EnableTransactionManagementConfiguration的源码如下:

public class TransactionAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnBean(TransactionManager.class)@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)public static class EnableTransactionManagementConfiguration {@Configuration(proxyBeanMethods = false)@EnableTransactionManagement(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")public static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableTransactionManagement(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)public static class CglibAutoProxyConfiguration {}}
}

EnableTransactionManagementConfiguration内中会自动引入JdkDynamicAutoProxyConfiguration和CglibAutoProxyConfiguration,且添加了@EnableTransactionManagement注解。该注解会自动引入TransactionManagementConfigurationSelector。TransactionManagementConfigurationSelector的父类实现了ImportSelector接口,该类的selectImports()方法返回AutoProxyRegistrar和ProxyTransactionManagementConfiguration类名数组,即AutoProxyRegistrar和ProxyTransactionManagementConfiguration会自动注入到Spring IOC容器中。

Spring的配置类解析的时候,会执行ConfigurationClassParser.processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports),在该方法中,判断importCandidates是否继承于ImportSelector接口,如果是,调用ImportSelector的selectImports()方法,获取需要额外自动加入容器的类。

ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration的源码如下:

package org.springframework.transaction.annotation;/*** Transaction事务管理代理的配置类*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {/*** 返回一个BeanFactoryTransactionAttributeSourceAdvisor【继承AbstractBeanFactoryPointcutAdvisor,重* 写了getPointcut()方法。即当前Advisor包含了事务需要的advice以及pointcut】* @return*/@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {// 声明一个BeanFactoryTransactionAttributeSourceAdvisor对象,// 传入的transactionAttributeSource为AnnotationTransactionAttributeSourceBeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource());advisor.setAdvice(transactionInterceptor());if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}/*** 创建一个AnnotationTransactionAttributeSource对象,该继承AbstractFallbackTransactionAttributeSource,* 对外提供了getTransactionAttribute()方法。该方法返回某个方法的事务注解信息*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/*** 创建一个TransactionInterceptor对象* @return*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor() {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource());if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}

在ProxyTransactionManagementConfiguration中,自动注入了BeanFactoryTransactionAttributeSourceAdvisor、AnnotationTransactionAttributeSource和TransactionInterceptor。添加了事务处理的advisor,该advisor生效时,会执行TransactionInterceptor拦截器。

BeanFactoryTransactionAttributeSourceAdvisor

BeanFactoryTransactionAttributeSourceAdvisor的源码如下:

package org.springframework.transaction.interceptor;/*** 继承AbstractBeanFactoryPointcutAdvisor,重写了getPointcut()方法。即当前Advisor包含了事务需要的advice以及pointcut* 1、维护TransactionAttributeSource对象;* 2、创建一个TransactionAttributeSourcePointcut,重写getTransactionAttributeSource(),返回TransactionAttributeSource对* 象给TransactionAttributeSourcePointcut,进行方法事务注解判断*/
@SuppressWarnings("serial")
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {// 在ProxyTransactionManagementConfiguration类中的transactionAdvisor()方法,// 设置transactionAttributeSource为AnnotationTransactionAttributeSource@Nullableprivate TransactionAttributeSource transactionAttributeSource;private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {@Override@Nullableprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {this.transactionAttributeSource = transactionAttributeSource;}public void setClassFilter(ClassFilter classFilter) {this.pointcut.setClassFilter(classFilter);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}}

在BeanFactoryTransactionAttributeSourceAdvisor切面中,切点为自定义的实现抽象类TransactionAttributeSourcePointcut的对象,抽象方法getTransactionAttributeSource()返回的TransactionAttributeSource对象为ProxyTransactionManagementConfiguration的transactionAdvisor()方法传入的AnnotationTransactionAttributeSource对象。

TransactionAttributeSourcePointcut

TransactionAttributeSourcePointcut的源码如下:

package org.springframework.transaction.interceptor;/*** 抽象类,继承StaticMethodMatcherPointcut,属于静态的方法匹配。在方法匹配中,通过抽象方法getTransactionAttributeSource()获* 取TransactionAttributeSource对象,然后执行TransactionAttributeSource.getTransactionAttribute(),判断方法是否有事务注解,* 如果有,表示匹配;没有则不匹配*/
@SuppressWarnings("serial")
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {@Overridepublic boolean matches(Method method, @Nullable Class<?> targetClass) {if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {return false;}// 返回AnnotationTransactionAttributeSourceTransactionAttributeSource tas = getTransactionAttributeSource();// 在AnnotationTransactionAttributeSource的getTransactionAttribute()方法中,// 获取方法添加的@Transactional注解的信息,如果没有添加@Transactional注解,返回nullreturn (tas == null || tas.getTransactionAttribute(method, targetClass) != null);}@Overridepublic boolean equals(Object other) {if (this == other) {return true;}if (!(other instanceof TransactionAttributeSourcePointcut)) {return false;}TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other;return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource());}@Overridepublic int hashCode() {return TransactionAttributeSourcePointcut.class.hashCode();}@Overridepublic String toString() {return getClass().getName() + ": " + getTransactionAttributeSource();}/*** 抽象方法,获得一个TransactionAttributeSource对象* @return*/@Nullableprotected abstract TransactionAttributeSource getTransactionAttributeSource();}

TransactionAttributeSourcePointcut继承StaticMethodMatcherPointcut,StaticMethodMatcherPointcut的类匹配默认为ClassFilter.TRUE,即匹配所有的类。

当Spring中的bean对象声明之后,都会调用BeanFactoryTransactionAttributeSourceAdvisor切面,遍历bean的方法,执行切点的matches(Method method, @Nullable Class<?> targetClass)方法,该方法执行如下:

6.1)调用getTransactionAttributeSource(),获取一个TransactionAttributeSource。此处为AnnotationTransactionAttributeSource对象;

6.2)因为TransactionAttributeSource对象不为空,所以执行TransactionAttributeSource的getTransactionAttribute()方法,该方法会执行AnnotationTransactionAttributeSource的父类AbstractFallbackTransactionAttributeSource的getTransactionAttribute()方法,如果方法有返回值,则matches()方法返回true。对应的bean创建代理类,并添加Advisor中的拦截器,即添加TransactionInterceptor;

在该方法中,最终会调用子类AnnotationTransactionAttributeSource的determineTransactionAttribute()方法,遍历事务的解析器,从解析器中获取事务属性信息;结合1)中的描述,会解析org.springframework.transaction.annotation包和javax.transaction包下的@Transactional注解。如果没有@Transactional注解,getTransactionAttribute()最终返回null。matches()会返回false,即不支持,如果有注解,则返回true。对应的bean创建代理类,并添加Advisor中的拦截器,即添加TransactionInterceptor;

AnnotationTransactionAttributeSource

AnnotationTransactionAttributeSource的相关源码如下:

package org.springframework.transaction.annotation;/*** 继承AbstractFallbackTransactionAttributeSource,对外提供了getTransactionAttribute()方法。该方法返回某个方法的事务注解信息* 1、添加不同类型的事务注解解析器,用于解析注解信息。支撑jta、ejb和spring的事务*/
@SuppressWarnings("serial")
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSourceimplements Serializable {/** 判断包中是否有javax.transaction.Transactional这个类,即项目中是否有对应的包。如果有,则需要添加对应的事务解析器 */private static final boolean jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", AnnotationTransactionAttributeSource.class.getClassLoader());/** 判断包中是否有javax.ejb.TransactionAttribute这个类,即项目中是否有对应的包。如果有,则需要添加对应的事务解析器 */private static final boolean ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader());/** 标记是否只有public方法才可以添加事务,默认为true */private final boolean publicMethodsOnly;private final Set<TransactionAnnotationParser> annotationParsers;public AnnotationTransactionAttributeSource() {this(true);}/*** 设置publicMethodsOnly的值,并添加事务的注解解析器集合*/public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;if (jta12Present || ejb3Present) {this.annotationParsers = new LinkedHashSet<>(4);// 添加spring的Transactional注解解析器this.annotationParsers.add(new SpringTransactionAnnotationParser());// 根据条件,添加jta和ejb3两种事务的注解解析器if (jta12Present) {this.annotationParsers.add(new JtaTransactionAnnotationParser());}if (ejb3Present) {this.annotationParsers.add(new Ejb3TransactionAnnotationParser());}}else {// 如果不支持jta和ejb3两种注解的事务,则只添加spring的Transactional注解解析器this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());}}/*** 调用determineTransactionAttribute()方法,从类中查找事务注解信息*/@Override@Nullableprotected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);}/*** 调用determineTransactionAttribute()方法,从方法中查找事务注解信息*/@Override@Nullableprotected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);}/*** 确定事务属性。遍历事务解析器集合,对当前element进行解析,如果能够正常解析,则返回对应* 的TransactionAttribute【RuleBasedTransactionAttribute对象】;否则返回null*/@Nullableprotected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {for (TransactionAnnotationParser annotationParser : this.annotationParsers) {TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);if (attr != null) {return attr;}}return null;}}

在AnnotationTransactionAttributeSource构造方法中,会添加SpringTransactionAnnotationParser和JtaTransactionAnnotationParser事务注解解析器。

7.1 SpringTransactionAnnotationParser

SpringTransactionAnnotationParser的源码如下:

package org.springframework.transaction.annotation;
/*** 实现TransactionAnnotationParser,解析org.springframework.transaction.annotation.Transactional的注解信息,* 返回一个RuleBasedTransactionAttribute对象*/
@SuppressWarnings("serial")
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {/*** 解析@Transaction注解,获取一个TransactionAttribute对象*/@Override@Nullablepublic TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {// 在所提供的element上方的注释层次结构中查找Transactional的第一个注释,// 并将该注释的属性与注释层次结构较低级别注释中的匹配的attribute合并AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);// 解析Transactional注解,返回一个RuleBasedTransactionAttribute对象if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}public TransactionAttribute parseTransactionAnnotation(Transactional ann) {return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));}/*** 从注解属性对象中,获取事务注解相关属性信息,封装成RuleBasedTransactionAttribute对象*/protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {// 根据注解的信息,创建一个RuleBasedTransactionAttribute对象RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());Isolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());rbta.setTimeout(attributes.getNumber("timeout").intValue());rbta.setReadOnly(attributes.getBoolean("readOnly"));rbta.setQualifier(attributes.getString("value"));// 添加回滚规则List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {rollbackRules.add(new RollbackRuleAttribute(rbRule));}for (String rbRule : attributes.getStringArray("rollbackForClassName")) {rollbackRules.add(new RollbackRuleAttribute(rbRule));}for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {rollbackRules.add(new NoRollbackRuleAttribute(rbRule));}for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {rollbackRules.add(new NoRollbackRuleAttribute(rbRule));}rbta.setRollbackRules(rollbackRules);return rbta;}@Overridepublic boolean equals(Object other) {return (this == other || other instanceof SpringTransactionAnnotationParser);}@Overridepublic int hashCode() {return SpringTransactionAnnotationParser.class.hashCode();}}

SpringTransactionAnnotationParser用于解析org.springframework.transaction.annotation包下的@Transactional的注解信息。

7.2 JtaTransactionAnnotationParser

JtaTransactionAnnotationParser的源码如下:

package org.springframework.transaction.annotation;/*** 实现TransactionAnnotationParser,用于解析javax.transaction.Transactional注解的事务属性。返回一个RuleBasedTransactionAttribute对象*/
@SuppressWarnings("serial")
public class JtaTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {/*** 判断是否有javax.transaction.Transactional,如果有,则进行注解解析【解析传播行为,回滚的异常类、不回滚的异常类设置回滚规则】,* 返回一个RuleBasedTransactionAttribute对象* @param element* @return*/@Override@Nullablepublic TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(element, javax.transaction.Transactional.class);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}public TransactionAttribute parseTransactionAnnotation(javax.transaction.Transactional ann) {return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));}protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();// 通过注解的value设定传播行为。默认为REQUIREDrbta.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + attributes.getEnum("value").toString());// 根据设置的回滚的异常类,添加回滚规则List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();for (Class<?> rbRule : attributes.getClassArray("rollbackOn")) {rollbackRules.add(new RollbackRuleAttribute(rbRule));}// 根据设置的不回滚的异常类,添加回滚规则,定义为不回滚for (Class<?> rbRule : attributes.getClassArray("dontRollbackOn")) {rollbackRules.add(new NoRollbackRuleAttribute(rbRule));}rbta.setRollbackRules(rollbackRules);return rbta;}@Overridepublic boolean equals(Object other) {return (this == other || other instanceof JtaTransactionAnnotationParser);}@Overridepublic int hashCode() {return JtaTransactionAnnotationParser.class.hashCode();}}

JtaTransactionAnnotationParser用于解析javax.transaction包下的@Transactional注解。

在6.2)中调用AnnotationTransactionAttributeSource的getTransactionAttribute(),最终会调用AnnotationTransactionAttributeSource的determineTransactionAttribute()方法,遍历事务注解的解析器,从解析器中获取事务属性信息。只要方法中添加了org.springframework.transaction.annotation包或javax.transaction包的@Transactional注解,则能够正常获取事务属性信息。

小结

限于篇幅,本篇先分享到这里。以下做一个小结:

1)在SpringBoot框架的项目,项目启动时,会自动开启@EnableTransactionManagement注解,从而自动添加BeanFactoryTransactionAttributeSourceAdvisor切面,添加TransactionAttributeSourcePointcut切点。该切点的ClassFilter为ClassFilter.TRUE,即过滤所有的类。

2)Spring的bean自动注入时,会执行BeanFactoryTransactionAttributeSourceAdvisor切面,遍历bena的方法,执行TransactionAttributeSourcePointcut的matches()。在该方法中,会执行AnnotationTransactionAttributeSource的getTransactionAttribute()方法,判断方法是否添加了org.springframework.transaction.annotation包或javax.transaction包下的@Transactional注解,如果有,则bean的代理类中添加TransactionInterceptor拦截器;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

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

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

相关文章

实验9 浮动静态路由配置

--名称-- 一、 原理描述二、 实验目的三、 实验内容四、 实验配置五、 实验步骤 一、 原理描述 浮动静态路由也是一种特殊的静态路由&#xff0c;主要考虑链路冗余。浮动静态路由通过配置一条比主路由优先级低的静态路由&#xff0c;用于保证在主路由失效的情况下&#xff0c;…

代码随想录 day27|day28|day29

回溯2 切割问题&#xff1a;是在每个节点判断是否是要剪枝收割元素。 startidx 是切割起点&#xff0c;i是本次切割终点 分割回文串 复原ip地址 非递减子序列 都是在树的节点依照题意判断&#xff0c;之后决定是否剪枝。 也就是都有if判断来剪枝 。 下面是非递减子序列。 下…

Python | 虚拟环境的增删改查

mkvirtualenv创建虚拟环境 mkvirtualenv是用于在Pyhon中创建虚拟环境的命令。它通过使用vitualenv库来创建一个隔离的Python环境&#xff0c;以便您可以安装特定版本的Python包&#xff0c;而不会影响全局Python环境。 使用方法: 安装virtualenv&#xff1a;pip install vir…

前端将xlsx转成json

第一种方式&#xff0c;用js方式 1.1先安装插件 万事都离不开插件的支持首先要安装两个插件 1.2. 安装xlsx cnpm install xlsx --save注&#xff1a;这块我用的cnpm&#xff0c;原生的是npm&#xff0c;因为镜像的问题安装了cnpm&#xff0c;至于怎么装网上一搜一大堆 1.3安…

用langchain搭配最新模型ollama打造属于自己的gpt

langchain 前段时间去玩了一下langchain,熟悉了一下大模型的基本概念&#xff0c;使用等。前段时间meta的ollama模型发布了3.0,感觉还是比较强大的&#xff0c;在了解过后&#xff0c;自己去用前后端代码&#xff0c;调用ollama模型搭建了一个本地的gpt应用。 核心逻辑 开始搭…

Vue 封装elementUI的el-popover

1.封装公共组件 <template><div class"confirm-popover disInlineBlock ml10"><el-popover placement"bottom" v-model"visible" :trigger"triggerType"><div class"confirm-popover-popover"><…

vue3中进度条上加高亮圆点

实现效果 小圆点基于进度条定位&#xff08;left&#xff09;。 实现代码 <template><!-- 这块代码实现的功能&#xff1a;progressData遍历的年份进度数组&#xff0c;展示每年完成的进度--><ul><li v-for"(item, index) in progressData" :k…

Unity VR 零基础开发之 Pico4 MR

一、新建Unity2021.3.37 3D工程 二、切换到Android安卓平台 1、点击Unity编辑器左上角的Flie后&#xff0c;选择Build Setting选项。 2、弹出弹窗后&#xff0c;点击Android选项&#xff0c;然后再点击Switch Platform按钮切换成安卓平台。 3、切换完成后Android选项后面会显示…

Centos7下安装sqlplus并连接远程数据库

已经不用Oracle好多年了&#xff0c;然而近期有项目居然需要用到Oracle&#xff0c;并且是在内网的&#xff0c;所以没办法直接安装数据库连接工具来测试连接&#xff0c;只能在Linux的服务器上面安装sqlplus工具来连接&#xff0c;找资料完成了安装和连接&#xff0c;遂记录。…

3. QGis二次开发项目实践一之解决“无法定位程序输入点“

前言 本章讲述实现本项目实现过程中遇到的QGis二次开发库版本和Qt以及其他动态库的版本匹配问题问题复现 本项目是要作为一个子模块集成到用户的项目中本项目最初的开发环境为QGis3.28+Qt5.15.2,而当时并未问清楚用户开发环境所以交付给用户之后,出现了类似下图的问题 出现该…

uniapp利用canvas绘制ECG网格图附带心电图显示------代码

之前是一个关于如何绘制心电图的代码&#xff0c;后续需求是添加ECG的网格背景图也是利用canvas绘制的 先看代码&#xff1a; <template><view><page-head :title"title"></page-head><!-- 心电图显示区 --><view class"disp…

AI论文:一键生成论文的高效工具

说到这个问题&#xff0c;那真的得看你对“靠谱”的定义是怎样的啦&#xff1f; 众所周知&#xff0c;写论文是一项极其耗时间的事情&#xff0c;从开始的选题到文献资料搜索查阅&#xff0c;大纲整理等等一大堆的繁杂工作是极艰辛的。用AI写论文就不一样了&#xff0c;自动化…

深入QML:现代用户界面开发的强大工具20240606

深入QML&#xff1a;现代用户界面开发的强大工具 引言 在数字化时代&#xff0c;用户界面 (UI) 是任何应用程序不可或缺的一部分。为了提供流畅、直观且具有吸引力的用户体验&#xff0c;开发者需要一种强大且灵活的开发工具。QML&#xff08;Qt Modeling Language&#xff0…

简述在 Vue 中,子组件为何不可以修改父组件传递的 Prop ?

在 Vue 中&#xff0c;子组件不应该直接修改父组件传递的 Prop&#xff0c;这是基于 Vue 的单向数据流和组件之间通信的设计原则。以下是几个关键原因&#xff1a; 单向数据流&#xff1a;Vue 提倡的是单向数据流&#xff0c;即从父组件流向子组件。当父组件的状态发生变化时&…

php7.3安装phalcon扩展

php7安装3.4版本的phalcon扩展 适用于Centos6.x和Centos7.x系统&#xff0c;php使用7.1版本&#xff0c;wlnmp一键包已支持该扩展 phalcon扩展包地址&#xff1a;https://github.com/phalcon/cphalcon &#xff08;git clone 有可能连接不上&#xff09; 1、安装所需依赖&a…

Linux Kernel nf_tables 本地权限提升漏洞(CVE-2024-1086)

文章目录 前言声明一、netfilter介绍二、漏洞成因三、漏洞危害四、影响范围五、漏洞复现六、修复方案临时解决方案升级修复方案 前言 2024年1月&#xff0c;各Linux发行版官方发布漏洞公告&#xff0c;修复了一个 netfilter:nf_tables 模块中的释放后重用漏洞&#xff08;CVE-…

基于语音识别的智能电子病历(其他)CC、HPI、ROS案例分析

门诊使用基于语音识别的智能电子病历相对要少一些。很多系统的门诊系统包含了丰富的模版&#xff0c;美国退伍军人医院在非常早的时候&#xff08;2010年之前&#xff09;就通过使用大量的模版来进行处理门诊的数据录入。下面分析中的截图也是来自一些美国退伍军人医院的系统。…

网络高频攻击手段与基本防护措施总结

主要攻击手段 一、云平台攻击 云平台攻击是指针对云服务器的恶意行为&#xff0c;旨在获取非法访问权限、窃取敏感数据或者破坏服务器的正常运行。云平台攻击的形式多样&#xff0c;以下是对云平台攻击的一些主要类型和特点的详细分析&#xff1a; 攻击类型&#xff1a; 凭据…

idea修改国际化语言教程

教程背景 第一种&#xff0c;如果以前的项目已经有国际化语言了&#xff0c;现在的项目是导入的。 第二种&#xff0c;你中途把idea删除卸载了&#xff0c;在安装了别的版本&#xff0c;把这个项目导入进来后的。 第三种&#xff0c;你下载或复制的别人的项目。 以上这三种…

如何实现vue项目不同屏幕适配(2024最新)

文章目录 1.下载插件&#xff0c;修改px单位为rem单位2.配置vue.config.js(如下图位置所示)3.屏幕自适应4.项目实际使用 1.下载插件&#xff0c;修改px单位为rem单位 npm i postcss-plugin-px2rem2.配置vue.config.js(如下图位置所示) 注意在根目录下&#xff0c;如果没有该文…