Idea 项目
使用 idea 的项目结构搭建开发环境,此方法简单,但是项目只能用在 Idea 上,在其它编辑器如 vs code 不通用。
使用 Idea 新建项目,构建系统选择 Intellij,jdk建议选择1.8,如果选择更高版本的jdk,项目语言级别要设置为8

需要在项目结构中添加3个依赖,classbean(ecology 应用代码)、lib(ecology 依赖)、resin-lib(resin 依赖)

**classbean 依赖获取方法:**
`ecology/classbean` 文件夹内的全部文件打包成 zip,然后更改文件类型为 jar,注意打包时不要将 classbean 打包进去,只打包里面的文件夹,然后将此jar包添加到项目依赖中。
**lib 依赖获取方法:**
可直接添加 `ecology/WEB-INF/lib` 文件夹到项目依赖(不推荐),建议复制 `ecology/WEB-INF/lib `文件夹到其它地方,然后再将此文件夹添加到项目依赖,这样可以避免添加的依赖中原本产品的依赖和自己放进去的依赖包混淆。
**resin-lib 依赖获取方法:**
直接添加 Resin/lib 文件夹到项目依赖
添加完这3个依赖后,记得将这3个依赖添加到模块依赖。
如果需要添加第三方依赖,可以将依赖添加到项目结构中,并将 jar 包放入 `ecology/WEB-INF/lib`文件夹内。

Maven 项目搭建
不推荐使用 Maven 搭建开发环境,因为 Ecology 有许多 jar 包,maven只支持将单个 jar 包添加到仓库,添加依赖会非常麻烦。
如果要搭建 maven 项目,可参考此文章:泛微在线文档,注意,此文章中的 maven 项目搭建并不可靠,并未说明依赖 jar 包的添加方法。
Gradle 项目搭建
这是我目前所使用的方式,它的好处是通用性强,所有支持 gradle 的编辑器都适用,包括 vs code,如果需要 AI 写代码,比如用 Cursor 写代码,这是比较好的选择,但缺点是复杂性高,需要会 Gradle。
使用 Idea 创建项目,构建选择 gradle。
创建项目后 idea 会自动下载 Gradle 包,此过程会比较小,受国内网络环境影响可能会下载失败。

