Java使用ANTLR4对Lua脚本语法校验

news2025/4/20 8:42:56

文章目录

  • 什么是ANTLR?
  • 第一个例子
  • ANTLR4 的工作流程
  • Lua脚本语法校验
    • 准备一个Lua Grammar文件
    • maven配置
    • 生成Lexer Parser Listener Visitor代码
    • 新建实体类
    • Lua语法遍历器
    • 语法错误监听器
    • 单元测试
  • 参考

什么是ANTLR?

https://www.antlr.org/

ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It’s widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build and walk parse trees.

ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。 它被广泛用于构建语言、工具和框架。ANTLR 根据语法定义生成解析器,解析器可以构建和遍历解析树。

第一个例子

https://github.com/antlr/antlr4/blob/master/doc/getting-started.md#a-first-example

  1. 新建个Hello.g4文件:
// Define a grammar called Hello
grammar Hello;
r  : 'hello' ID ;         // match keyword hello followed by an identifier
ID : [a-z]+ ;             // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
  1. 安装IDEA插件
    ANTLR v4:https://plugins.jetbrains.com/plugin/7358-antlr-v4

  2. 打开ANTLR Preview
    r : 'hello' ID ; // match keyword hello followed by an identifier这行上右键,点击Test Rule r
    输入hello world,能够准确识别出ID为word。
    在这里插入图片描述
    输入hello World,就不能够识别出ID为world了。
    在这里插入图片描述

ANTLR4 的工作流程

  • 词法分析器 (Lexer) :将字符序列转换为单词(Token)的过程。词法分析器(Lexer)一般是用来供语法解析器(Parser)调用的。
  • 语法解析器 (Parser) :通常作为编译器或解释器出现。它的作用是进行语法检查,并构建由输入单词(Token)组成的数据结构(即抽象语法树)。语法解析器通常使用词法分析器(Lexer)从输入字符流中分离出一个个的单词(Token),并将单词(Token)流作为其输入。实际开发中,语法解析器可以手工编写,也可以使用工具自动生成。
  • 抽象语法树 (Parse Tree) :是源代码结构的一种抽象表示,它以树的形状表示语言的语法结构。抽象语法树一般可以用来进行代码语法的检查,代码风格的检查,代码的格式化,代码的高亮,代码的错误提示以及代码的自动补全等。
  • 使用 antlr4 的一般流程如下:
    • 书写 antlr4 的词法和文法规则
    • 使用 antlr4 的生成工具处理写好的规则,以生成指定语言的 Lexer 和 Parser 代码
    • 调用生成的 Lexer 和 Parser 类,书写相应的逻辑代码,将原始输入文本转化为一个抽象语法树
    • 使用 antlr4 的 visitor 来解析语法树,实现各种功能

实际上,除了 visitor 之外,antlr4 还提供了另一种解析语法树方式,叫做 Listener。Listener 是 antlr4 默认解析语法树的方式,它和 visitor 一样都可以实现对 ParseTree 的解析。如果开启了 visitor 或 listener,那么 antlr4 除了会生成 Lexer 和 Parser 代码,还会生成相应的 Visitor 和 Listener 代码。Listener 和 Visitor 区别如下

ListenerVisitor(个人倾向这种)
是否访问所有节点访问所有节点只访问手动指定的节点
访问节点方式通过 enter 和 exit 方法通过 visit 方法
方法是否有返回值没有返回值有返回值

了解了 Listener 和 Visitor 的区别之后,我们可以总结出 antlr4 的大致工作流程如下:
在这里插入图片描述
如上左边的点线流程代表了通过 ANTLR4,将原始的.g4 规则转化为 Lexer、Parser、Listener 和 Visitor。右边的虚线流程代表了将原始的输入流通过 Lexer 转化为 Tokens,再将 Tokens 通过 Parser 转化为语法树,最后通过 Listener 或 Visitor 遍历 ParseTree 得到最终结果。

Lua脚本语法校验

准备一个Lua Grammar文件

https://github.com/antlr/grammars-v4/tree/master/lua

