代码生成器实现

news2024/11/9 9:37:54

代码生成器实现

实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的

构造数据模型

需求分析

借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而

言,其强调 数据模型 + 模板 = 文件 的思想,所以代码生成器最重要的一个部分之一就是数据模型。在这里数据

模型共有两种形式组成:

  • 数据库中表、字段等信息

针对这部分内容,可以使用元数据读取并封装到java实体类中

  • 用户自定义的数据

为了代码生成器匹配多样的使用环境,可以让用户自定义的数据,并且以key-value的形式配置到properties文件中

接下来针对这两方面的数据进行处理

PropertiesUtils工具类自定义数据

通过PropertiesUtils工具类,统一对properties文件夹下的所有.properties文件进行加载,并存入内存中

package cn.itcast.generate.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

/**
 * 需要将自定义的配置信息写入到properties文件中
 *      配置到相对于工程的properties文件夹下
 */
public class PropertiesUtils {

    public static Map<String,String> customMap = new HashMap<>();

    static {
        File dir = new File("properties");
        try {
            List<File> files = FileUtils.searchAllFile(new File(dir.getAbsolutePath()));
            for (File file : files) {
                if(file.getName().endsWith(".properties")) {
                    Properties prop = new Properties();
                    prop.load(new FileInputStream(file));
                    customMap.putAll((Map) prop);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PropertiesUtils.customMap.forEach((k, v)->{
            System.out.println(k+"--"+v);
        });
    }
}

骚戴理解:这里的PropertiesUtils工具类的作用就是把properties文件夹下的所有.properties文件读取处理,主要就是用于用户自定义的数据,可能有的数据不是从数据库里面读取的,所以就可以定义在properties配置文件里,然后读取配置文件获取数据,再加载到模板里面

导入代码生成器依赖的配置文件

#SQL类型和java类型替换规则
VARCHAR=String
BIGINT=Long
INT=Integer
DATE=java.util.Date
DATETIME=java.util.Date
DOUBLE=Double
TEXT=String
VARCHAR2=String
NVARCHAR2=String
NUMBER=Long
CHAR=String
MEDIUMTEXT=String
TINYINT=Integer
LONGTEXT=String

#Table的前缀或者后缀
tableRemovePrefixes=tb_,co_,pe_,bs_

元数据处理

加载指定数据库表,将表信息转化为实体类对象(Table)

package cn.itcast.generate.utils;

import cn.itcast.generate.entity.Column;
import cn.itcast.generate.entity.DataBase;
import cn.itcast.generate.entity.Table;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class DataBaseUtils {

    //获取到mysql中所有的数据库名称

    //获取数据库连接
    public static Connection getConnection(DataBase db) throws Exception {
        Properties props = new Properties();
        props.put("remarksReporting","true");//获取数据库的备注信息
        props.put("user",db.getUserName());
        props.put("password",db.getPassWord());
        Class.forName(db.getDriver());//注册驱动
        return DriverManager.getConnection(db.getUrl(),props);
    }


    //获取数据库列表
    public static List<String> getSchemas(DataBase db) throws Exception {
        //1.获取元数据
        Connection connection = getConnection(db);
        DatabaseMetaData metaData = connection.getMetaData();
        //2.获取所有数据库列表
        ResultSet rs = metaData.getCatalogs();
        List<String> list = new ArrayList<>();
        while (rs.next()) {
            list.add(rs.getString(1));
        }
        rs.close();
        connection.close();
        return list;
    }

    /**
     * 获取数据库中的表和字段构造实体类
     *      Table对象
     *
     *  1.参数
     *      DataBase 数据库对象
     *  2.操作步骤
     *      1.获取连接
     *      2.获取databasemetaData
     *      3.获取当前数据库中的所有表
     *      4.获取每个表中的所有字段
     *      5.封装到java对象中即可
     */
    public static List<Table> getDbInfo(DataBase db) throws  Exception {
        //1.获取连接
        Connection connection = getConnection(db);
        //2.获取元数据
        DatabaseMetaData metaData = connection.getMetaData();
        //3.获取当前数据库中的所有表
        ResultSet tables = metaData.getTables(null, null, "pe_permission", new String[]{"TABLE"});

        List<Table> list = new ArrayList<>();

        while (tables.next()) {
            Table tab = new Table();
            //i.表名
            String tableName = tables.getString("TABLE_NAME"); //bs_user  User
            //ii.类名
            String className = removePrefix(tableName);
            //iii.描述
            String remarks = tables.getString("REMARKS");
            //iiii.主键(主键可能是组合主键,有多个,所以primaryKeys是多个主键的set集合)
            ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, tableName);
            String keys = "";
            while (primaryKeys.next()) {
                String keyname = primaryKeys.getString("COLUMN_NAME");
                keys += keyname+",";
            }
            tab.setName(tableName);
            tab.setName2(className);
            tab.setComment(remarks);
            tab.setKey(keys);
            //处理表中的所有字段

            ResultSet columns = metaData.getColumns(null, null, tableName, null);

            List <Column> columnList = new ArrayList<>();

            while (columns.next()) {
                Column cn = new Column();
                //构造Column对象
                //列名称
                String columnName = columns.getString("COLUMN_NAME"); //user_id  userId , create_time createTime
                cn.setColumnName(columnName);
                //属性名
                String attName = StringUtils.toJavaVariableName(columnName);
                cn.setColumnName2(attName);
                //java类型和数据库类型
                String dbType = columns.getString("TYPE_NAME");//VARCHAR,DATETIME
                cn.setColumnDbType(dbType);
                String javaType = PropertiesUtils.customMap.get(dbType);
                cn.setColumnType(javaType);
                //备注
                String columnRemark = columns.getString("REMARKS");//VARCHAR,DATETIME
                cn.setColumnComment(columnRemark);
                //是否主键
                String pri = null;
                if(StringUtils.contains(columnName ,keys.split(","))) {
                    pri = "PRI";
                }
                cn.setColumnKey(pri);
                columnList.add(cn);
            }
            columns.close();
            tab.setColumns(columnList);
            list.add(tab);
        }
        tables.close();
        connection.close();
        return list;
    }

    public static String removePrefix(String tableName) {
        String prefix = PropertiesUtils.customMap.get("tableRemovePrefixes");
        //bs_,     tb_    , co_    ,
        String temp = tableName;  //bs_user
        for(String pf : prefix.split(",")) {
            temp = StringUtils.removePrefix(temp,pf,true);
        }
        //temp = user
        return StringUtils.makeAllWordFirstLetterUpperCase(temp);
    }

    public static void main(String[] args) throws Exception {
        DataBase db = new DataBase("MYSQL","ihrm");
        db.setUserName("root");
        db.setPassWord("111111");

        List<Table> dbInfo = DataBaseUtils.getDbInfo(db);
        for (Table table : dbInfo) {
            List<Column> columns = table.getColumns();
            for (Column column : columns) {
                System.out.println(column);
            }
        }
    }
}

骚戴理解:removePrefix方法就是把数据库表名的前缀去掉,然后再转成首字母大写的实体类名称,表名前缀都写在了配置文件里面,所以先通过PropertiesUtils获取配置文件的前缀配置信息,如下所示,然后调用StringUtils的removePrefix方法去掉前缀,最后调用StringUtils的makeAllWordFirstLetterUpperCase方法转成首字母大写

#Table的前缀或者后缀
tableRemovePrefixes=tb_,co_,pe_,bs_

实现代码生成

需求分析

为了代码更加直观和易于调用,实现代码生成共有两个类组成:

  • UI界面统一调用的入口类:GeneratorFacade

方便多种界面调用,主要完成数据模型获取,调用核心代码处理类完成代码生成

  • 代码生成核心处理类:Generator

根据数据模型和模板文件路径,统一生成文件到指定的输出路径

模板生成

  • 配置统一调用入口类GeneratorFacade
package cn.itcast.generate.core;

import cn.itcast.generate.entity.DataBase;
import cn.itcast.generate.entity.Settings;
import cn.itcast.generate.entity.Table;
import cn.itcast.generate.utils.DataBaseUtils;
import cn.itcast.generate.utils.PropertiesUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 1.采集用户UI界面输入的数据
 *      模板位置
 *      代码生成路径
 *      工程配置对象 setting
 *      数据库对象   DataBase
 * 2.准备数据模型
 *      1.自定义配置
 *      2.元数据
 *      3.setting
 * 3.调用核心处理类完成代码生成工作
 *      方法:Generator
 */
public class GeneratorFacade {

    private String templatePath;
    private String outPath;
    private Settings settings;
    private DataBase db;
    private Generator generator;

    public GeneratorFacade(String templatePath, String outPath, Settings settings, DataBase db) throws Exception {
        this.templatePath = templatePath;
        this.outPath = outPath;
        this.settings = settings;
        this.db = db;
        this.generator = new Generator(templatePath,outPath);
    }

    /**
     * 1.准备数据模型
     * 2.调用核心处理类完成代码生成工作
     */
    public void generatorByDataBase() throws  Exception {
        List<Table> tables = DataBaseUtils.getDbInfo(db);
        for (Table table : tables) {
            //对每一个Table对象进行代码生成
            /**
             * 数据模型
             * 调用Generator核心处理类
             */
            Map<String,Object> dataModel = getDataModel(table);
//
//            for(Map.Entry<String,Object> entry:dataModel.entrySet()) {
//                System.out.println(entry.getKey() + "--" + entry.getValue());
//            }
//            System.out.println("------------------------");
            generator.scanAndGenerator(dataModel);
        }
    }

    /**
     * 根据table对象获取数据模型
     */
    private  Map<String,Object> getDataModel(Table table) {
        Map<String,Object> dataModel = new HashMap<>();
        //1.自定义配置
        dataModel.putAll(PropertiesUtils.customMap);
        //2.元数据
        dataModel.put("table",table);  //table.name2
        //3.setting
        dataModel.putAll(this.settings.getSettingMap());
        //4.类型
        dataModel.put("ClassName",table.getName2());
        return dataModel;
    }
}

骚戴理解:通过DataBaseUtils.getDbInfo(db);获取到的是数据库表数据的元数据信息,getDataModel方法的作用是为了封装数据模型,数据模型结合模板生成代码,数据模型包括自定义配置、元数据、setting里面的数据,而ClassName只是为了方便后面拿这个类名而已,所以也封装在其中

  • 处理模板代码生成的核心类Generator
package cn.itcast.generate.core;

import cn.itcast.generate.utils.FileUtils;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器的核心处理类
 *      使用Freemarker完成文件生成
 *             数据模型 + 模板
 *  数据:
 *      数据模型
 *      模板的位置
 *      生成文件的路径
 *
 */
public class Generator {

    private String templatePath;//模板路径
    private String outPath;//代码生成路径
    private Configuration cfg;

    public Generator(String templatePath, String outPath) throws Exception {
        this.templatePath = templatePath;
        this.outPath = outPath;
        //实例化Configuration对象
        cfg = new Configuration();
        //指定模板加载器
        FileTemplateLoader ftl = new FileTemplateLoader(new File(templatePath));
        cfg.setTemplateLoader(ftl);
    }

    /**
     * 代码生成
     *      1.扫描模板路径下的所有模板
     *      2.对每个模板进行文件生成(数据模型)
     */
    public void scanAndGenerator(Map<String,Object> dataModel) throws Exception {
        //1.根据模板路径找到此路径下的所有模板文件
        List<File> fileList = FileUtils.searchAllFile(new File(templatePath));
        //2.对每个模板进行文件生成
        for (File file : fileList) {
            executeGenertor(dataModel,file);
        }
    }

    /**
     * 对模板进行文件生成
     * @param dataModel : 数据模型
     * @param file      : 模板文件
     *            模板文件:c:com.ihrm.system.abc.java
     */
    private void executeGenertor(Map<String,Object> dataModel,File file) throws Exception {
        //1.文件路径处理   (E:\模板\${path1}\${path2}\${path3}\${ClassName}.java)
        //templatePath : E:\模板\
        String templateFileName = file.getAbsolutePath().replace(this.templatePath,"");
        String outFileName = processTemplateString(templateFileName,dataModel);
        //2.读取文件模板
        Template template = cfg.getTemplate(templateFileName);
        template.setOutputEncoding("utf-8");//指定生成文件的字符集编码
        //3.创建文件
        File file1 = FileUtils.mkdir(outPath, outFileName);
        //4.模板处理(文件生成)
        FileWriter fw = new FileWriter(file1);
        template.process(dataModel,fw);
        fw.close();
    }


    public String processTemplateString(String templateString,Map dataModel) throws Exception {
        StringWriter out = new StringWriter();
        Template template = new Template("ts",new StringReader(templateString),cfg);
        template.process(dataModel,out);
        return out.toString();
    }

    public static void main(String[] args) throws Exception {
        String templatePath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day13\\资源\\测试\\模板";
        String outPath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day13\\资源\\测试\\生成代码路径";
        Generator generator = new Generator(templatePath, outPath);
        Map <String,Object> dataModel = new HashMap<>();
        dataModel.put("username","张三");
        generator.scanAndGenerator(dataModel);
    }

}

骚戴理解:executeGenertor方法这段代码的作用是根据模板文件和数据模型生成代码文件。

1. String templateFileName = file.getAbsolutePath().replace(this.templatePath,"");

这行代码的作用是将模板文件的绝对路径中的模板根路径(即templatePath)替换为空字符串,得到模板文件名(不包含根路径)。

2. String outFileName = processTemplateString(templateFileName,dataModel);

这行代码的作用是将模板文件名中的变量替换为具体的值,得到输出文件的文件名。

3. Template template = cfg.getTemplate(templateFileName);

这行代码的作用是根据模板文件名,从Configuration对象中获取对应的Template对象。

4. template.setOutputEncoding("utf-8");

这行代码的作用是指定生成文件的字符集编码为UTF-8。

5. File file1 = FileUtils.mkdir(outPath, outFileName);

这行代码的作用是根据输出路径和输出文件名,创建输出文件。

6. FileWriter fw = new FileWriter(file1);

这行代码的作用是创建一个FileWriter对象,用于将模板处理后的结果输出到指定的文件中。

7. template.process(dataModel,fw);

这行代码的作用是将数据模型和FileWriter对象作为参数传入Template的process方法中,根据数据模型中的值,将模板文件中的变量替换为具体的值,并将输出结果写入FileWriter对象中。

8. fw.close();

这行代码的作用是关闭FileWriter对象,释放资源。

路径处理

使用字符串模板对文件生成路径进行统一处理

    public String processTemplateString(String templateString,Map dataModel) throws Exception {
        StringWriter out = new StringWriter();
        Template template = new Template("ts",new StringReader(templateString),cfg);
        template.process(dataModel,out);
        return out.toString();
    }

骚戴理解:这段代码是使用FreeMarker模板引擎来处理模板字符串,将模板字符串中的变量替换为具体的值,最终返回替换后的字符串。

1.StringWriter out = new StringWriter();

这行代码的作用是创建一个StringWriter对象,用于接收模板处理后的输出结果。

2.Template template = new Template("ts",new StringReader(templateString),cfg);

这行代码的作用是创建一个Template对象,用于解析模板字符串并进行变量替换。其中,第一个参数是模板名称(随意取名),第二个参数是StringReader对象,用于读取模板字符串,第三个参数是Configuration对象,用于配置模板引擎的相关参数。

3.template.process(dataModel,out);

这行代码的作用是将数据模型和输出流作为参数传入Template的process方法中,根据数据模型中的值,将模板字符串中的变量替换为具体的值,并将输出结果写入输出流。

4.return out.toString();

这行代码的作用是将输出流中的内容转换为字符串,并返回该字符串。

制作模板

模板制作的约定

  • 模板位置

模板统一放置到相对于当前路径的模板文件夹下

  • 自定义数据

自定义的数据以 .propeties 文件(key-value)的形式存放入相对于当前路径的 properties 文件夹下

  • 数据格式

名称

说明

author

作者

project

工程名

path1

包名1

path2

包名2

path3

包名3

pPackage

完整包名

projectComment

工程描述

ClassName

类名

table

数据库信息

table中数据内容:

name

表名

comment

表注释

key

表主键

columns

所有列信息

columnName

字段列名

columnName2

属性名

columnType

java类型

columnDbType

数据库类型

columnComment

注释

columnKey

是否主键

需求分析

制作通用的SpringBoot程序的通用模板

  • 实体类

类路径,类名,属性列表(getter,setter方法)

  • 持久化层

类路径,类名,引用实体类

  • 业务逻辑层

类路径,类名,引用实体类,引用持久化层代码

  • 视图层

类路径,类名,引用实体类,引用业务逻辑层代码,请求路径

  • 配置文件

pom文件,springboot配置文件

SpringBoot通用模板

实体类

package ${pPackage}.pojo;


import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name = "${table.name}")
public class ${ClassName} implements Serializable {
     //这里table.columns是因为上面数据模型里面放的是 dataModel.put("table",table);
    <#list table.columns as column>
    <#if column.columnKey??>
    @Id
    </#if>
    private ${column.columnType} ${column.columnName2};
    </#list>