下载完 Gradle 包之后就会自动生成 Gradle 项目结构。
gradle 配置大体有几点:
- 指定 java 版本为 1.8
- jar 包命名规则为:seconddev-模块名称
- 所有模块依赖 common 模块的代码
- 配置依赖路径
- 测试使用 Mockito + Junit5
build.gradle 代码如下:
group 'com.weaver.seconddev'
version '1.0.0'
description 'e9二开'
// 应用到所有项目(包括根项目)
allprojects {
apply plugin: 'java'
// java 配置,源码和编译后语言版本都为 1.8
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
// java 编译配置,源码和编译后语言版本都为 1.8
compileJava {
options.encoding = 'UTF-8'
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
// jar 包生成命名规则
jar {
archiveBaseName = "seconddev-" + project.name
}
repositories {
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenLocal()
mavenCentral()
}
}
// 应用到子模块
subprojects { subproject ->
// 除 common 模块外,其它子模块都引入 common 模块的源码
if (subproject.name != 'common') {
dependencies {
implementation project(':common')
}
}
// sourceSets {
// main {
// resources {
// exclude '**'
// }
// }
// }
jar {
manifest {
attributes 'weaver-ecode-seconddev-id': 'hnweaver-' + rootProject.version,
'Implementation-Version': rootProject.version,
'Implementation-Vendor-Id': rootProject.group,
'Implementation-Title': rootProject.name
}
}
// 添加公共的依赖
dependencies {
compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
testImplementation("junit:junit:4.12")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.3")
testImplementation("org.junit.platform:junit-platform-launcher:1.13.3")
testImplementation("org.mockito:mockito-core:5.18.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.18.0")
// 项目不建议引入 powermock 依赖,之所以引入是因为要兼容以前使用 powermock 的代码,请使用 mockito
testImplementation("org.powermock:powermock-api-mockito2:2.0.9")
testImplementation("org.powermock:powermock-module-junit4:2.0.9")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.13.3")
// e9自有的依赖,放到模块 /lib 目录下
implementation fileTree(dir: rootProject.projectDir.getPath() + '/lib', includes: ['**/*.jar', '**/*.class'])
implementation fileTree(dir: subproject.projectDir.getPath() + '/lib', includes: ['**/*.jar', '**/*.class'])
// 项目二开自定义依赖,放到模块 /out-dep 目录下
implementation fileTree(dir: subproject.projectDir.getPath() + "/out-dep", includes: ['**/*.jar', '**/*.class'])
}
// 代码测试配置,使用 JUnit5
test {
jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED'
jvmArgs '--add-opens', 'java.base/java.util=ALL-UNNAMED'
jvmArgs '--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED'
jvmArgs '--add-exports', 'java.base/com.sun.crypto.provider=ALL-UNNAMED'
useJUnitPlatform {
includeEngines 'junit-jupiter', 'junit-vintage'
}
}
}
更改了 gradle.build 文件后,记得重新加载 gradle 项目
在项目下创建 lib 文件夹,将 `ecology/WEB-INF/lib` 和 `Resin/lib` 内的所有jar包都复制到该文件夹内,以及将打包的 classbean.jar 包放到该文件夹内,作为项目依赖。
如果需要第三方依赖,可在模块内创建 out-lib 文件夹,将第三方依赖 jar 包放到该文件夹内,或者在 gradle.build 文件内添加第三方依赖。
**项目结构:**
不建议在项目根目录内的 src 文件夹写代码,而是分多个子模块,例如以下的项目结构,common 模块存放公共代码,供其它模块使用,可根据团队项目规范,按所开发的系统或开发的模块功能,创建子模块,例如下面的结构中 Liuzhousteel-dev 为柳钢OA的开发代码,Sgmw-dev 为上通五的开发代码。

添加子模块后,需要在根目录下的 settings.gradle 文件添加新创建的模块,这样 gradle 才能识别心创建的模块,文件内容例如:
rootProject.name = 'E9SecondDev'
// 以下都为子模块名称,与模块文件夹名称相同
include 'common'
include 'demo'
include 'Liuzhou-city-cology-dev'
include 'Liuzhousteel-dev'
include 'second-dev'
include 'Sgmw-dev'
include 'Shida-dev'
include 'Wlnev-dev'
include 'Wuling-dev'
include 'Wuling-gy-dev'
子模块里还能继续创建子模块
子项目
如果将所有客户的代码都放在一个项目中,后面项目会越来越大,导致编辑器卡顿。那么可以在当前项目下创建子项目,并引入根项目中的依赖与 Gradle 配置,这样这个子项目就可以使用编辑器单独打开,并作为一个完整的 Gradle 项目。
**创建子项目:** 可以在根项目中创建子项目,例如:根项目/SonProjects/客户项目,创建项目时你需要在 IDEA 等编辑器中进行新建项目,构建工具选择 Gradle。
创建子项目后,修改 `build.gradle` 文件,修改为:
// 创建变量,指定根项目路径
ext.e9RootDir = file('../..')
// 引入根项目的 gradle 配置
apply from: file('../../build.gradle')
// 添加单元测试配置
allprojects {
tasks.withType(Test).configureEach {
useJUnitPlatform()
filter {
failOnNoMatchingTests = false
}
}
}
`setting.gradle` 文件修改为:
rootProject.name = 'LiuzhouMedicine-dev'
include ':common', ':functions', ':functions:extend',":functions:convenient-tool"
project(':common').projectDir = file('../../common')
project(':functions').projectDir = file('../../functions')
project(':functions:extend').projectDir = file('../../functions/extend')
project(':functions:convenient-tool').projectDir = file('../../functions/convenient-tool')
子项目的 `setting.gradle` 中需要引入根项目的 common 等模块,才能在子项目中引入 common 等模块的依赖,并配置模块的路径映射。
**根项目配置:** 在根项目中的 `build.gradle` 中修改配置
在 `allprojects` 后面添加:
def e9RootDir = rootProject.hasProperty('e9RootDir')
? rootProject.e9RootDir
: rootProject.projectDir
// 主工程只配置各子模块;作为独立工程引入时,还要配置当前根工程本身。
// 此时 e9RootDir 由 SonProjects/LiuzhouMedicine-dev/build.gradle 注入。
def configuredProjects = rootProject.hasProperty('e9RootDir')
? ([project] + subprojects)
: subprojects
configure(configuredProjects) { subproject ->
dependencies {
implementation fileTree(
dir: new File(e9RootDir, 'lib'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(subproject.projectDir, 'lib'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(e9RootDir, 'out-dep'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(subproject.projectDir, 'out-dep'),
includes: ['**/*.jar', '**/*.class']
)
// 参数校验
implementation("jakarta.validation:jakarta.validation-api:3.1.1")
implementation('org.hibernate.validator:hibernate-validator:8.0.1.Final')
implementation('org.glassfish:jakarta.el:4.0.2')
compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
testImplementation("junit:junit:4.12")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.3")
testImplementation("org.junit.platform:junit-platform-launcher:1.13.3")
testImplementation("org.mockito:mockito-core:5.18.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.18.0")
// 项目不建议引入 powermock 依赖,之所以引入是因为要兼容以前使用 powermock 的代码,请使用 mockito
testImplementation("org.powermock:powermock-api-mockito2:2.0.9")
testImplementation("org.powermock:powermock-module-junit4:2.0.9")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.13.3")
}
}
// 独立打开 SonProjects/LiuzhouMedicine-dev 时,当前工程是根项目而非
// subprojects 的成员;显式补上主工程中原本由 subprojects 配置的公共模块依赖。
if (rootProject.hasProperty('e9RootDir')) {
dependencies {
implementation project(':common')
implementation project(':functions:extend')
}
jar {
from(project(':common').sourceSets.main.output.classesDirs.files)
from(project(':functions:extend').sourceSets.main.output.classesDirs.files)
}
}
这些配置定义了根项目和子项目的公共依赖,在 `subprojects` 中不需要有依赖配置了,可以把原先的依赖配置删掉。
完整的根项目 `build.gradle` 内容:
group 'com.weaver.seconddev'
version '1.0.0'
description 'e9二开'
// 应用到所有项目(包括根项目)
allprojects {
apply plugin: 'java'
// java 配置,源码和编译后语言版本都为 1.8
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
// java 编译配置,源码和编译后语言版本都为 1.8
compileJava {
options.encoding = 'UTF-8'
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
// jar 包生成命名规则
jar {
archiveBaseName = "weaver-seconddev-" + project.name
}
repositories {
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenLocal()
mavenCentral()
}
}
def e9RootDir = rootProject.hasProperty('e9RootDir')
? rootProject.e9RootDir
: rootProject.projectDir
// 主工程只配置各子模块;作为独立工程引入时,还要配置当前根工程本身。
// 此时 e9RootDir 由 SonProjects/LiuzhouMedicine-dev/build.gradle 注入。
def configuredProjects = rootProject.hasProperty('e9RootDir')
? ([project] + subprojects)
: subprojects
configure(configuredProjects) { subproject ->
dependencies {
implementation fileTree(
dir: new File(e9RootDir, 'lib'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(subproject.projectDir, 'lib'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(e9RootDir, 'out-dep'),
includes: ['**/*.jar', '**/*.class']
)
implementation fileTree(
dir: new File(subproject.projectDir, 'out-dep'),
includes: ['**/*.jar', '**/*.class']
)
// 参数校验
implementation("jakarta.validation:jakarta.validation-api:3.1.1")
implementation('org.hibernate.validator:hibernate-validator:8.0.1.Final')
implementation('org.glassfish:jakarta.el:4.0.2')
compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
testImplementation("junit:junit:4.12")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.3")
testImplementation("org.junit.platform:junit-platform-launcher:1.13.3")
testImplementation("org.mockito:mockito-core:5.18.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.18.0")
// 项目不建议引入 powermock 依赖,之所以引入是因为要兼容以前使用 powermock 的代码,请使用 mockito
testImplementation("org.powermock:powermock-api-mockito2:2.0.9")
testImplementation("org.powermock:powermock-module-junit4:2.0.9")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.13.3")
}
}
// 独立打开 SonProjects/LiuzhouMedicine-dev 时,当前工程是根项目而非
// subprojects 的成员;显式补上主工程中原本由 subprojects 配置的公共模块依赖。
if (rootProject.hasProperty('e9RootDir')) {
dependencies {
implementation project(':common')
implementation project(':functions:extend')
}
jar {
from(project(':common').sourceSets.main.output.classesDirs.files)
from(project(':functions:extend').sourceSets.main.output.classesDirs.files)
}
}
// 应用到子模块
subprojects { subproject ->
// 除 common 模块外,其它子模块都引入 common 模块的源码
if (subproject.name != 'common') {
dependencies {
implementation project(':common')
}
jar {
// 合并打包公共模块的class文件
from(project(":common").sourceSets.main.output.classesDirs.files)
}
}
if (subproject.name != 'extend' && subproject.name != 'common') {
dependencies {
implementation project(':functions:extend')
}
jar {
from(project(":functions:extend").sourceSets.main.output.classesDirs.files)
}
}
// 忽略所有资源文件
sourceSets {
main {
resources {
exclude '**'
}
}
}
jar {
manifest {
attributes 'weaver-ecode-seconddev-id': 'hnweaver-' + rootProject.version,
'Implementation-Version': rootProject.version,
'Implementation-Vendor-Id': rootProject.group,
'Implementation-Title': rootProject.name
}
}
// 代码测试配置,使用 JUnit5
test {
jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED'
jvmArgs '--add-opens', 'java.base/java.util=ALL-UNNAMED'
jvmArgs '--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED'
jvmArgs '--add-exports', 'java.base/com.sun.crypto.provider=ALL-UNNAMED'
useJUnitPlatform {
includeEngines 'junit-jupiter', 'junit-vintage'
}
}
}
**现在你可以:**
- 使用编辑器单独打开子项目
- 子项目中使用根项目的依赖与 gradle 配置,无需再进行依赖添加,保持依赖公共模块
开发文件打包
方式一:将编译后的 class 文件放入 ecology/classbean 内(不推荐)
此方式不推荐,容易造成漏打文件,还和 Ecology 本身的代码混合在一起,不好维护。

方式二:作为 jar 包放入(推荐)
可将模块内的代码打包成 jar 包,然后放入 `ecology/WEB-INF/lib` 目录下,jar包命名建议以 seconddev 开头命名,区分 ecology 内的原本 jar 包。此打包方式方便,不需要注意哪个文件发生了更改,但要注意如果一个模块有多个人同时开发,打包时放入的 jar 包不要和之前的 jar 包冲突,比如漏掉了另一个人写的代码。
这种方法也方便维护,如果出现问题不确定是标准产品引起还是二开引起,把二开 jar 包去除就能判断。
无论哪种方式,打包后都需要重启系统,让开发文件生效。
Debug 断点调试
允许在编辑器中打断点调试,不允许在生产环境上调试。
需要开启调试端口:
修改配置文件
resin/conf/resin.properties的jvm_args属性最后面增加:
-Xdebug -Xrunjdwp:transport=dt_socket,address=9088,server=y,suspend=n -Dcom.sun.management.jmxremote -Xloggc:/var/log/resin_gc.log
此时配置的调试端口为:9088
然后重启 Resin 服务
在 idea 中配置调试,点击右上角的编辑配置

点击添加远程jvm调试

编辑配置,端口填入 9088 ,这是调试端口,也就是上面配置的端口,jdk 选择 5-8,模块类路径选择当前调试的模块,确定后就可以了。

运行调试后就能打断点。
热部署
debug 调试的附带功能就是热部署,允许在不重启应用的情况下将代码部署到系统中,让代码立即生效,但它有局限性,只支持代码块的更改,比如方法内的代码块,对于创建新的类、新的方法是没有用的。
在 Idea 启动 Ecology
可以在 Idea 中启动 Ecology ,可用于代码调试,我觉得这没什么用,用正常方式启动 Ecology 就行了,代码调试可以使用上面章节中的 Debug 进行调试。
配置步骤
1.新建 Resin 配置
配置应用程序服务器,选择 resin 所在的位置
http 端口配置没有使用的端口


在 idea 启动 Resin 后进入系统无法显示页面原因(Mac系统)
服务已启动,但是显示不了页面,页面空白
查看 stderr.log 日志,显示报错 :com.caucho.jsp.JspParseException: javac: 无效的标�?:

这是一个无法编译jsp文件的报错,原因是 idea 的应用程序名中含有空格,把空格去掉即可