/*
BSD License

Copyright (c) 2013, Kazunori Sakamoto
Copyright (c) 2016, Alexander Alexeev
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the NAME of Rainer Schuster nor the NAMEs of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This grammar file derived from:

    Lua 5.3 Reference Manual
    http://www.lua.org/manual/5.3/manual.html

    Lua 5.2 Reference Manual
    http://www.lua.org/manual/5.2/manual.html

    Lua 5.1 grammar written by Nicolai Mainiero
    http://www.antlr3.org/grammar/1178608849736/Lua.g

Tested by Kazunori Sakamoto with Test suite for Lua 5.2 (http://www.lua.org/tests/5.2/)

Tested by Alexander Alexeev with Test suite for Lua 5.3 http://www.lua.org/tests/lua-5.3.2-tests.tar.gz
*/

grammar Lua;

chunk
    : block EOF
    ;

block
    : stat* retstat?
    ;

stat
    : ';'
    | varlist '=' explist
    | functioncall
    | label
    | 'break'
    | 'goto' NAME
    | 'do' block 'end'
    | 'while' exp 'do' block 'end'
    | 'repeat' block 'until' exp
    | 'if' exp 'then' block ('elseif' exp 'then' block)* ('else' block)? 'end'
    | 'for' NAME '=' exp ',' exp (',' exp)? 'do' block 'end'
    | 'for' namelist 'in' explist 'do' block 'end'
    | 'function' funcname funcbody
    | 'local' 'function' NAME funcbody
    | 'local' attnamelist ('=' explist)?
    ;

attnamelist
    : NAME attrib (',' NAME attrib)*
    ;

attrib
    : ('<' NAME '>')?
    ;

retstat
    : 'return' explist? ';'?
    ;

label
    : '::' NAME '::'
    ;

funcname
    : NAME ('.' NAME)* (':' NAME)?
    ;

varlist
    : var_ (',' var_)*
    ;

namelist
    : NAME (',' NAME)*
    ;

explist
    : exp (',' exp)*
    ;

exp
    : 'nil' | 'false' | 'true'
    | number
    | string
    | '...'
    | functiondef
    | prefixexp
    | tableconstructor
    | <assoc=right> exp operatorPower exp
    | operatorUnary exp
    | exp operatorMulDivMod exp
    | exp operatorAddSub exp
    | <assoc=right> exp operatorStrcat exp
    | exp operatorComparison exp
    | exp operatorAnd exp
    | exp operatorOr exp
    | exp operatorBitwise exp
    ;

prefixexp
    : varOrExp nameAndArgs*
    ;

functioncall
    : varOrExp nameAndArgs+
    ;

varOrExp
    : var_ | '(' exp ')'
    ;

var_
    : (NAME | '(' exp ')' varSuffix) varSuffix*
    ;

varSuffix
    : nameAndArgs* ('[' exp ']' | '.' NAME)
    ;

nameAndArgs
    : (':' NAME)? args
    ;

/*
var_
    : NAME | prefixexp '[' exp ']' | prefixexp '.' NAME
    ;

prefixexp
    : var_ | functioncall | '(' exp ')'
    ;

functioncall
    : prefixexp args | prefixexp ':' NAME args
    ;
*/

args
    : '(' explist? ')' | tableconstructor | string
    ;

functiondef
    : 'function' funcbody
    ;

funcbody
    : '(' parlist? ')' block 'end'
    ;

parlist
    : namelist (',' '...')? | '...'
    ;

tableconstructor
    : '{' fieldlist? '}'
    ;

fieldlist
    : field (fieldsep field)* fieldsep?
    ;

field
    : '[' exp ']' '=' exp | NAME '=' exp | exp
    ;

fieldsep
    : ',' | ';'
    ;

operatorOr
	: 'or';

operatorAnd
	: 'and';

operatorComparison
	: '<' | '>' | '<=' | '>=' | '~=' | '==';

operatorStrcat
	: '..';

operatorAddSub
	: '+' | '-';

operatorMulDivMod
	: '*' | '/' | '%' | '//';

operatorBitwise
	: '&' | '|' | '~' | '<<' | '>>';

operatorUnary
    : 'not' | '#' | '-' | '~';

operatorPower
    : '^';

number
    : INT | HEX | FLOAT | HEX_FLOAT
    ;

