java 插件式架构_springboot插件式开发框架

2023-11-07

介绍

该框架主要是集成于springboot项目,用于开发插件式应用的集成框架。

核心功能

插件配置式插拔于springboot项目。

在springboot上可以进行插件式开发, 扩展性极强, 可以针对不同项目开发不同插件, 进行不同插件jar包的部署。

可通过配置文件指定要启用或者禁用插件。

支持上传插件和插件配置文件到服务器, 并且无需重启主程序, 动态部署插件、更新插件。

支持查看插件运行状态, 查看插件安装位置。

无需重启主程序, 动态的安装插件、卸载插件、启用插件、停止插件、备份插件、删除插件。

在插件应用模块上可以使用Spring注解定义组件, 进行依赖注入。

支持在插件中开发Rest接口。

支持在插件中单独定义持久层访问等需求。

可以遵循主程序提供的插件接口开发任意扩展功能。

插件可以自定义配置文件。目前只支持yml文件。

支持自定义扩展开发接口, 使用者可以在预留接口上扩展额外功能。

利用扩展机制, 定制了SpringBoot-Mybatis扩展包。使用该扩展包, 使用者可以在插件中自定义Mapper接口、Mapper xml 以及对应的实体bean。

源码地址

https://gitee.com/starblues/springboot-plugin-framework-parent

运行环境

jdk1.8+

apache maven 3.6

mavne 仓库地址

https://mvnrepository.com/artifact/com.gitee.starblues/springboot-plugin-framework

快速入门

新建项目。

Maven目录结构下所示

-example

- example-runner

- pom.xml

- example-main

- pom.xml

- example-plugin-parent

- pom.xml

- plugins

- example-plugin1

- pom.xml

- plugin.properties

- example-plugin2

- pom.xml

- plugin.properties

- pom.xml

- pom.xml

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

结构说明:

pom.xml 代表maven的pom.xml

plugin.properties 为开发环境下, 插件的元信息配置文件, 配置内容详见下文。

example 为项目的总Maven目录。

example-runner 在运行环境下启动的模块。主要依赖example-main模块和插件中使用到的依赖包。

example-main 该模块为项目的主程序模块。

example-plugin-parent 该模块为插件的父级maven pom 模块, 主要定义插件中公共用到的依赖, 以及插件的打包配置。

plugins 该文件夹下主要存储插件模块。上述模块中主要包括example-plugin1、example-plugin2 两个插件。

example-plugin1、example-plugin2 分别为两个插件Maven包。

主程序集成步骤

主程序为上述目录结构中的 example-main 模块。

在主程序中新增maven依赖包

com.gitee.starblues

springboot-plugin-framework

${springboot-plugin-framework.version}

1

2

3

4

5

实现并定义配置

实现 com.plugin.development.integration.IntegrationConfiguration 接口。

import com.gitee.starblues.integration.DefaultIntegrationConfiguration;

import org.pf4j.RuntimeMode;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

@Component

@ConfigurationProperties(prefix = "plugin")