    <#list table.columns as column>
    public void set${column.columnName2?cap_first}(${column.columnType} value) {
        this.${column.columnName2} = value;
    }
    public ${column.columnType} get${column.columnName2?cap_first}() {
       return this.${column.columnName2};
    }
    </#list>
}

骚戴理解:这段代码使用FreeMarker模板语言生成Java类的属性和getter/setter方法。

具体来说,这段代码包含了以下两个部分:

1. 属性生成部分

-<#list table.columns as column>:这行代码使用FreeMarker的list指令,遍历数据模型中的table.columns字段,将每个元素赋值给变量column。

-<#if column.columnKey??>:这行代码使用FreeMarker的if指令,判断当前列是否为主键列。

-@Id:如果当前列是主键列,则生成@Id注解。

-<#if>:if指令的结束标签。

-private ${column.columnType} ${column.columnName2};:生成Java类的私有属性,属性名和属性类型分别对应数据模型中的column.columnName2和column.columnType。

-</#list>:list指令的结束标签。

2. getter/setter方法生成部分

-<#list table.columns as column>:这行代码使用FreeMarker的list指令,遍历数据模型中的table.columns字段,将每个元素赋值给变量column。

-public void set${column.columnName2?cap_first}(${column.columnType} value) {:生成Java类的setter方法,方法名为set+属性名(首字母大写),方法参数类型和属性类型相同,方法体为将参数值赋值给属性,其中?cap_first是使用内置函数,可以把column.columnName2变成首字母大写。

-public ${column.columnType} get${column.columnName2?cap_first}() {:生成Java类的getter方法,方法名为get+属性名(首字母大写),方法返回值类型为属性类型,方法体为返回属性值。

-</#list>:list指令的结束标签。

总之,这段代码的作用是根据数据模型中的表结构信息,生成Java类的属性和getter/setter方法。

Service层

<#assign classNameLower = ClassName ? uncap_first>
package ${pPackage}.service;


import com.ihrm.common.utils.IdWorker;
import ${pPackage}.dao.${ClassName}Dao;
import ${pPackage}.pojo.${ClassName};
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class ${ClassName}Service {

    @Autowired
    private ${ClassName}Dao ${classNameLower}Dao;

    @Autowired
    private IdWorker idWorker;

    /**
     * 保存
     */
    public void add(${ClassName} ${classNameLower}) {
        //基本属性的设置
        String id = idWorker.nextId()+"";
        ${classNameLower}.setId(id);
        ${classNameLower}Dao.save(${classNameLower});
    }

    /**
     * 更新
     */
    public void update(${ClassName} ${classNameLower}) {
        ${classNameLower}Dao.save(${classNameLower});
    }

    /**
     * 删除
     */
    public void deleteById(String id) {
        ${classNameLower}Dao.deleteById(id);
    }

    /**
     * 根据id查询
     */
    public ${ClassName} findById(String id) {
        return ${classNameLower}Dao.findById(id).get();
    }

    /**
     * 查询列表
     */
    public List<${ClassName}> findAll() {
        return ${classNameLower}Dao.findAll();
    }
}

Controller层

<#assign classNameLower = ClassName ? uncap_first>
package ${pPackage}.controller;

import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;


import ${pPackage}.service.${ClassName}Service;
import ${pPackage}.pojo.${ClassName};
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

//解决跨域问题
@CrossOrigin
@RestController
@RequestMapping(value="/${classNameLower}")
public class ${ClassName}Controller {

    @Autowired
    private ${ClassName}Service ${classNameLower}Service;

    //保存
    @RequestMapping(value="",method = RequestMethod.POST)
    public Result save(@RequestBody ${ClassName} ${classNameLower})  {
        //业务操作
        ${classNameLower}Service.add(${classNameLower});
        return new Result(ResultCode.SUCCESS);
    }

    //根据id更新
    @RequestMapping(value = "/{id}",method = RequestMethod.PUT)
    public Result update(@PathVariable(value="id") String id, @RequestBody ${ClassName} ${classNameLower} ) {
        //业务操作
        ${classNameLower}.setId(id);
        ${classNameLower}Service.update(${classNameLower});
        return new Result(ResultCode.SUCCESS);
    }

    //根据id删除
    @RequestMapping(value="/{id}",method = RequestMethod.DELETE)
    public Result delete(@PathVariable(value="id") String id) {
        ${classNameLower}Service.deleteById(id);
        return new Result(ResultCode.SUCCESS);
    }

    //根据id查询
    @RequestMapping(value="/{id}",method = RequestMethod.GET)
    public Result findById(@PathVariable(value="id") String id) throws CommonException {
        ${ClassName} ${classNameLower} = ${classNameLower}Service.findById(id);
        return new Result(ResultCode.SUCCESS,${classNameLower});
    }

    //查询全部
    @RequestMapping(value="",method = RequestMethod.GET)
    public Result findAll() {
        List<${ClassName}> list = ${classNameLower}Service.findAll();
        Result result = new Result(ResultCode.SUCCESS);
        result.setData(list);
        return result;
    }
}

Dao层

package ${pPackage}.dao;

import ${pPackage}.pojo.${ClassName};
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;


public interface ${ClassName}Dao extends JpaRepository<${ClassName},String> ,JpaSpecificationExecutor<${ClassName}> {
}

配置文件

  • application.yml
server: 
 port: 9001
spring: 
 application:  
   name: ${project}-${path3} #指定服务名
 datasource:  
   driverClassName: ${driverName}
   url: ${url}
   username: ${dbuser}
   password: ${dbpassword}
 jpa: 
   database: MySQL
   show-sql: true
  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>${path2}_parent</artifactId>
        <groupId>${path1}.${path2}</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>${path2}_${project}</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>${path1}.${path2}</groupId>
            <artifactId>${path2}_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>

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

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

相关文章

chatgpt赋能python:Python与硬件结合的现实价值

Python与硬件结合的现实价值 Python是当今最受欢迎和广泛使用的编程语言之一&#xff0c;因其易学易用、开放源代码和灵活性而备受欢迎。但是当我们将它与硬件相结合&#xff0c;它能做到什么&#xff1f; 在这篇文章中&#xff0c;我们将向您介绍如何将Python与硬件结合&…

戴尔外星人m16r1国行中文原厂Windows11系统自带Support Assist OS Recovery恢复出厂设置

戴尔外星人m16r1国行中文原厂系统自带Support Assist OS Recovery恢复出厂设置 文件地址https://pan.baidu.com/s/1Pq09oDzmFI6hXVdf8Vqjqw?pwd3fs8 提取码:3fs8 支持Support Assist OS recovery型号: 戴尔外星人m18r1国行中文版Windows11系统 戴尔外星人x16r1国行中文版…

2023/6/9总结

CSS Less嵌套 子元素的选择器可以直接写在父元素里面。 如果不是它的后代元素&#xff0c;比如你想写伪类选择器、交集选择器&#xff0c;需要在前面加&号。 Less运算&#xff1a; 加减乘除都可以&#xff0c;运算符必须用空格隔开。如果俩个元素都有单位&#xff0…

【SpringBoot 3.x】使用starter整合Druid

Druid介绍 Druid是阿里巴巴的一个开源项目&#xff0c;号称为监控而生的数据库连接池&#xff0c;在功能、性能、扩展性方面都超过其他例如DBCP、C3P0、BoneCP、Proxool、JBoss DataSource等连接池,而且Druid已经在阿里巴巴部署了超过600个应用&#xff0c;通过了极为严格的考…

网络作业9【计算机网络】

网络作业9【计算机网络】 前言推荐网络作业9一. 单选题&#xff08;共12题&#xff0c;36分&#xff09;二. 多选题&#xff08;共1题&#xff0c;3分&#xff09;三. 填空题&#xff08;共2题&#xff0c;10分&#xff09;四. 阅读理解&#xff08;共1题&#xff0c;17分&…

C++【STL】之string的使用

STL简介 STL是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。STL由六大组件构成&#xff1a;仿函数、算法、迭代器、空间配置器、容器和配接器。 其中各种容器可以很大帮助的提升我们编写程序的效率&#…

静态NAT配置与验证实验

静态NAT配置与验证实验 【实验目的】 部署静态NAT。熟悉静态NAT的应用方法。验证配置。 【实验拓扑】 实验拓扑如图所示。 实验拓扑 设备参数如表所示。 设备参数表 设备 接口 IP地址 子网掩码 默认网关 R1 f0/0 192.168.10.1 255.255.255.0 N/A S1/0 10.0.0.1…

GlyphControl: Glyph Conditional Control for Visual Text Generation

GlyphControl: Glyph Conditional Control for Visual Text Generation (Paper reading) Yukang Yang, Microsoft Research Asia, arXiv2023, Cited: 0, Code, Paper 1. 前言 最近&#xff0c;人们对开发基于扩散的文本到图像生成模型的兴趣日益增长&#xff0c;这些模型能够…

软件工程开发文档写作教程(11)—需求分析书的编写

本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl本文参考资料&#xff1a;电子工业出版社《软件文档写作教程》 马平&#xff0c;黄冬梅编著 需求分析书主要内容 按照国家《软件需求说明书GB8567-88》所定义的标准&#xff0c;软件需求…

2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;阅读文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 一个基于uniapp写的小程序&#xff0c;后端…

MATLAB | 绘图复刻(九) | 泰勒图及组合泰勒图

有粉丝问我这个图咋画&#xff1a; 我一看&#xff0c;这不就泰勒图嘛&#xff0c;就fileexchange上搜了一下泰勒图绘制代码&#xff0c;但是有的代码比较新的版本运行要改很多地方&#xff0c;有的代码需要包含一些压缩包没并没有的别人写的函数&#xff0c;于是我干脆自己写了…

JAVA-八种基础数据类型和包装类型及相关面试题

文章目录 前言一、基本数据类型1.1 分类1.2 概念1.3 代码1.4 二维表 二、各基本数据类型间强制转换2.1 为什么Java中有强制转换&#xff1f;2.2 示例代码 三、包装类型3.1 为什么有包装类型&#xff1f;3.2 基本概念3.3 转换方法 四、转换过程中使用的自动装箱和自动拆箱4.1 来…

Redis Lua脚本原理

Lua脚本执行过程 创建并修改Lua环境 1 创建基础Lua环境2 载入函数库3 创建全局表格Lua4 替换随机函数5 创建排序辅助函数6 创建redis.pcall函数7 全局环境保护8 修改后的Lua环境保存到服务器状态的Lua属性&#xff0c;等待脚本执行 Redis中带有不确定性的命令&#xff1a; …

RK3588平台开发系列讲解(以太网篇)PHY状态机

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、PHY状态机定义二、PHY的状态变化三、PHY的状态变化打印沉淀、分享、成长,让自己和他人都能有所收获!😄 一、PHY状态机定义 phy状态机: 目录:include/linux/phy.h enum phy_state {PHY_DOWN = 0,</

开源模型的力量

2 月&#xff0c;Meta 发布了其大型语言模型&#xff1a;LLaMA。与 OpenAI 及其 ChatGPT 不同&#xff0c;Meta 不仅仅为世界提供了一个可以玩的聊天窗口。 相反&#xff0c;它将代码发布到开源社区&#xff0c;此后不久模型本身就被泄露了。研究人员和程序员立即开始修改、改…

Protobuf实战:通讯录

网络版通讯录 需求 Protobuf常⽤于通讯协议、服务端数据交换场景。接下来将实现⼀个⽹络版本的通讯录&#xff0c;模拟实现客⼾端与服务端的交互&#xff0c;通过Protobuf来实现各端之间的协议序列化。 需求如下&#xff1a; 客⼾端可以选择对通讯录进⾏以下操作&#xff1a;…

电子科技大学编译原理复习笔记(七):自下而上语法分析

目录 前言 重点一览 引言 自下而上分析 分析方法 规范规约&#xff08;最左规约&#xff0c;对应最右推导&#xff09; 算符优先分析法 算符优先文法 最左素短语 举个例子 优先关系表的构造 规范规约与算符优先分析 LR分析法 概述 LR&#xff08;0&#xff09…

系统架构设计师 2:计算机基础

一、计算机硬件 1 处理器&#xff08;CPU&#xff09; 处理器是计算机系统运算和控制的核心部件。 1.1 指令集 处理器的指令集按照其复杂程度可分为复杂指令集&#xff08;CISC&#xff09;与精简指令集&#xff08;RISC&#xff09;。 随着研究的深入&#xff0c;RISC已经…

基于深度学习的高精度安全背心检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度安全背心检测识别系统可用于日常生活中或野外来检测与定位安全背心目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的安全背心目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5…

2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号

2.25 sigprocmask函数使用 阻塞信号集有时称作信号掩码。 联想&#xff1a;fcntl函数可以修改fd属性。 ./sigprocmask & //将程序设置为后台运行&#xff0c;输入ls可以同步有输出 fg //将程序恢复到前台运行#include <stdio.h> #include <signal.…