string
    : NORMALSTRING | CHARSTRING | LONGSTRING
    ;

// LEXER

NAME
    : [a-zA-Z_][a-zA-Z_0-9]*
    ;

NORMALSTRING
    : '"' ( EscapeSequence | ~('\\'|'"') )* '"'
    ;

CHARSTRING
    : '\'' ( EscapeSequence | ~('\''|'\\') )* '\''
    ;

LONGSTRING
    : '[' NESTED_STR ']'
    ;

fragment
NESTED_STR
    : '=' NESTED_STR '='
    | '[' .*? ']'
    ;

INT
    : Digit+
    ;

HEX
    : '0' [xX] HexDigit+
    ;

FLOAT
    : Digit+ '.' Digit* ExponentPart?
    | '.' Digit+ ExponentPart?
    | Digit+ ExponentPart
    ;

HEX_FLOAT
    : '0' [xX] HexDigit+ '.' HexDigit* HexExponentPart?
    | '0' [xX] '.' HexDigit+ HexExponentPart?
    | '0' [xX] HexDigit+ HexExponentPart
    ;

fragment
ExponentPart
    : [eE] [+-]? Digit+
    ;

fragment
HexExponentPart
    : [pP] [+-]? Digit+
    ;

fragment
EscapeSequence
    : '\\' [abfnrtvz"'\\]
    | '\\' '\r'? '\n'
    | DecimalEscape
    | HexEscape
    | UtfEscape
    ;

fragment
DecimalEscape
    : '\\' Digit
    | '\\' Digit Digit
    | '\\' [0-2] Digit Digit
    ;

fragment
HexEscape
    : '\\' 'x' HexDigit HexDigit
    ;

fragment
UtfEscape
    : '\\' 'u{' HexDigit+ '}'
    ;

fragment
Digit
    : [0-9]
    ;

fragment
HexDigit
    : [0-9a-fA-F]
    ;

COMMENT
    : '--[' NESTED_STR ']' -> channel(HIDDEN)
    ;

LINE_COMMENT
    : '--'
    (                                               // --
    | '[' '='*                                      // --[==
    | '[' '='* ~('='|'['|'\r'|'\n') ~('\r'|'\n')*   // --[==AA
    | ~('['|'\r'|'\n') ~('\r'|'\n')*                // --AAA
    ) ('\r\n'|'\r'|'\n'|EOF)
    -> channel(HIDDEN)
    ;

WS
    : [ \t\u000C\r\n]+ -> skip
    ;

SHEBANG
    : '#' '!' ~('\n'|'\r')* -> channel(HIDDEN)
    ;

maven配置

使用JDK8的注意:antlr4最高版本为4.9.3,原因如下:
来源:https://github.com/antlr/antlr4/releases/tag/4.10

Increasing minimum java version
Going forward, we are using Java 11 for the source code and the compiled .class files for the ANTLR tool. The Java runtime target, however, and the associated runtime tests use Java 8 (bumping up from Java 7).

<dependencies>
    <dependency>
        <groupId>org.antlr</groupId>
        <artifactId>antlr4-runtime</artifactId>
        <version>${antlr.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-maven-plugin</artifactId>
            <version>${antlr.version}</version>
            <configuration>
                <visitor>true</visitor>
                <listener>true</listener>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>antlr4</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

<properties>
    <!-- https://mvnrepository.com/artifact/org.antlr/antlr4-runtime -->
    <!-- Antlr4 4.9.3 is the last version compatible with Java 8 -->
    <antlr.version>4.9.3</antlr.version>
</properties>

生成Lexer Parser Listener Visitor代码

mvn clean compile

在这里插入图片描述

新建实体类

语法错误:每行有什么错误。

package com.baeldung.antlr.lua.model;

/**
 * 语法错误
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class SyntaxErrorEntry {
    private Integer lineNum;
    private String errorInfo;

    public Integer getLineNum() {
        return lineNum;
    }

    public void setLineNum(Integer lineNum) {
        this.lineNum = lineNum;
    }

    public String getErrorInfo() {
        return errorInfo;
    }

    public void setErrorInfo(String errorInfo) {
        this.errorInfo = errorInfo;
    }
}

语法错误报告:每行有什么错误的集合。

package com.baeldung.antlr.lua.model;

import java.util.LinkedList;
import java.util.List;

/**
 * 语法错误报告
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class SyntaxErrorReportEntry {
    private final List<SyntaxErrorEntry> syntaxErrorList = new LinkedList<>();

    public void addError(int line, int charPositionInLine, Object offendingSymbol, String msg) {
        SyntaxErrorEntry syntaxErrorEntry = new SyntaxErrorEntry();
        syntaxErrorEntry.setLineNum(line);
        syntaxErrorEntry.setErrorInfo(line + "行," + charPositionInLine + "列," + offendingSymbol + "字符处,存在语法错误:" + msg);
        syntaxErrorList.add(syntaxErrorEntry);
    }

    public List<SyntaxErrorEntry> getSyntaxErrorReport() {
        return syntaxErrorList;
    }
}

Lua语法遍历器

package com.baeldung.antlr.lua;

import com.baeldung.antlr.LuaParser;
import com.baeldung.antlr.LuaVisitor;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;

/**
 * Lua语法遍历器
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class LuaSyntaxVisitor implements LuaVisitor<Object> {
// ctrl+O Override即可
}

语法错误监听器

package com.baeldung.antlr.lua;

import com.baeldung.antlr.lua.model.SyntaxErrorReportEntry;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;

/**
 * 语法错误监听器
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class SyntaxErrorListener extends BaseErrorListener {
    private final SyntaxErrorReportEntry reporter;

    public SyntaxErrorListener(SyntaxErrorReportEntry reporter) {
        this.reporter = reporter;
    }

    @Override
    public void syntaxError(Recognizer<?, ?> recognizer,
                            Object offendingSymbol, int line, int charPositionInLine,
                            String msg, RecognitionException e) {
        this.reporter.addError(line, charPositionInLine, offendingSymbol, msg);
    }
}

单元测试

package com.baeldung.antlr;

import com.baeldung.antlr.lua.LuaSyntaxVisitor;
import com.baeldung.antlr.lua.SyntaxErrorListener;
import com.baeldung.antlr.lua.model.SyntaxErrorEntry;
import com.baeldung.antlr.lua.model.SyntaxErrorReportEntry;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.junit.Test;

import java.util.List;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class LuaSyntaxErrorUnitTest {
    public static List<SyntaxErrorEntry> judgeLuaSyntax(String luaScript) {
        //新建一个CharStream,读取数据
        CharStream charStreams = CharStreams.fromString(luaScript);
        //包含一个词法分析器的定义,作用是将输入的字符序列聚集成词汇符号。
        LuaLexer luaLexer = new LuaLexer(charStreams);
        //新建一个词法符号的缓冲区,用于存储词法分析器生成的词法符号(Token)
        CommonTokenStream tokenStream = new CommonTokenStream(luaLexer);
        //新建一个语法分析器,用于分析词法符号缓冲区中的词法符号
        LuaParser luaParser = new LuaParser(tokenStream);

        SyntaxErrorReportEntry syntaxErrorReporter = new SyntaxErrorReportEntry();
        SyntaxErrorListener errorListener = new SyntaxErrorListener(syntaxErrorReporter);
        luaParser.addErrorListener(errorListener);

        LuaSyntaxVisitor luaSyntaxVisitor = new LuaSyntaxVisitor();
        luaSyntaxVisitor.visit(luaParser.chunk());
        return syntaxErrorReporter.getSyntaxErrorReport();
    }

    @Test
    public void testGood() throws Exception {
        List<SyntaxErrorEntry> errorEntryList = judgeLuaSyntax("if a~=1 then print(1) end");
        assertThat(errorEntryList.size(), is(0));
    }

    @Test
    public void testBad() throws Exception {
        //新建一个CharStream,读取数据
        List<SyntaxErrorEntry> errorEntryList = judgeLuaSyntax("if a!=1 then print(1) end");
        assertThat(errorEntryList.size(), is(2));
    }
}

顺便说一下:把antlr4看成一种语言,和java同一级别,这个在使用groovy时也是一样的。
最终目录情况及单元测试情况如下:
在这里插入图片描述

参考

https://www.baeldung.com/java-antlr
https://juejin.cn/post/7018521754125467661
https://www.nosuchfield.com/2023/08/26/ANTLR4-from-Beginning-to-Practice/
https://blog.csdn.net/qq_37771475/article/details/106387201
https://blog.csdn.net/qq_37771475/article/details/106426327

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

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

相关文章

vue3.2 + element-plus 实现跟随input输入框的弹框,弹框里可以分组或tab形式显示选项

效果 基础用法&#xff08;分组选项&#xff09; 高级用法&#xff08;带Tab栏&#xff09; <!-- 弹窗跟随通用组件 SmartSelector.vue --> <!-- 弹窗跟随通用组件 --> <template><div class"smart-selector-container"><el-popove…

Windows VsCode Terminal窗口使用Linux命令

背景描述&#xff1a; 平时开发环境以Linux系统为主&#xff0c;有时又需要使用Windows系统下开发环境&#xff0c;为了能像Linux系统那样用Windows VsCode&#xff0c;Terminal命令行是必不可少内容。 注&#xff1a;Windows11 VsCode 1.99.2 下面介绍&#xff0c;如何在V…

负载均衡的实现方式有哪些?

负载均衡实现方式常见的有: 软件负载均衡、硬件负载均衡、DNS负载均衡 扩展 二层负载均衡&#xff1a;在数据链路层&#xff0c;基于MAC地址进行流量分发&#xff0c;较少见于实际应用中 三层负载均衡&#xff1a;在网络层&#xff0c;基于IP地址来分配流量&#xff0c;例如某…

LWIP学习笔记

TCP/ip协议结构分层 传输层简记 TCP&#xff1a;可靠性强&#xff0c;有重传机制 UDP&#xff1a;单传机制&#xff0c;不可靠 UDP在ip层分片 TCP在传输层分包 应用层传输层网络层&#xff0c;构成LWIP内核程序&#xff1a; 链路层&#xff1b;由mac内核STM芯片的片上外设…

Nodejs Express框架

参考&#xff1a;Node.js Express 框架 | 菜鸟教程 第一个 Express 框架实例 接下来我们使用 Express 框架来输出 "Hello World"。 以下实例中我们引入了 express 模块&#xff0c;并在客户端发起请求后&#xff0c;响应 "Hello World" 字符串。 创建 e…

Visual Studio Code 开发 树莓派 pico

开发环境 MCU&#xff1a;Pico1&#xff08;无wifi版&#xff09;使用固件&#xff1a;自编译版本开发环境&#xff1a;Windows 10开发工具&#xff1a;Visual Studio Code 1.99.2开发语言&#xff1a;MicroPython & C 插件安装 找到Raspberry Pi Pico并安装开启科学上网…

Python与R语言用XGBOOST、NLTK、LASSO、决策树、聚类分析电商平台评论信息数据集

全文链接&#xff1a;https://tecdat.cn/?p41501 分析师&#xff1a;Rui Liu 在当今数字化浪潮席卷的时代&#xff0c;电商市场的蓬勃发展犹如一部波澜壮阔的史诗&#xff0c;蕴藏着无尽的商业价值与潜力。电商平台积累的海量数据&#xff0c;宛如一座等待挖掘的宝藏&#xff…

半导体制造如何数字化转型

半导体制造的数字化转型正通过技术融合与流程重构&#xff0c;推动着这个精密产业的全面革新。全球芯片短缺与工艺复杂度指数级增长的双重压力下&#xff0c;头部企业已构建起四大转型支柱&#xff1a; 1. 数据中枢重构产线生态 台积电的「智慧工厂4.0」部署着30万物联网传感器…

LabVIEW 程序持续优化

LabVIEW 以其独特的图形化编程方式&#xff0c;在工业自动化、测试测量、数据分析等众多领域发挥着关键作用。为了让 LabVIEW 程序始终保持高效、稳定&#xff0c;并契合不断变化的实际需求&#xff0c;持续改进必不可少。下面将从多个关键维度&#xff0c;为大家细致地介绍通用…

Windows10系统RabbitMQ无法访问Web端界面

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 项目场景&#xff1a; 在一个基于 .NET 的分布式项目中&#xff0c;团队使用 RabbitMQ 作为消息队列中间件&#xff0c;负责模块间的异步通信。开发环境为 Windows 10 系统&#xff0c;开发人员按照官…

初阶数据结构--链式二叉树

二叉树&#xff08;链式结构&#xff09; 前面的文章首先介绍了树的相关概念&#xff0c;阐述了树的存储结构是分为顺序结构和链式结构。其中顺序结构存储的方式叫做堆&#xff0c;并且对堆这个数据结构进行了模拟实现&#xff0c;并进行了相关拓展&#xff0c;接下来会针对链…

SpringAI版本更新:向量数据库不可用的解决方案!

Spring AI 前两天&#xff08;4.10 日&#xff09;更新了 1.0.0-M7 版本后&#xff0c;原来的 SimpleVectorStore 内存级别的向量数据库就不能用了&#xff0c;Spring AI 将其全部源码删除了。 此时我们就需要一种成本更低的解决方案来解决这个问题&#xff0c;如何解决呢&…

BladeX单点登录与若依框架集成实现

1. 概述 本文档详细介绍了将BladeX认证系统与若依(RuoYi)框架集成的完整实现过程。集成采用OAuth2.0授权码流程&#xff0c;使用户能够通过BladeX账号直接登录若依系统&#xff0c;实现无缝单点登录体验。 2. 系统架构 2.1 总体架构 #mermaid-svg-YxdmBwBtzGqZHMme {font-fa…

JVM 内存调优

内存调优 内存泄漏&#xff08;Memory Leak&#xff09;和内存溢出&#xff08;Memory Overflow&#xff09;是两种常见的内存管理问题&#xff0c;它们都可能导致程序执行不正常或系统性能下降&#xff0c;但它们的原因和表现有所不同。 内存泄漏 内存泄漏&#xff08;Memo…

Shell脚本提交Spark任务简单案例

一、IDEA打包SparkETL模块&#xff0c;上传值HDFS的/tqdt/job目录 二、创建ods_ETL.sh脚本 mkdir -p /var/tq/sh/dwd vim /var/tq/sh/dwd/ods_ETL.sh chmod 754 /var/tq/sh/dwd/ods——ETL.sh #脚本内容如下 #!/bin/bash cur_date$(date %Y-%m-%d) /opt/bigdata/spark-3.3.2/b…

国标GB28181视频平台EasyCVR视频汇聚系统,打造别墅居民区智能监控体系

一、现状背景 随着国家经济的快速增长&#xff0c;生活水平逐渐提高&#xff0c;私人别墅在城市、乡镇和农村的普及率也在逐年增加。然而&#xff0c;由于别墅区业主经济条件较好&#xff0c;各类不法事件也日益增多&#xff0c;主要集中在以下几个方面&#xff1a; 1&#x…

BGP分解实验·23——BGP选路原则之路由器标识

在选路原则需要用到Router-ID做选路决策时&#xff0c;其对等体Router-ID较小的路由将被优选&#xff1b;其中&#xff0c;当路由被反射时&#xff0c;包含起源器ID属性时&#xff0c;该属性将代替router-id做比较。 实验拓扑如下&#xff1a; 实验通过调整路由器R1和R2的rout…

【玩泰山派】MISC(杂项)- 使用vscode远程连接泰山派进行开发

文章目录 前言流程1、安装、启动sshd2、配置一下允许root登录3、vscode中配置1、安装remote插件2、登录 **注意** 前言 有时候要在开发板中写一写代码&#xff0c;直接在终端中使用vim这种工具有时候也不是很方便。这里准备使用vscode去通过ssh远程连接泰山派去操作&#xff0…

同步/异步日志系统

同步/异步日志系统 项目演示基础测试性能测试测试环境&#xff1a;同步日志器单线程同步日志器多线程异步日志器单线程异步日志器多线程 工具类&#xff08;util.hpp&#xff09;日志等级level.hpp 日志消息message.hpp 日志消息格式化formatter.hpp 日志消息落地sink.hpp 日志…

typescript html input无法输入解决办法

input里加上这个&#xff1a; onkeydown:(e: KeyboardEvent) > {e.stopPropagation();