public class PluginConfiguration extends DefaultIntegrationConfiguration {

/**

* 运行模式

* 开发环境: development、dev

* 生产/部署 环境: deployment、prod

*/

@Value("${runMode:dev}")

private String runMode;

/**

* 插件的路径

*/

@Value("${pluginPath:plugins}")

private String pluginPath;

/**

* 插件文件的路径

*/

@Value("${pluginConfigFilePath:pluginConfigs}")

private String pluginConfigFilePath;

@Override

public RuntimeMode environment() {

return RuntimeMode.byName(runMode);

}

@Override

public String pluginPath() {

return pluginPath;

}

@Override

public String pluginConfigFilePath() {

return pluginConfigFilePath;

}

/**

* 重写上传插件包的临时存储路径。只适用于生产环境

* @return String

*/

@Override

public String uploadTempPath() {

return "temp";

}

/**

* 重写插件备份路径。只适用于生产环境

* @return String

*/

@Override

public String backupPath() {

return "backupPlugin";

}

/**

* 重写插件RestController请求的路径前缀

* @return String

*/

@Override

public String pluginRestControllerPathPrefix() {

return "/api/plugins";

}

/**

* 重写是否启用插件id作为RestController请求的路径前缀。

* 启动则插件id会作为二级路径前缀。即: /api/plugins/pluginId/**

* @return String

*/

@Override

public boolean enablePluginIdRestControllerPathPrefix() {

return true;

}

public String getRunMode() {

return runMode;

}

public void setRunMode(String runMode) {

this.runMode = runMode;

}

public String getPluginPath() {

return pluginPath;

}

public void setPluginPath(String pluginPath) {

this.pluginPath = pluginPath;

}

public String getPluginConfigFilePath() {

return pluginConfigFilePath;

}

public void setPluginConfigFilePath(String pluginConfigFilePath) {

this.pluginConfigFilePath = pluginConfigFilePath;

}

@Override

public String toString() {

return "PluginArgConfiguration{" +

"runMode='" + runMode + '\'' +

", pluginPath='" + pluginPath + '\'' +

", pluginConfigFilePath='" + pluginConfigFilePath + '\'' +

'}';

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

配置说明:

runMode:运行项目时的模式。分为开发环境(dev)、生产环境(prod)

pluginPath: 插件的路径。开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。

pluginConfigFilePath: 在生产环境下, 插件的配置文件路径。在生产环境下, 请将所有插件使用到的配置文件统一放到该路径下管理。如果启动主程序时, 报插件的配置文件加载错误, 有可能是该该配置不合适导致的。

uploadTempPath: 上传插件包时使用。上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)

backupPath: 备份插件包时使用。备份插件包的路径。默认: backupPlugin(相对于主程序jar路径)

pluginRestControllerPathPrefix: 插件RestController请求的路径前缀

enablePluginIdRestControllerPathPrefix: 是否启用插件id作为RestController请求的路径前缀。启动则插件id会作为二级路径前缀。即: /api/plugins/pluginId/**

配置bean

import com.gitee.starblues.integration.*;

import com.gitee.starblues.integration.initialize.AutoPluginInitializer;

import com.gitee.starblues.integration.initialize.PluginInitializer;

import org.pf4j.PluginException;

import org.pf4j.PluginManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class PluginBeanConfig {

/**

* 通过默认的集成工厂返回 PluginManager

* @param integrationConfiguration 集成的配置文件

* @return

* @throws PluginException

*/

@Bean

public PluginManager pluginManager(IntegrationConfiguration integrationConfiguration) throws PluginException {

IntegrationFactory integrationFactory = new DefaultIntegrationFactory();

return integrationFactory.getPluginManager(integrationConfiguration);

}

/**

* 定义默认的插件应用。使用可以注入它操作插件。

* @return

*/

@Bean

public PluginApplication pluginApplication(){

return new DefaultPluginApplication();

}

/**

* 初始化插件。此处定义可以在系统启动时自动加载插件。

* 如果想手动加载插件, 则可以使用 com.plugin.development.integration.initialize.ManualPluginInitializer 来初始化插件。

* @param pluginApplication

* @return

*/

@Bean

public PluginInitializer pluginInitializer(PluginApplication pluginApplication){

AutoPluginInitializer autoPluginInitializer = new AutoPluginInitializer(pluginApplication);

return autoPluginInitializer;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

插件包集成步骤

插件包pom.xml配置说明

以 provided 方式引入springboot-plugin-framework包

com.gitee.starblues

springboot-plugin-framework

${springboot-plugin-framework.version}

provided

1

2

3

4

5

6

定义打包配置.主要用途是将 Plugin-Id、Plugin-Version、Plugin-Provider、Plugin-Class、Plugin-Dependencies的配置值定义到META-INF\MANIFEST.MF文件中

springboot-plugin-example-plugin1

com.plugin.example.plugin1.DefinePlugin

${project.version}

StarBlues

1.8

UTF-8

UTF-8

3.7.0

3.1.1

org.apache.maven.plugins

maven-compiler-plugin

${maven-compiler-plugin.version}

${java.version}

${java.version}

${project.build.sourceEncoding}

org.apache.maven.plugins

maven-assembly-plugin

${maven-assembly-plugin.version}

jar-with-dependencies

true

true

${plugin.id}

${plugin.version}

${plugin.provider}

${plugin.class}

make-assembly

package

single

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

在插件包的一级目录下新建plugin.properties文件(用于开发环境)

新增如下内容(属性值同步骤1中pom.xml定义的manifestEntries属性一致):

plugin.id=springboot-plugin-example-plugin1

plugin.class=com.plugin.example.plugin1.DefinePlugin

plugin.version=2.0-SNAPSHOT

plugin.provider=StarBlues

1

2

3

4

配置说明:

plugin.id: 插件id

plugin.class: 插件实现类。见步骤3说明

plugin.version: 插件版本

plugin.provider: 插件作者

1

2

3

4

继承 com.gitee.starblues.realize.BasePlugin 包

import com.gitee.starblues.realize.BasePlugin;

import org.pf4j.PluginException;

import org.pf4j.PluginWrapper;

public class DefinePlugin extends BasePlugin {

public DefinePlugin(PluginWrapper wrapper) {

super(wrapper);

}

@Override

protected void startEvent() throws PluginException {

}

@Override

protected void deleteEvent() throws PluginException {

}

@Override

protected void stopEvent() {

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

并且将该类的包路径(com.plugin.example.plugin1.DefinePlugin)配置在步骤1和2的plugin.class属性中。

新增HelloPlugin1 controller

此步骤主要验证环境是否加载插件成功。

@RestController

@RequestMapping(path = "plugin1")

public class HelloPlugin1 {

@GetMapping()

public String getConfig(){

return "hello plugin1"

}

}

1

2

3

4

5

6

7

8

9

10

11

12

运行配置

配置模块 example-runner 的pom.xml

将主程序的依赖新增到pom.xml 下

将插件中的依赖以 provided 方式引入到 pom.xml 下

如下所示:

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">

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.0.3.RELEASE

com.gitee.starblues

plugin-example-runner

2.0-RELEASE

pom

2.8.2

com.gitee.starblues

plugin-example-start

${project.version}

com.google.code.gson

gson

${gson.version}

provided

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

设置idea的启动

Working directory : D:\xx\xx\springboot-plugin-framework-parent\plugin-example

Use classpath of module: plugin-exampe-runner

勾选: Include dependencies with “Provided” scope

启动2步骤的配置。

观察日志出现如下说明加载插件成功。

Plugin 'springboot-plugin-example-plugin1@2.0-RELEASE' resolved

Start plugin 'springboot-plugin-example-plugin1@2.0-RELEASE'

Init Plugins Success

1

2

3

访问插件中的Controller 验证。

浏览器输入:http://ip:port/api/plugins/springboot-plugin-example-plugin1/plugin1

响应并显示: hello plugin1

说明集成成功!

使用说明

插件中定义配置文件

在插件包的 resources 目录下定义配置文件 plugin1.yml

name: plugin1

plugin: examplePlugin1

setString:

- set1

- set2

listInteger:

- 1

- 2

- 3

subConfig:

subName: subConfigName

1

2

3

4

5

6

7

8

9

10

11

在代码中定义对应的bean

import com.gitee.starblues.annotation.ConfigDefinition;

import java.util.List;

import java.util.Set;

@ConfigDefinition("plugin1.yml")

public class PluginConfig1 {

private String name;

private String plugin;

private Set setString;

private List listInteger;

private String defaultValue = "defaultValue";

private SubConfig subConfig;

// 自行提供get set 方法

}

public class SubConfig {

private String subName;

public String getSubName() {

return subName;

}

// 自行提供get set 方法

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

该bean必须加上 @ConfigDefinition(“plugin1.yml”) 注解。其中值为插件文件的名称。

其他地方使用时, 可以通过注入方式使用。

例如:

@Component("plugin2HelloService")

public class HelloService {

private final PluginConfig1 pluginConfig1;

private final Service2 service2;

@Autowired

public HelloService(PluginConfig1 pluginConfig1, Service2 service2) {

this.pluginConfig1 = pluginConfig1;

this.service2 = service2;

}

public PluginConfig1 getPluginConfig1(){

return pluginConfig1;

}

public String sayService2(){

return service2.getName();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

注意事项

在开发环境:配置文件必须放在resources目录下。并且@ConfigDefinition(“plugin1.yml”)中定义的文件名和resources下配置的文件名一致。

在生产环境: 该文件存放在pluginConfigFilePath配置的目录下。

集成扩展

SpringBoot Mybatis 扩展

文档见: springboot-plugin-framework-extension-mybatis

案例部署

普通例子运行见:package/example

windows环境下运行: package.bat

linux、mac 环境下运行: package.sh

mybatis 案例部署

普通例子运行见:package/example-persistence

windows环境下运行: package.bat

linux、mac 环境下运行: package.sh

sql在 plugin-example-persistence/sql 文件夹下。

生产环境目录

-main.jar

-main.yml

-plugins

-plugin1.jar

-plugin2.jar

-pluginFile

-plugin1.yml

-plugin2.yml

1

2

3

4

5

6

7

8

9

10

11

12

案例说明

plugin-example:插件基础功能案例。

plugin-example-persistence: 针对Mybatis集成的案例。

生产环境配置禁用启用功能

启用、禁用功能

1.在插件目录下新建 enabled.txt 文件

2.enabled.txt的内容为:

########################################

# - 启用的插件

########################################

example-plugin1

1

2

3

4

将需要启用的插件id配置到文件中。

所有注释行(以#字符开头的行)都将被忽略。

禁用功能

1.在插件目录下新建 disabled.txt 文件

2.disabled.txt的内容为:

########################################

# - 禁用的插件

########################################

example-plugin1

1

2

3

4

将需要启用的插件id配置到文件中。

所有注释行(以#字符开头的行)都将被忽略。

注意事项

插件中代码编写完后, 请保证在class文件下的类都是最新编译的, 再运行主程序, 否则会导致运行的插件代码不是最新的。

如果启动时插件没有加载。请检查配置文件中的 pluginPath

如果pluginPath 配置为相当路径,请检查是否是相对于当前工作环境的目录。

如果pluginPath配置为绝对路径,请检查路径是否正确。

1

2

3

小技巧

idea 启动主程序时, 自动编译插件包的配置

选择

File->Project Structure->Project Settings->Artifacts->点击+号->JAR->From modules whith dependencies->选择对应的插件包->确认OK

启动配置:

在Before launch 下-> 点击小+号 -> Build ->Artifacts -> 选择上一步新增的>Artifacts

版本更新

1.1 版本

新增插件注册、卸载监听器。

新增可通过 PluginUser 获取插件中实现主程序中定义的接口的实现类。

新增插件注册、卸载时监听器。

2.0 版本(重大版本更新)

重构代码。

新增扩展机制。

简化依赖注入注解, 保持与SpringBoot依赖注入方式一致。

新增插件工厂监听器、新增插件初始化监听器(适用于第一次启动)。

新增插件包Mybatis的集成, 可在插件包中独立定义Mapper接口、Mapper xml、实体bean。

---------------------

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java 插件式架构_springboot插件式开发框架 的相关文章

  • n个数分为两组,两组数的个数尽可能相等,差值最小

    题目描述 对于有n个数的数组 分为两组 这两组的数的个数尽可能相等 不超过1 同时两组的数之和的差值最小 这个题目使用类似0 1背包问题 思路 从k个数中选i个数 求所有可能的和 并把这些和放在flag中用true表示 k i flag见代
  • ubuntu忘记root密码怎么办?

    普通用户 无论你是否申请了root帐号 或是普通账号密码忘记了都没有问题的 首先 重启ubuntu 随即长按shift进入grub菜单 其次 选择第二个高级模式recovery mode进入Recovery Menu界面 选择root Dr
  • jsp代码中EL表达式无法显示(已解决)

    jstl最近是不是有问题 还是我的代码有问题 每次遍历都不会出来 都是显示这样 有的说是必须要有jstl jar和 standard jar 但是我也有了 但是也是没有说打刀口上 其实是忽略了一个知识点 EL表达式 在这里加上 isELIg
  • CUnit 单元测试 方法总结

    CUnit是一个用C语言编写 管理和运行单元测试的轻量级系统 它为C程序员提供了基本的测试功能和灵活的各种用户接口 CUnit被构建为一个与用户的测试代码链接的静态库 它使用一个简单的框架来构建测试结构 并为测试常见数据类型提供了一套丰富的
  • Java 多线程 --- 终止线程 Terminate Threads

    Java 多线程 终止线程 Terminate Threads 为什么要终止线程 终止线程的方法 return stop interrupt InterruptedException 为什么要终止线程 线程消耗资源 包括内存 内核 CPU等
  • 非常详细的51单片机引脚介绍

    引用cy pp 的 非常详细的51单片机引脚介绍 T89C2051是精简版的51单片机 精简掉了P0口和P2口 只有20引脚 但其内部集成了一个很实用的模拟比较器 特别适合开发精简的51应用系统 毕竟很多时候我们开发简单的产品时用不了全部3
  • Qt编写控件属性设计器-用户属性

    一 前言 用户属性是后面新增加的一个功能 自定义控件如果采用的Q PROPERTY修饰的属性 会自动识别到属性栏中 这个一般称为控件属性 在组态设计软件中 光有控件本身的控件属性还是不够的 毕竟这些属性仅仅是以外观为主 并不能表示某个设备的
  • c#监测文件的类---FileSystemWatcher

    本文转载自 http blog csdn net jason ldh article details 9972801 FileSystemWatcher控件主要功能 监控指定文件或目录的文件的创建 删除 改动 重命名等活动 可以动态地定义需
  • 带头结点和尾节点的双向链表的(增删改查,头遍历,尾遍历)java实现

    废话不多说 请看代码 测试输出 代码 package com coderman dataStruct 双链表 Author zhangyukang Date 2020 2 18 19 00 Version 1 0 class DoubleL
  • PyTorch使用LMDB数据库加速文件读取

    PyTorch使用LMDB数据库加速文件读取 文章目录 PyTorch使用LMDB数据库加速文件读取 背景介绍 具体操作 LMDB主要类 lmdb Environment lmdb Transaction Imdb Cursor 操作流程
  • mybatisPlus-wrapper使用

    创建测试类 import com baomidou mybatisplus core conditions query QueryWrapper import com plus mybatis mapper UserMapper impor
  • 思科模拟器静态路由

    网络拓扑图 Router0配置 Router2配置 Multilayer Switch0配置 Switch0配置 Switch2配置 主机PC0配置 202 199 1 2 255 255 255 0 202 199 1 1 主机PC1配置
  • LDAP: error code 32 - No Such Object

    使用spring ldap创建节点的时候报错 LDAP error code 32 No Such Object 是在调用 this ldapTemplate create ldapUser 的时候报的错 找了半天没有发现原因 最后看到一篇
  • JavaScript整理

    第一章 JavaScript概述 1 1 JavaScript简介 JavaScript 简称js 是一种直译式脚本语言 是一种动态类型 弱类型 基于原型的语言 内置支持类型 它的解释器被称为JavaScript引擎 为浏览器的一部分 广泛
  • ajax请求和普通请求的区别

    当浏览器按照window location href index html 进行定期请求时 会清除当前窗口并将服务器响应加载到窗口中 使用ajax请求 当前窗口 文档不受影响 JavaScript代码可以检查请求的结果 并使用这些结果执行所
  • 一些黑科技接口钩子 钉钉,禅道,gitlab,jenkins等

    日常工作中需要做流程的串联 这个时候就需要掌握一些黑科技接口 这些接口甚至是官方文档上并没有提供的 但是我们确实可以使用 进行内部工具开发的一定要记得提供钩子 没有钩子 做不了朋友 钉钉相关 钉钉群中的自定义机器人 curl https o
  • C/C++在线编译器

    一直以来都喜欢用手机看书 尤其是在上班时 看的最多的是编程一类的书 主要是C 看着就想写写代码 可是电脑用不能用 怎么办 于是想到用UC浏览器找找看网上有没有在线的编译器 想什么时候写代码都可以验证 于是就找了几个 各有千秋吧 中文的我没找
  • 在地址栏中输入一段内容,接下来都发生了些什么

    用户发出 URL 请求到页面开始解析的这个过程 就叫做导航 用以定位到新资源 并且将老的资源从页面卸载 一 用户输入 地址栏首先判断输入的内容是搜索内容还是符合url规则的url 如果是搜索内容的话 浏览器会拼接上该搜索内容形成一个新的ur
  • mybatis-plus

    mybatis plus的sql拼接规则 实体对象参数属性有值 那么该属性名会拼接sql语句中 实体对象参数属性为基本属性时 会有默认值 mybatis plus认为是有值 参与sql拼接 mybatis plus与mybatis的对比 m

随机推荐

  • 3d指向检测 ros_3d_pointing_detection

    Introduction The workflow of this project is Detect 2D human joints from color image by Openpose Compute 3D human joints
  • stopPropagation, preventDefault 和 return false 的区别

    http blog csdn net bkq421511585 article details 14166789
  • 使用Docker拉起ES容器和Kibana容器并设置密码Demo

    1 准备条件 安装好docker 在同一台服务器上安装es和kibana 安装docker命令参考 可以按顺序执行如下命令安装 1 sudo yum install y yum utils 2 sudo yum config manager
  • 做擦边网站 服务器放在狗爹,在GoDaddy搭建Prosper202服务器

    记录一下我在GoDaddy搭建Prosper202服务器的过程 1 首先 我购买的是Liunx Deluxe共享虚拟主机 狗爹这个类型的产品可以建多个网站 我有一个域名 www网站已经上线 虽然还没有什么内容 2 为你的Prosper202
  • add_library使用 $<TARGET_OBJECTS:name>

    一 背景 前面介绍了add library的两种格式 今天分享一个实例 Cmake分别生成静态链接库 OBJ链接库 并使用
  • 人人商城小程序消息服务器配置,人人商城小程序订阅消息设置方法和几个坑!...

    操作步骤 第一步 开通订阅消息功能 登录微信小程序官网后台 mp weixin qq com 开通订阅消息 第二步 服务类目 新增 商家自营 gt 服装 鞋 箱包 第三步 添加订阅消息 4个 订阅消息 公共模板库 搜索 订单支付成功通知 编
  • Android仿小米商城底部导航栏(基于BottomNavigationBar)

    简介 现在大多数App都会用到底部导航栏 比如QQ 微信和购物App等等 有了底部导航栏 用户可以随时切换界面 查看不同的内容 Android底部导航栏的实现方式特别多 例如TabHost TabLayout 或者TextView等 都可以
  • 机器学习-支持向量机算法实现与实例程序

    一 SMO算法基础 支持向量就是离分隔超平面最近的那些点 分隔超平面是将数据集分开来的决策边界 支持向量机将向量映射到一个更高维的空间里 在这个空间里建立有一个最大间隔超平面 在分开数据的超平面的两边建有两个互相平行的超平面 建立方向合适的
  • 剑指offer总结

    时间复杂度一般比空间复杂度更重要 因为改进时间对算法的要求更高 是空间换时间 还是时间换空间 一般要看具体的应用 对于普通的应用 一般是空间换时间 因为普通用户更关心速度 而且一般有足够的存储空间允许这样操作 对于嵌入式的软件 一般我们会用
  • 简说C++学习-第一章C++语言概述

    简说C 学习 第一章C 语言概述 1 C 语言的发展 1972年 贝尔实验室在B语言的基础上 做了进一步的充实和完善 设计出了C语言 C语言的优点 语言简洁 使用灵活 方便 具有丰富的运算符和数据类型 可以进行低级操作 适合开发系统软件 程
  • Java jdbc实现多表查询

    数据库中的一张表对应Java中的一个类 我这里示例的是学生类 老师类 成绩类 还有一个用于存储多表查询结果后的SelectAll类 public class Student 学生表 private Integer id 学生编号 priva
  • sublime text 3下载与安装详细教程

    一 下载 打开官网下载链接http www sublimetext com 3 下载Sublime Text 3 portable version 下载下来为 Sublime Text Build 3083 x64 zip 编辑器的包 解压
  • Linux 中统计指定目录下同一类文件总的大小

    root PC1 test ls a map a ped a txt b ped b txt root PC1 test ll h total 1 4G rw r r 1 root root 200M Dec 1 19 42 a map r
  • Tensorflow-2-Tensorboard使用

    一 概述 机器学习如此复杂 训练模型的时候 摸不清背后到底是如何运行的 自己设置的参数和关键变量 如果能看到在训练时的变化情况 可以为后面的参数调优阶段提供很大的便利 Tensorboard就是这样一个工具 它刻意将模型抽象成图像 tens
  • UmiJS基础+UmiUI安装使用+Mock使用示例+DvaJS案例

    title UmiJS基础 date 2022 09 12 19 20 27 tags React 框架 UmiJS categories 框架 UmiJS 介绍 官方文档 可扩展 Umi 实现了完整的生命周期 并使其插件化 Umi 内部功
  • 问题(02)Message 消息提示每次只弹出1个,不能同时出现2个

    项目场景 PC端开发 vue elementUI 问题描述 Message 消息提示同时出现2个 原因分析 Element UI的Message消息提示是点击一次触发一次的 解决方案 在utils文件创建resetMessage js re
  • Jar包中Class文件替换

    1 查找替换的class的具体路径 jar tvf jar grep class 根据自己的jar包和类名替换 2 根据第一步查到的class的具体路径解压出来对应文件 jar xvf jar class 3 替换解压出来的文件中的clas
  • 代码制作数字流星雨_用C语言编写流星雨程序

    展开全部 数字流星雨代码 流星雨 cpp Defines the entry point for the console application 程序名称 数字流星雨 最后修改e5a48de588b632313133353236313431
  • Python 判断生肖

    Python 判断年份干支纪年及生肖 生肖 12年一循环 干支纪年法 60年一循环 十天干 甲 乙 丙 丁 午 戊 庚 辛 壬 癸 十二地支 子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥 十二生肖 鼠 牛 虎 兔 龙 蛇 马 羊 猴 鸡
  • java 插件式架构_springboot插件式开发框架

    介绍 该框架主要是集成于springboot项目 用于开发插件式应用的集成框架 核心功能 插件配置式插拔于springboot项目 在springboot上可以进行插件式开发 扩展性极强 可以针对不同项目开发不同插件 进行不同插件jar包的