jbase实现业务脚本化

经过晚上和早上的努力,终于补上框架最后一块了,业务脚本侦听变化后自动编译jar包和调用,实现维护成本低,开发效率高的框架的基本体系。

实现自动编译jar包的类

package appcode;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;
import java.io.*;
import org.apache.commons.io.FileUtils;
import java.nio.charset.StandardCharsets;
import java.nio.charset.*;

public class AutoBuild {
    /// <summary>
    /// 缓存路径和类型,允许多线程读一个线程写
    /// </summary>
    private static ConcurrentHashMap<String, Boolean> Building = new ConcurrentHashMap<>();

    /// <summary>
    /// 编译指定路径的java代码
    /// </summary>
    /// <param name="basePath">根路径</param>
    /// <param name="codepath">类代码路径</param>
    /// <param name="proname">工程名称,不带后缀的</param>
    /// <param name="standprofilepath">标准工程文件全路径</param>
    /// <returns>返回空成功,非空就是失败原因</returns>
    public static String Build(String basePath,String codepath, String proname, String standprofilepath)
    {
        Process proc = null;
        try
        {
            //有编译的退出
            if (Building.containsKey(codepath))
            {
                System.out.println(codepath+"已经有进程在编译了,退出本次编译!");
                return "";
            }
            try
            {
                if (!Building.containsKey(codepath))
                {
                    AutoBuild.HashTryAdd(Building, codepath, true);
                }
            }
            catch (Exception ex)
            {
                System.out.println("编译并发"+ex.getMessage());
            }
            //编译临时总目录
            String buildPath = Paths.get(basePath, "AutoBuildTmp").toString();
            File directory = new File(buildPath);
            //没有编译的临时目录就创建
            if (!directory.exists()) {
                directory.mkdirs();
            }
            //构建项目编译文件夹
            String proBuildPath = Paths.get(buildPath, proname).toString();
            File directoryPro = new File(proBuildPath);
            //没有编译的临时目录就创建
            if (!directoryPro.exists()) {
                directoryPro.mkdirs();
            }
            File fi = new File(codepath);
            //编译目录类全名
            String clsFullName = Paths.get(proBuildPath, fi.getName()).toString();
            System.out.println("拷贝:" + codepath + "到:"+ clsFullName);
            List<String> lines = FileUtils.readLines(fi, "UTF-8");
            StringBuilder sbNewCode=new StringBuilder();
            String jarRootPath=proBuildPath;
            System.out.println("包名称:" + proname);
            String [] arr=proname.split("\\.");
            String pakName="";
            if(arr.length>1)
            {
                for(int p=0;p<arr.length-1;p++)
                {
                    jarRootPath=Paths.get(jarRootPath,arr[p]).toString();
                    //创建jar包目录结构
                    File directoryProJarChild = new File(jarRootPath);
                    //没有编译的临时目录就创建
                    if (!directoryProJarChild.exists()) {
                        directoryProJarChild.mkdirs();
                    }
                    if(pakName=="")
                    {
                        pakName=arr[p];
                    }
                    else
                    {
                        pakName+="."+arr[p];
                    }
                }
            }
            sbNewCode.append("package "+pakName+";"+System.lineSeparator());
            for (String line : lines) {
                sbNewCode.append(line+System.lineSeparator());
            }
            WriteText2File(clsFullName,sbNewCode.toString());
            //复制类代码
            //copyFile(new File(codepath), new File(clsFullName));
            String cmmand = "javac";
            StringBuilder retsb = new StringBuilder();
            //得到javac编译命令串
            String cmdStr=GetJavacStr(basePath,standprofilepath,fi.getName());
            System.out.println("编译命令:" + cmdStr);
            System.out.println("运行路径:" + directoryPro);
            // 创建进程并执行命令
            Process process = Runtime.getRuntime().exec(cmdStr,null,directoryPro);
            // 获取命令行程序的输出结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                retsb.append(line);
            }
            // 等待命令行程序执行完毕
            int exitCode=process.waitFor();
            // 关闭资源
            reader.close();
            System.out.println("编译返回:" + exitCode);
            //编译完成
            if (exitCode == 0)
            {
                // 获取目录中的所有文件和子目录
                File[] files = directoryPro.listFiles();
                // 遍历文件和子目录
                for (File file : files) {
                    if (!file.isDirectory()) {
                        if (file.getName().endsWith(".class")) {
                            copyFile(new File(file.toString()),new File(Paths.get(jarRootPath,file.getName()).toString()));
                        }
                    }
                }
                String pakStr="jar cf "+proname+".jar"+" "+arr[0];
                System.out.println("打包命令:" + pakStr);
                System.out.println("打包运行路径:" + directoryPro);
                //打包jar包
                Process processPak = Runtime.getRuntime().exec(pakStr,null,directoryPro);
                // 等待命令行程序执行完毕
                int exitCodePak=processPak.waitFor();
                System.out.println("打包返回:" + exitCodePak);
                if (exitCodePak == 0)
                {
                    String jarOutPath=Paths.get(proBuildPath,proname+".jar").toString();
                    String jarBinPath=Paths.get(basePath,"BinAshx",proname+".jar").toString();
                    System.out.println("从:" + jarOutPath+"拷贝到:"+jarBinPath);
                    //拷贝生成的jar包到BinAshx
                    copyFile(new File(jarOutPath), new File(jarBinPath));
                }
                //删除源文件
                //DeleteFile(new File(proBuildPath));
                return "";
            }
            String retstr = retsb.toString();
            return retstr;
        }
        catch (Exception ex)
        {
            return ex.getMessage();
        }
        finally
        {
            if (Building.containsKey(codepath))
            {
                Building.remove(codepath);
            }
        }
    }

    /// <summary>
    /// 解决多线程写并发
    /// </summary>
    /// <param name="hs"></param>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public static void HashTryAdd(ConcurrentHashMap<String, Boolean> hs, String key, Boolean value)
    {
        try
        {
            //更新类型
            if (!hs.containsKey(key))
            {
                hs.put(key, value);
            }
        }
        catch (Exception ex)
        {
            System.out.println("并发编译捕获的正常日志,忽略"+ex.getMessage());
            //更新类型
            if (!hs.containsKey(key))
            {
                hs.put(key, value);
            }
        }
    }

    ///拷贝文件
    public static void copyFile(File srcFile, File destFile) {
        //判断原文件是否存在
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("源文件:" + srcFile + "不存在!");
        }
        //判断原文件是否为一个文件
        if (!srcFile.isFile()) {
            throw new IllegalArgumentException(srcFile + "不是一个文件!");
        }
        try {
            FileInputStream in = new FileInputStream(srcFile);
            FileOutputStream out = new FileOutputStream(destFile);
            /**
             * 读取原文件,以字节流的形式读到byte数组,1024个字节=1KB
             * 循环读取
             * in.read()读到bytes数组中,位置从0-bytes.length
             */
            byte[] bytes = new byte[10 * 1024];
            int b;  //b为读取到的字节长度
            while ((b = in.read(bytes, 0, bytes.length)) != -1) {
                //写入
                out.write(bytes, 0, b);
                out.flush();
            }
            //关闭
            in.close();
            out.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    ///删除文件和目录
    public static void DeleteFile(File file){
        //判断文件不为null或文件目录存在
        if (file == null || !file.exists()){
            return;
        }
        //取得这个目录下的所有子文件对象
        File[] files = file.listFiles();
        //遍历该目录下的文件对象
        for (File f: files){
            //判断子目录是否存在子目录,如果是文件则删除
            if (f.isDirectory()){
                DeleteFile(f);
            }else {
                f.delete();
            }
        }
    }

    ///得到javac编译命令
    ///basePath:根地址
    ///standprofilepath:依赖jar包工程地址
    ///javaName:java类文件名称
    private static String GetJavacStr(String basePath,String standprofilepath,String javaName)
    {
        String retStr="javac -encoding UTF-8 -classpath ";
        try {
            //判断配置是否存在
            File file = new File(standprofilepath);
            if (!file.exists()) {
                System.out.println(standprofilepath + "文件不存在,请确认!");
                return "";
            }
            //解析xml
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(file);
            // 获得根节点
            Element rootElement = document.getDocumentElement();
            // 获得根节点下的所有子节点
            NodeList students = rootElement.getChildNodes();
            String classPath="";
            for (int i = 0; i < students.getLength(); i++) {
                // 由于节点多种类型,而一般我们需要处理的是元素节点
                Node childNode = students.item(i);
                // 元素节点就是非空的子节点,也就是还有孩子的子节点
                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element childElement = (Element) childNode;
                    //不是对象配置元素就忽略
                    if (childElement.getNodeName() != "orderEntry") {
                        continue;
                    }
                    //解析得到包名
                    String name = childElement.getAttribute("name");
                    String oneJarPath=Paths.get(basePath,name+".jar").toString();
                    if(classPath=="")
                    {
                        classPath=oneJarPath;
                    }
                    else
                    {
                        classPath+=";"+oneJarPath;
                    }
                }
            }
            retStr+=classPath+" "+javaName;
            return retStr;
        }
        catch (Exception ex) {
            System.out.println(standprofilepath + ex.getMessage());
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 将文本写入文件
     *
     * @param filePath 文件全路径
     * @param text     文本
     **/
    public static void WriteText2File(String filePath, String text) {
        // 创建文件
        File file = new File(filePath);
        if (!file.exists()) {
            try {
                // 创建文件父级目录
                File parentFile = file.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                // 创建文件
                file.createNewFile();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 将文本写入文件
        WriteText2File(file, text);
    }

    /**
     * 将文本写入文件
     *
     * @param file 文件对象
     * @param text 文本
     **/
    public static void WriteText2File(File file, String text) {

        BufferedWriter writer = null;
        try {
            FileOutputStream writerStream = new FileOutputStream(file);
            writer = new BufferedWriter(new OutputStreamWriter(writerStream, "UTF-8"));
            writer.write(text);
            writer.flush();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}

然后实现目录Java监控调用编译

import java.io.*;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationObserver;


//监控文件
public class FileListener extends FileAlterationListenerAdaptor {
    //启动
    @Override
    public void onStart(FileAlterationObserver observer) {
        super.onStart(observer);
    }

    //新建目录
    @Override
    public void onDirectoryCreate(File directory)
    {
    }

    //目录修改
    @Override
    public void onDirectoryChange(File directory)
    {
    }

    //目录删除
    @Override
    public void onDirectoryDelete(File directory)
    {
    }

    //创建文件
    @Override
    public void onFileCreate(File file) {
        String compressedPath = file.getAbsolutePath();
        if(compressedPath.contains("AutoBuildTmp"))
        {
            return;
        }
        if (file.canRead()) {
            String confStr = compressedPath.replace(MainInit.BllJavaBasePath, "").replace("\\", "/").split("\\.")[0];
            MainMiddleware.GetObjectByConfString(confStr,null,"",compressedPath);
        }
    }

    //修改文件
    @Override
    public void onFileChange(File file) {
        String compressedPath = file.getAbsolutePath();
        if(compressedPath.contains("AutoBuildTmp"))
        {
            return;
        }
        String confStr = compressedPath.replace(MainInit.BllJavaBasePath, "").replace("\\", "/").split("\\.")[0];
        MainMiddleware.GetObjectByConfString(confStr,null,"",compressedPath);
    }

    //删除文件
    @Override
    public void onFileDelete(File file) {

    }

    //停止
    @Override
    public void onStop(FileAlterationObserver observer) {
        super.onStop(observer);
    }
}


文件侦听器

import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;

import java.io.File;

//监控文件
public class FileMonitor {

    //监视器
    private FileAlterationMonitor monitor;

    //构造函数
    public FileMonitor(long interval)
    {

        monitor = new FileAlterationMonitor(interval);
    }

    /**
     * 给文件添加监听
     *
     * @param path     文件路径
     * @param listener 文件监听器
     */
    public void monitor(String path, FileAlterationListener listener) {
        IOFileFilter directories = FileFilterUtils.and(
                FileFilterUtils.directoryFileFilter(),
                HiddenFileFilter.VISIBLE);
        IOFileFilter files       = FileFilterUtils.and(
                FileFilterUtils.fileFileFilter(),
                FileFilterUtils.suffixFileFilter(".java"));
        IOFileFilter filter = FileFilterUtils.or(directories, files);
        FileAlterationObserver observer = new FileAlterationObserver(new File(path),filter);
        monitor.addObserver(observer);
        observer.addListener(listener);
    }

    //停止
    public void stop() throws Exception {
        monitor.stop();
    }

    //启动
    public void start() throws Exception {
        monitor.start();

    }
}

主初始化实现java脚本监控

import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.nio.file.*;
import java.io.*;

public class MainInit {
    //是否已经执行了初始化
    private static boolean HasInit=false;

    //网站根地址
    private static String webBasePath="";

    //业务脚本根地址
    public static String BllJavaBasePath="";

    //执行初始化
    public static void TryInit(String basePath) {
        //只初始化一次
        if (HasInit == true) {
            return;
        }
        HasInit = true;
        webBasePath = basePath;
        File fiBase=new File(basePath);
        String parentPath=fiBase.getParent();
        File fiParent=new File(parentPath);
        String codeBasePath=basePath;
        //开发环境
        if(fiParent.getName().equals("artifacts"))
        {
            //到out一级
            File fiParent1=new File(fiParent.getParent());
            //到WebUI一级
            File fiParent2=new File(fiParent1.getParent());
            codeBasePath=Paths.get(fiParent2.toString(),"web").toString()+File.separator;
        }
        //用容器的配置xml初始化容器
        LIS.Core.Context.ObjectContainer.InitIoc(basePath);
        try
        {
            BllJavaBasePath=codeBasePath;
            System.out.println("监控目录:"+codeBasePath);
            FileMonitor fileMonitor = new FileMonitor(5000);
            fileMonitor.monitor(codeBasePath, new FileListener());
            fileMonitor.start();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

主中间件调整

import appcode.IBaseHttpHandler;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.concurrent.ConcurrentHashMap;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.util.*;


@javax.servlet.annotation.WebServlet(name = "MianMiddleware")
public class MainMiddleware extends javax.servlet.http.HttpServlet {

    /// <summary>
    /// 缓存路径和类型,允许多线程读一个线程写
    /// </summary>
    private static ConcurrentHashMap<String, Class> hsType = new ConcurrentHashMap<>();

    ///网站根地址
    public static String WebBasePath="";

    ///执行post请求
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //得到网站根路径
        if(WebBasePath=="")
        {
            WebBasePath= getServletContext().getRealPath("/");
        }
        //尝试执行初始化主逻辑
        MainInit.TryInit(WebBasePath);
        response.setContentType("text/html");
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        String url=request.getRequestURI();
        //解析得到类名
        String className = url.split("\\.")[0];
        PrintWriter writer = response.getWriter();
        //取第一部分
        if (className.charAt(0) == '/') {
            className = className.substring(1);
        }
        int index=className.indexOf("/");
        className=className.substring(index+1);
        //反射得到类型
        Object objDeal = GetObjectByConfString(className,writer,WebBasePath,"");
        //转换处理接口
        if(objDeal!=null)
        {
            //转换成接口
            appcode.IBaseHttpHandler baseDeal=(appcode.IBaseHttpHandler)objDeal;
            baseDeal.ProcessRequest(request,response);
        }
        else
        {
            Write(writer,"未找到名称为:"+className+"的处理类");
        }
    }


    /// <summary>
    /// 通过配置得当对象
    /// </summary>
    /// <param name="confStr">配置UI/login/ashx/AshDemo</param>
    /// <param name="writer">输出</param>
    /// <param name="basePath">Web根</param>
    /// <param name="isBuild">是否是编译</param>
    /// <returns></returns>
    public static Object GetObjectByConfString(String confStr,PrintWriter writer,String basePath,String javaBllClsPath) {
        try {
            //根
            if(basePath=="")
            {
                basePath=WebBasePath;
            }
            System.out.println("confStr:"+confStr);
            //不包含类型或者要强行编译就进行编译
            if (!hsType.containsKey(confStr)||javaBllClsPath!="") {
                String [] nameArr=confStr.split("/");
                String classFullName = "";
                //类代码全路径
                String classCodePath = basePath;
                for (int i = 0; i < nameArr.length; i++)
                {
                    //类代码文件全名
                    classCodePath = Paths.get(classCodePath, nameArr[i]).toString();
                    //类带命名空间的全名
                    if(classFullName!="")
                    {
                        classFullName += "." + nameArr[i];
                    }
                    else
                    {
                        classFullName = nameArr[i];
                    }
                }
                //类代码地址,后面实现用脚本编译用
                classCodePath = classCodePath + ".java";
                if(javaBllClsPath!="")
                {
                    classCodePath=javaBllClsPath;
                }
                String standardPath=Paths.get(basePath,"Conf","StandAshxProj.iml").toString();
                //编译返回
                String buildRet=appcode.AutoBuild.Build(basePath,classCodePath,classFullName,standardPath);
                System.out.println("buildRet:"+buildRet);
                //编译的jar名字
                String clsJarPath = Paths.get(basePath, "BinAshx", classFullName + ".jar").toString();
                System.out.println("加载jar包:"+clsJarPath);
                //自己生成jar包路径
                URL url = new File(clsJarPath).toURI().toURL();
                URL[] urls = new URL[]{url};
                //加载程序集,这里很重要,一定要指定父加载器,否则加载的类和父加载器的类不认为是一个类
                URLClassLoader loader = new URLClassLoader(urls, MainMiddleware.class.getClassLoader());
                //加载类
                Class c = loader.loadClass(classFullName);
                //先写死,后面执行编译和从jar包反射
                hsType.put(confStr, c);
            }
            Class c = hsType.get(confStr);
            //创建对象
            Object o = c.newInstance();
            return o;
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    //get直接走post的逻辑
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request,response);
    }

    ///输出数据到前台
    private static void Write(PrintWriter writer,String str)
    {
        writer.println(str);
        writer.flush();
        writer.close();
    }

}

在这里插入图片描述

自动编译的临时目录结构
在这里插入图片描述

自动生成的jar包
在这里插入图片描述

在这里插入图片描述

在idea里面改业务脚本代码后用浏览器访问就能立即生效,简单高效,适用于服务型系统。整个涉及到目录文件监控、驱动javac和jar打包、反射重复加载类、文件读写等。

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

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

相关文章

无感刷新 token

文章目录 背景基本思路需解决的问题请求进入死循环标记刷新 token 请求避免请求拦截覆盖 refresh token并发刷新 token 完整代码注意&#xff1a;拦截器注册顺序另一种方案&#xff1a;事件驱动刷新 前景提要&#xff1a; ts 简易封装 axios&#xff0c;统一 API 实现在 confi…

海康Visionmaster调试脚本:对脚本进行调试的方法

第一步&#xff0c;在脚本模块中使用导出工程功能&#xff0c;将模块中的代码导出 第二步&#xff0c;找到导出的工程&#xff0c;并打开 第三步&#xff0c;生成解决方案&#xff0c;设置断点&#xff0c;点击 VS 菜单调试中的附加到进程&#xff0c;选择 ShellModuleManage…

计算虚拟化1——CPU虚拟化

目录 vCPU的概念 vCPU和CPU的关系 CPU的Ring级别 CPU虚拟化技术 软件辅助全虚拟化 半虚拟化 硬件辅助虚拟化 计算资源的虚拟化可以分为CPU虚拟化、内存虚拟化、I/O虚拟化三个方面 CPU虚拟化&#xff1a;多个虚拟机共享CPU资源&#xff0c;对虚拟机中的敏感指令进行截获…

EntherNet IP通讯学习

# 哎 最近接触ENIP通讯&#xff0c;但是觉得这玩意真的挺复杂的&#xff0c;主要是资料太少了。 好像大家都在保密一样。 1、学习这个通讯一定是因为实际工作中有用到&#xff0c;所以这个时候你一定有一个PLC做了从站。 OK&#xff0c;那下面继续学习吧&#xff01; 首先先上…

数智赋能!麒麟信安参展全球智慧城市大会

10月31日至11月2日&#xff0c;为期三天的2023全球智慧城市大会长沙在湖南国际会展中心举办&#xff0c;大会已连续举办12届&#xff0c;是目前全球规模最大、专注于城市和社会智慧化发展及转型的主题展会。长沙市委常委、常务副市长彭华松宣布开幕&#xff0c;全球智慧城市大会…

TypeScript 第一站概念篇

前言 &#x1f52e; 好长一段时间没有写文章了&#xff0c;原因是经历了一次工作变动&#xff0c;加入了一个有一定规模的开发团队&#xff0c;前端算上我有四个人&#xff0c;很欣慰&#xff0c;体验一下团队配合的感觉&#xff0c;在我之上有一个组长&#xff0c;比我年长四…

Mozilla Firefox 119 现已可供下载

Mozilla Firefox 119 开源网络浏览器现在可以下载了&#xff0c;是时候先看看它的新功能和改进了。 Firefox 119 改进了 Firefox View 功能&#xff0c;现在可以提供更多内容&#xff0c;如最近关闭的标签页和浏览历史&#xff0c;你可以按日期或网站排序&#xff0c;还支持查…

学习笔记三十一:k8s安全管理:认证、授权、准入控制概述SA介绍

K8S安全实战篇之RBAC认证授权-v1 k8s安全管理&#xff1a;认证、授权、准入控制概述认证k8s客户端访问apiserver的几种认证方式客户端认证&#xff1a;BearertokenServiceaccountkubeconfig文件 授权Kubernetes的授权是基于插件形成的&#xff0c;其常用的授权插件有以下几种&a…

SpringBoot集成Dubbo

在SpringMVC中Dubbo的使用https://tiantian.blog.csdn.net/article/details/134194696?spm1001.2014.3001.5502 阿里巴巴提供了Dubbo集成SpringBoot开源项目。(这个.....) 地址GitHub https://github.com/apache/dubbo-spring-boot-project 查看入门教程 反正是pilipala一大…

【技术分享】RK356X Android 使用 libgpiod 测试gpio

前言 libgpiod 是用于与 Linux GPIO 字符设备交互的 C 库和工具库&#xff1b;此项目包含六种命令行工具&#xff08;gpiodetect、gpioinfo、gpioset、gpioget、gpiomon&#xff09;&#xff0c;使用这些工具可以在命令行设置和获取GPIO的状态信息&#xff1b;在程序开发中也可…

网易按照作者批量采集新闻资讯软件说明文档

大家好&#xff0c;我是淘小白~ 今天给大家介绍的爬虫软件是网易按照作者采集的软件 1、软件语言&#xff1a; Python 2、使用到的工具 Python selenium库、谷歌浏览器、谷歌浏览器驱动 3、文件说明&#xff1a; 4、配置文件说明&#xff1a; 5、环境配置 安装Python&am…

【入门Flink】- 03Flink部署

集群角色 Flik提交作业和执行任务&#xff0c;需要几个关键组件&#xff1a; 客户端(Client)&#xff1a;代码由客户端获取并做转换&#xff0c;之后提交给JobManger JobManager&#xff1a;就是Fink集群里的“管事人”&#xff0c;对作业进行中央调度管理&#xff1b;而它获…

《面向对象软件工程》笔记——1-2章

“学习不仅是一种必要&#xff0c;而且是一种愉快的活动。” - 尼尔阿姆斯特朗 文章目录 第一章 面向对象软件工程的范畴历史方面经济方面维护方面现代软件维护观点交付后维护的重要性 需求、分析和设计方面团队开发方面没有计划&#xff0c;测试&#xff0c;文档阶段的原因面向…

Nginx简介,Nginx搭载负载均衡以及Nginx部署前端项目

目录 一. Nginx简介 Nginx的优点 二. Nginx搭载负载均衡 2.1 Nginx安装 2.1.1 安装依赖 2.1.2 解压nginx安装包 2.1.3 安装nginx 2.1.4 启动nginx服务 2.2 tomcat负载均衡 2.3 Nginx配置 三. Nginx前端部署 一. Nginx简介 NGINX&#xff08;读作&#xff1a;engi…

欧科云链研究院:如何降低Web3风险,提升虚拟资产创新的安全合规

在香港Web3.0行业&#xff0c;技术推动了虚拟资产投资市场的快速增长&#xff0c;但另一方面&#xff0c;JPEX诈骗案等行业风险事件也接连发生&#xff0c;为Web3行业发展提供了重要警示。在近期的香港立法会施政报告答问会上&#xff0c;行政长官李家超表示&#xff0c;与诈骗…

win10 下编译ffmpeg3.36.tar.gz

所需工具&#xff1a; win10 ffmpeg3.36.tar.gz。 或其他版本&#xff0c;下载地址&#xff1a;Index of /releases msys2。 下载地址&#xff1a;http://www.msys2.org。 Visual Studio 2017。 1. 安装MSYS MSYS2像是windows下的一个子系统&#xff0c;…

3.4_Linux-浏览文件系统

1.Linux 文件系统 如果你刚接触Linux系统&#xff0c;可能就很难弄清楚Linux如何引用文件和目录&#xff0c;对已经习惯Microsoft Windows操作系统方式的人来说更是如此。在继续探索Linux系统之前&#xff0c;先了解一下它的布局是有好处的。 你将注意到的第一个不同点是&…

MASK-RCNN tensorflow环境搭建

此教程默认你已经安装了Anaconda&#xff0c;且tensorflow 为cpu版本。为什么不用gpu版本&#xff0c;原因下面解释。 此教程默认你已经安装了Anaconda。 因为tensorflow2.1后的gpu版&#xff0c;不支持windows。并且只有高版本的tensorflow才对应我的CUDA12.2&#xff1b; 而…

从零开始的JSON库教程(一)

本文是学习github大佬miloyip而做的读书笔记&#xff0c;项目点此进入 目录 1、JSON是什么 2、搭建编译环境 3、头文件与API设计 4、JSON的语法子集 5、单元测试 6、宏的编写技巧 7、实现解析器 8、关于断言 1、JSON是什么 JSON&#xff08;JavaScript Object Notati…

SoftwareTest5 - 你就只知道功能测试吗 ?

你就只知道功能测试吗 ? 一 . 按照测试对象划分1.1 文档测试1.2 可靠性测试1.3 容错性测试1.4 安装卸载测试1.5 内存泄漏测试1.6 弱网测试 二 . 按是否查看代码划分2.1 黑盒测试2.2 白盒测试2.3 灰盒测试 三 . 按照开发阶段划分3.1 单元测试3.2 集成测试3.3 冒烟测试3.4 系统测…
最新文章