简介:JavaEE SDK 7u3 是一套面向 Windows 7 环境的企业级 Java 开发套件,主要服务于需要在旧操作系统上构建、测试和部署企业应用的开发者,能够提供稳定可靠的底层运行与编译环境。该版本以 Glassfish 4.0 作为 Java EE 7 参考实现,涵盖 Web 服务、事务处理、数据库连接、消息传递等标准服务,并引入 WebSocket、CDI 等较新特性,可配合 NetBeans IDE 的代码自动完成与调试工具进行高效开发。压缩包大小约一百二十六兆字节,内含整套 SDK 组件与应用服务器相关文件,适合遗留项目维护或对特定版本存在硬性依赖的团队选用。目前已有三百三十一人学习下载,说明这一老版本在兼容性场景下仍具备实际参考价值。对于希望获得稳定开发环境、又暂时无法升级到新版的企业开发者,这份套件能直接支撑起日常开发、测试与部署工作,减少环境迁移带来的成本。 我的博客结构设计大致如下:
1. javaeesdk 7u3 不是考古,是一台还在转的老发动机
(引入,无子标题)
2. 拆开 7u3 的包:SDK 里有什么,为什么选它而不是单下 Web Profile
2.1 参考实现:SDK 的核心才是“标准答案”
2.2 解压安装与第一个 domain:从压缩包到管理控制台
3. 把 7u3 接进日常开发:命令行、环境变量与 Maven 坐标
3.1 asadmin 管理命令与连接参数:先让脚本跑通
3.2 构建工具与 IDE:从 Maven 坐标到自动部署
4. 部署路径:把一个 WAR 落到 7u3 的 domain 上
4.1 最小 WAR 部署:asadmin 部署命令与常用参数
4.2 连接池与 JNDI 数据源:让应用真正连上数据库
5. 7u3 排查手册:旧 SDK 撞上新环境的 5 个典型坑
5.1 高版本 JDK 下启动失败:模块系统与反射调用冲突
5.2 改了内存参数却不生效:set 命令与 domain.xml 的优先级
5.3 HTTPS 握手失败:TLS 协议版本和证书链过期
5.4 端口被占与 domain 锁:第二个实例怎么都起不来
5.5 中文乱码:从请求到数据库的完整链路
6. 让 7u3 少闹腾:启动参数、systemd 托管与备份习惯
(最后一章,落到具体技巧上,包含表格,收尾用第一人称习惯+希望帮到你)
现在开始写具体内容。我需要确保每个中间章节至少有1800字。由于中文一行约等于一个字符宽度的问题,我数一下大概:
作者经验法:在简体中文字渲染里,每行约20-25个字符(带宽度),4-6行约80-150字。所以每段应该80-150字。我的内容需要比较充实。
我预计合理成稿时,总体在8000-9000字左右。
写完的过程我需要注意语言的自然和可读性。避免it语言里的翻译腔。
我现在准备写草稿。由于体量很大,我会分段写。
第2章细节要点:
- Java EE 7 标准 + 参考实现概念
- Full Platform vs Web Profile 选择
- SDK 包内容:bin、domains、docs、samples
- 解压安装步骤 + 环境变量 + 启动 + 验证
- Windows/Linux 差异注意
第3章细节要点:
- asadmin 常用命令体系
- --passwordfile 和远程管理
- Maven 坐标 javax:javaee-api:7.0 provided
- IDE命令替代方案
- autodeploy 目录原理
- 编译级别注意事项
第4章细节要点:
- mvn package
- asadmin deploy 参数
- list-applications 验证
- 从日志看部署错误
- 创建连接池、JNDI 资源
- ping 验证
- 驱动放置和常见错误
第5章细节要点:
- JDK 版本问题
- 内存参数失效
- HTTPS 证书问题
- 端口冲突
- 中文乱码
注意第5章标题是“7u3 排查手册:旧 SDK 撞上新环境的 5 个典型坑”,包含“排查”安全,符合要求。
第6章细节要点:
- JVM 参数整理建议(表)
- systemd 托管
- 备份清单(表)
- 迁移习惯
我需要不太使用特殊键词,比如“稳定”字眼尽可能多用而不是“墙”等……注意没有风险。
有一个风险:在正文中我提到“mx”内存设置,多少合适。我会给建议,而不是精确程序化数值。
好,动手写全文。
整个写作我仔细计算字数不是必要的,但要保持在7000-12000字。我会把中间章内容写厚。
写作时注意:
- 代码块后跟逻辑说明和参数说明
- 不要写“本文”等字眼,直接叙述
- 避免禁用词
- 每章暗中尽量有连接词“我一般会”“常见做法是”
- 最后一章收尾“希望帮到你”
开始动手。 ## 1. javaeesdk 7u3 不是考古,是一台还在转的老发动机
javaeesdk 7u3 全称 Java EE 7 SDK Update 3,核心是连同参考实现 GlassFish 4.0 一起打包的一套完整 SDK。它跟 JDK 的 7u3 不是一回事,很多刚接手老系统的开发,看到这个标题就默认要配 JDK 7,结果在启动阶段就翻了车。作为一套具体的 Java EE 7 发行版,它的意义不是新,而是“标准”:老项目当年基于它开发,今天改不动,也绕不开。
现在还频繁遇到它的场景主要有两种:一是遗留系统要迁移服务器、换 JDK、补证书,必须先把这套旧环境摸透;二是新项目要和老系统联调,接口行为必须以它的兼容性为准。无论哪种场景,你需要的都不是科普,而是一套能照着做的判断标准和操作流程。
这篇笔记按接手老环境的顺序展开:拆开 SDK 看内部组成,把命令行和环境变量跑通,完成一次真实部署,再逐个排查旧版本最容易踩的坑,最后落到让服务少出问题的收尾习惯上。适合开发、运维,以及准备规划迁移方案的人。
2. 拆开 7u3 的包:SDK 里有什么,为什么选它而不是单下 Web Profile
2.1 参考实现:SDK 的核心才是“标准答案”
Java EE 7 是一组规范,定义了 Servlet、EJB、JPA、JAX-RS、JMS 等接口,但规范本身不附带可运行的程序。SDK 把这组规范连同实现一起交付,其中捆绑的参考实现就是 GlassFish 4.0。参考实现的价值在于:当规范文本有歧义,比如某个注解在整个部署链路中究竟由谁解析,参考实现的行为就代表规范的标准答案。
对维护老系统的人来讲,这套“标准答案”可以直接当排错依据。同一个@TransactionAttribute在不同容器里行为不同,别的商用服务器可能做了一些扩展,GlassFish 4.0 更贴近规范原文,所以排查时拿它做参照,能快速确认是哪一层的实现偏差。遇到和 EJB 事务、JMS 消息、JPA 二级缓存相关的诡异现象,先在 7u3 上复现一遍,能过滤掉大量“假 bug”。
选型上,区分 Full Platform 和 Web Profile 是关键。项目里只用 Servlet/JSP、静态资源、REST 接口,Web Profile 够用;一旦涉及 EJB、JMS、JTA 分布式事务、定时调度,就必须 Full Platform。判断老项目是否被 Web Profile 覆盖,最快的方法是看依赖清单里有没有javax.ejb、javax.jms、javax.batch这样的包名。凡是出现这些,直接上完整版 SDK 7u3,别为省那一点磁盘空间给自己留隐患。
2.2 解压安装与第一个 domain:从压缩包到管理控制台
SDK 7u3 的安装形态就是一个压缩包,没有图形安装向导。拿到包之后,解压即用,但路径规划要做在前面。我一般会建一个不带版本号的软链接,这样以后升级 SDK 时,只换链接指向,不用改脚本里的路径。
# 把 SDK 压缩包放到 /opt 下,指定目录解压 mkdir -p /opt/javaee unzip javaee-sdk-7u3.zip -d /opt/javaee # 解压后根目录通常是 glassfish4,建立软链接方便引用 ln -s /opt/javaee/glassfish4 /opt/glassfish # 看一下解压出的顶层目录,确认 bin、glassfish、docs 等都在 find /opt/glassfish -maxdepth 2 -type d | head -30unzip -d指定释放目标目录,避免 zip 包把文件散落在当前目录。软链接指向glassfish4,后续所有脚本里写/opt/glassfish即可,版本号不会侵入配置。find查看目录层级,目的是确认解压完整;如果 bin 目录缺失,说明包没解全,后边所有命令都会失败。
接下来配置环境变量。SDK 7u3 官方对应的 JDK 版本是 7 和 8,但实际维护中我强烈建议用 JDK 8。JDK 7 太老,很多 TLS 协议和新工具链已经不支持;JDK 9 以后模块化改动又会让 GlassFish 启动报错。所以 JDK 8 是这个版本的最优解。
# 以 JDK 8 为例,注意 JAVA_HOME 指向 JDK 目录,不要指到 JRE export JAVA_HOME=/usr/local/java/jdk1.8.0_x64 export GLASSFISH_HOME=/opt/glassfish export PATH=$GLASSFISH_HOME/bin:$JAVA_HOME/bin:$PATH # 验证版本号能正常读出 asadmin versionJAVA_HOME指向 JDK 根目录,不是bin目录,也不是 JRE 安装目录;否则asadmin找不到编译器和部分工具。GLASSFISH_HOME指向软链接路径,asadmin version能返回类似 GlassFish 4.0 的版本信息。如果这里直接报错,九成是环境变量写错或bin目录没有执行权限,先排查这两点再往下走。
启动默认 domain 并验证服务:
# 启动默认域 domain1,监听端口默认是 4848(管理)和 8080(HTTP) asadmin start-domain # 列出当前存在的所有域及状态 asadmin list-domainsstart-domain不带参数时启动默认域,日志输出到$GLASSFISH_HOME/glassfish/domains/domain1/logs/server.log。启动完成后,浏览器访问http://localhost:4848能看到管理控制台,访问http://localhost:8080能看到默认欢迎页面。这两个页面任何一个打不开,先回头看日志,不要盲目改端口。
在 Windows 上解压时,注意路径不要带空格,zip 包内长路径也可能导致解压失败;建议解压到D:\sdk\glassfish4这类短路径下,再手动创建目录引用。Linux 下基本没这个问题,但要注意/opt目录的写权限,普通用户安装时经常会卡在创建 domain 这一步。
3. 把 7u3 接进日常开发:命令行、环境变量与 Maven 坐标
3.1 asadmin 管理命令与连接参数:先让脚本跑通
GlassFish 的管理有图形控制台和命令行两套入口。图形控制台适合偶尔点两下的场景,一旦要反复部署、批量操作,还是命令行可靠。原因很简单:命令行操作可以被脚本记录、被自动化任务复用,还能避免在图形界面里手滑点错配置。
asadmin 最常用的是一组 domain 生命周期命令,它们是整个维护工作的基石:
# 停止指定域,不写域名的默认操作是 domain1 asadmin stop-domain domain1 # 重启指定域,适合修改配置后快速生效 asadmin restart-domain domain1 # 列出已部署的应用,确认当前线上运行的是什么 asadmin list-applicationslist-applications在排查问题时非常好用,它会显示应用名称、类型和当前状态。如果某次部署后应用没起来,先跑这条命令看状态是enabled还是disabled,再决定是调整配置还是直接查日志。
远程管理时,需要在 asadmin 命令后指定目标主机、端口和管理员账号:
# 用一个密码文件免交互登录,适合放进自动化脚本 asadmin --host 192.168.10.20 --port 4848 \ --user admin \ --passwordfile /opt/secret/admin.pwd \ list-applications参数含义:--host是目标机器 IP;--port是管理端口;--user是管理员账号;--passwordfile指向明文密码文件,文件第一行写AS_ADMIN_PASSWORD=密码。注意密码文件的权限必须收紧,否则相当于把管理密码贴在门上。
刚接触这套命令的人最容易踩的坑是:在本地启动了一个 domain,然后用--host指向另一台机器,发现连不上。这不是命令写错了,而是目标机器的enable-secure-admin没开启,或者防火墙把 4848 端口挡了。排查顺序是:先在本机用asadmin list-domains确认域名和端口,再检查目标机器的管理服务状态,最后看防火墙策略。
3.2 构建工具与 IDE:从 Maven 坐标到自动部署
Java EE 7 项目编译时依赖的是标准 API,而不是 GlassFish 自带的实现类。Maven 里的依赖坐标很稳定,关键是 scope 必须用provided:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>provided表示编译和测试时需要这个 API,但打包进 WAR 时要排除掉。因为 GlassFish 启动时自己会加载同名实现类,如果把 API 和实现一起打进去,类加载器会面临两份同名ServletContext的冲突,表现就是部署时一堆NoClassDefFoundError和ClassCastException。这是新手最常犯的错误,也是 IDE 里编译通过、一部署就崩的常见原因。
构建系统里要注意编译级别。我一般会把 Maven 编译器插件锁定在 JDK 8:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin>source和target都设为1.8,保证生成的 class 文件版本能被老容器识别。如果本机 JDK 是 17,但编译目标写成target=17,打包出的 class 文件 GlassFish 4.0 的类加载器根本读不进去,启动时直接报UnsupportedClassVersionError。
IDE 侧的接入相对简单。常见做法是在 IDE 里配置应用服务器路径和 domain 目录,IDE 的部署按钮底层调用的仍然是 asadmin。配置时注意两点:IDE 使用的 JDK 必须也是 8,否则热部署会失败;部署方式推荐选择“部署到管理域”,而不是“复制到 autodeploy 目录”,因为后者在 IDE 里无法及时反馈部署状态。
实际开发中还有一条更省事的路径:把构建产物往autodeploy目录里一丢,GlassFish 会自动捡起来部署。这个目录默认在$GLASSFISH_HOME/glassfish/domains/domain1/autodeploy。它的机制是目录扫描,每次有新文件写入就触发部署。好处是搬家式部署特别快,坏处是手滑丢一个旧包进去就会误线上;我一般只在本地测试环境用 autodeploy,生产环境一律走 asadmin 命令。
4. 部署路径:把一个 WAR 落到 7u3 的 domain 上
4.1 最小 WAR 部署:asadmin 部署命令与常用参数
部署一个 WAR 包,本质上就是把文件复制到 domain 的应用目录,然后让容器完成类加载、资源初始化、监听器启动这一串动作。手工复制不是不行,但 asadmin 会顺带帮你处理依赖、上下文路径、状态回滚这些事,所以老老实实用命令。
先构建出 WAR 包,再执行部署:
# 在项目根目录构建,跳过测试以节省时间 mvn clean package -DskipTests # 部署到默认域,指定应用名和上下文根 asadmin deploy --name demo --contextroot /demo --force target/demo-1.0.war # 部署后立即确认应用状态 asadmin list-applications--name指定部署后的应用名,后续的更新、卸载、启停都靠这个名字;--contextroot是访问路径,浏览器通过http://localhost:8080/demo访问;--force允许同名应用重复部署时直接替换,避免每次都要先 undeploy 再 deploy。部署成功后在list-applications输出里能看到demo。
如果部署后访问 404,先看应用是否处于enabled状态,再看上下文根是否拼写一致。还有一类隐蔽问题见多了:本地打包时用 JDK 8,部署到服务器时本机有JAVA_HOME指向 JDK 11,GlassFish 启动时继承了错误环境。部署和运行的 JDK 必须一致,Windows 上还经常出现服务注册表里残留旧 JAVA_HOME 的情况,一重启就原形毕露。
生产环境我一般不用--force,而是先部署一个新版本名,验证没问题后再切换流量。切换方式是部署时用相同的--contextroot,让新应用先就绪,然后通过asadmin undeploy摘掉旧版本。整个过程对调用方是无感的。
4.2 连接池与 JNDI 数据源:让应用真正连上数据库
Java EE 老项目的数据库连接一般不走应用自己创建Connection的方式,而是把连接池建在容器里,应用通过 JNDI 名字去拿。这样做的原因有三个:连接复用能省掉反复建连的耗时,容器能统一管理连接泄漏和超时,XA 分布式事务也必须依赖容器的资源管理。所以看到老的持久化配置里写jdbc/appdb,那指的是 JNDI 资源,不是数据库 URL。
先看一下当前环境已有哪些资源:
# 列出连接池和 JNDI 资源 asadmin list-jdbc-connection-pools asadmin list-jdbc-resources没有现成资源时,依次执行两步创建。下面以常见的 MySQL 数据库为例:
# 第一步:创建物理连接池,指定数据源实现类和数据库连接属性 asadmin create-jdbc-connection-pool \ --restype javax.sql.DataSource \ --datasourceclassname com.mysql.jdbc.Driver \ --property user=appuser:password=ChangeMe0912:url=jdbc:mysql://dbhost:3306/appdb?useUnicode=true\&characterEncoding=UTF-8 \ appPool # 第二步:创建 JNDI 资源,把逻辑名绑定到连接池上 asadmin create-jdbc-resource --connectionpoolid appPool jdbc/appdb # 第三步:检查连接池能不能真正连上数据库 asadmin ping-connection-pool appPool--datasourceclassname是驱动类全限定名,这个类必须存在于 domain 的lib/ext目录下。常见做法是把驱动 jar 放进$GLASSFISH_HOME/glassfish/domains/domain1/lib/ext,然后重启 domain。--property里用冒号分隔多项属性,URL 中的&在 Shell 里要用\&转义,否则会被当成后台执行符。ping-connection-pool返回成功,说明从连接池到数据库的链路是通的。
在这个操作里,最容易翻车的是驱动类写错。MySQL 老驱动叫com.mysql.jdbc.Driver,新驱动改成了com.mysql.cj.jdbc.Driver,两个类名不一样,放错任何一个都会报找不到类。排查思路是先用jar tf查看驱动 jar 里到底有哪些类,再回填--datasourceclassname,不能用猜的。
还有一种情况:连接池创建成功,ping 也通,但应用启动时还是报NamingException。这不是连接池的问题,而是 JNDI 资源名没对上。老项目里persistence.xml写的jdbc/appdb,和 asadmin 里创建的资源名一字不差才行,大小写、斜杠都不能错。我一般会把 JNDI 资源名全部小写,从源头上避开拼写差异。
5. 7u3 排查手册:旧 SDK 撞上新环境的 5 个典型坑
5.1 高版本 JDK 下启动失败:模块系统与反射调用冲突
现象:用 JDK 11 或 17 启动 domain,日志里出现IllegalAccessError、NoClassDefFoundError,异常堆栈指向java.sql、java.xml或javax.annotation相关类,启动进程直接退出。
原因:JDK 9 开始引入模块系统,把原本散落在各处的内部 API 做了封装,老 GlassFish 用反射访问这些内部接口时被模块边界拦截。与此同时 Java EE 的 API 从 JDK 里被移除,老容器找不到javax.*的运行时实现。
解决:我一般直接锁定 JDK 8,不折腾--add-opens那一堆参数。老容器的兼容性列表就是按照 JDK 8 验证的,换到新 JDK 后各种反射问题防不胜防。维护脚本里显式固定:
export JAVA_HOME=/usr/local/java/jdk1.8.0_x64 /opt/glassfish/bin/asadmin restart-domain domain1JAVA_HOME在启动脚本和 domain.xml 里的配置要保持一致。如果两处不一致,启动时走的 JDK 和配置文件里声明的完全不是同一个,问题会被隐藏得很深。我的排查习惯是:先跑asadmin version,再看实际 Java 进程的启动命令,确认进程真正用的是哪个 JDK。
5.2 改了内存参数却不生效:set 命令与 domain.xml 的优先级
现象:用asadmin create-jvm-options加了-Xmx1024m,重启 domain 后查看进程参数,值还是原来的 512m。或者启动时直接报Unrecognized VM option 'PermSize'。
原因:SDK 7u3 对应的旧调优模板常写-XX:PermSize和-XX:MaxPermSize,这两个参数在 JDK 8 里已经被元空间取代,写了会被直接忽略甚至报错。另一个原因是create-jvm-options命令虽然执行成功,但修改写进了 domain.xml 的java-config节点,而进程真正读取的参数可能来自启动脚本或系统环境变量覆盖。
解决:最直观的方法是直接编辑 domain.xml,而不是用命令一层层改:
# 先查看当前域生效的 JVM 参数 asadmin get domain1.java-config.jvm-options # 修改示例:删除旧值后新增 asadmin delete-jvm-options "-Xmx512m" asadmin create-jvm-options "-Xmx1024m:-XX:MaxMetaspaceSize=256m" # 重启后验证进程参数 asadmin restart-domain domain1create-jvm-options一次设置多个参数时用冒号分隔,这个语法很容易写错。写错的时候不会立刻报错,而是重启后只有前半段生效;我一般直接改$GLASSFISH_HOME/glassfish/domains/domain1/config/domain.xml里的<jvm-options>标签,改完再重启,直观且不易出错。
5.3 HTTPS 握手失败:TLS 协议版本和证书链过期
现象:外部客户端访问https://端口(默认 8181)时握手失败,浏览器提示连接不安全或直接拒绝,但在服务器本地用curl又能通。
原因:老 SDK 默认密钥库里放的是若干年前生成的自签名证书,有效期早就过了;另外 JDK 8 之前的 TLS 协议协商能力偏弱,新客户端默认要求 TLS 1.2,老服务端如果只支持 TLS 1.0,两边协商不上,表现就是连接被重置。
解决:把证书换新,TLS 版本尽量拉高。操作前先备份密钥库,这是后悔药:
# 备份原有密钥库 cp keystore.jks keystore.jks.bak.$(date +%Y%m%d) # 生成新的自签名密钥对,alias 沿用默认的 s1as keytool -genkeypair -alias s1as -keyalg RSA -keysize 2048 \ -validity 3650 \ -keystore /opt/glassfish/glassfish/domains/domain1/config/keystore.jks \ -storepass changeit -keypass changeit \ -dname "CN=localhost, OU=IT, O=Internal, C=CN"-validity 3650表示证书有效期 3650 天,约十年,换一次能顶很久。-dname必须提供,否则 keytool 交互式追问会把脚本卡住。执行完后重启 domain,再用curl -v https://localhost:8181验证握手是否成功。
证书换完后,客户端需要重新信任这个自签证书。如果应用对接方没法手动导入证书,那就得考虑内部 CA 签发,或者让网关层做 TLS 终止。老容器的 HTTPS 排查,十次里有七次是证书过期,剩下三次是协议版本不匹配,很少真有“莫名其妙连不上”的玄学。
5.4 端口被占与 domain 锁:第二个实例怎么都起不来
现象:在同一台机器上创建第二个 domain 并启动,报Address already in use,或者提示管理端口已被占用;如果上一个 domain 被强制 kill,再次启动时报锁文件错误。
原因:两个 domain 的默认端口会重叠,默认 domain1 的 4848 和 8080 与新建 domain 的默认配置完全相同。另一个原因是 GlassFish 启动时会写锁文件来标记运行状态,进程被强杀或系统宕机,锁文件残留,启动逻辑会误以为已经有实例在跑。
解决:新建 domain 时用--portbase让端口自动偏移:
# 以 18000 为基准,管理端口映射到 18048,HTTP 端口映射到 18080 asadmin create-domain --portbase 18000 appdomain # 启动新域 asadmin start-domain appdomain--portbase会自动给默认端口加一个偏移量,省去逐项改端口的手工操作。11000 以上的基准值比较安全,既能避开已占用的段,也方便后续通过端口号快速识别是哪个域。
如果确认端口没有被占用,但仍报端口冲突,就去 domain 目录下找锁文件,路径一般在$GLASSFISH_HOME/glassfish/domains/<域名>/下,文件名带.lck后缀。删除前先确认没有存活进程,避免误删后两个实例同时写同一份配置,那种情况更麻烦。
5.5 中文乱码:从请求到数据库的完整链路
现象:浏览器提交的中文参数在页面显示为问号,日志里打印出来的中文是乱码,数据库表里的中文也变成问号或 Unicode 转义字符。
原因:不是某一个环节坏了,而是请求编码、应用容器编码、数据库连接编码、数据库表字符集四段链路不全是 UTF-8,任何一端不一致就会出现乱码。最常见的错位是请求已经用 UTF-8 进入容器,但 JDBC URL 没带characterEncoding=UTF-8,导致数据入库时被转成别的编码。
解决:按链路逐段锁定。
# 第一段:容器请求编码,改成 UTF-8 asadmin set server.network-config.protocols.protocol.http-listener-1.http.encoding-enabled=true asadmin set server.network-config.protocols.protocol.http-listener-1.http.encoding-uri-encoding=UTF-8 # 第二段:JVM 文件编码,统一成 UTF-8 asadmin create-jvm-options "-Dfile.encoding=UTF-8" # 第三段:重启让配置生效 asadmin restart-domain domain1encoding-enabled打开后 GlassFish 会按encoding-uri-encoding指定的编码解析请求参数;-Dfile.encoding=UTF-8主要影响日志输出和文件读写。真正容易漏掉的还是 JDBC URL,必须在连接池属性里明确加上characterEncoding=UTF-8,连接池配置的优先级比你persistence.xml里写的任何属性都高。
如果数据库里已经存了乱码,改配置救不了存量数据。应对办法是先导出现有数据,修复字符集后再导入;关键是这次要把连接 URL、表和库的默认字符集、客户端连接编码都确认一致,不然下次迁移数据还会再来一遍。
6. 让 7u3 少闹腾:启动参数、systemd 托管与备份习惯
最后一章不聊新功能,聊怎么让这台老发动机少出幺蛾子。SDK 7u3 本身已经不是活跃开发版本,日常维护的目标是“稳定运行、可快速恢复”,所以我会把精力放在三个地方:参数固化、进程托管、备份留底。
JVM 参数不要每次在命令行里临时加,统一写进 domain.xml。一个经过验证的常见配置如下:
| 参数 | 建议值 | 说明 |
|---|---|---|
-Xms | 256m | 启动时堆大小,避免频繁扩容 |
-Xmx | 1024m | 堆上限,按实际应用内存占用调整 |
-XX:MaxMetaspaceSize | 256m | 元空间上限,防止类加载器泄漏 |
-Dfile.encoding=UTF-8 | UTF-8 | 统一中文编码 |
-Duser.timezone=Asia/Shanghai | 系统时区 | 避免日志时间与本地时间不一致 |
进程托管建议用 systemd 而不是裸启动脚本。托管的好处是开机自启、崩溃自动拉起、日志统一收集。domain 启动和停止各写一条命令即可:
# ExecStart 使用 start-domain,ExecStop 使用 stop-domain ExecStart=/opt/glassfish/bin/asadmin start-domain domain1 ExecStop=/opt/glassfish/bin/asadmin stop-domain domain1注意asadmin stop-domain是阻塞式的,systemd 可以通过ExecStop稳定收到退出状态。如果 systemd 配置有问题,现象是服务状态显示 active,但端口听不到,这时先看journalctl -u glassfish的日志,再检查ExecStart里的路径是否因为软链接解析不完整导致失败。
备份是最后一道防线。我维护的每一套 SDK 7u3 环境,备份清单只有三样东西:
| 备份对象 | 路径 | 恢复方式 |
|---|---|---|
| 域配置 | domains/domain1/config/domain.xml | 停止域后覆盖,重启 |
| 已部署应用 | domains/domain1/applications/ | 复制回原目录后启用 |
| 自定义库 | domains/domain1/lib/ext/ | 含连接池驱动和第三方 jar |
用 crontab 定期把这三个目录打包,保留最近七天版本,出问题时能恢复到任意一天的状态。我曾经就是因为少备份了lib/ext下的驱动 jar,服务器故障后新环境连不上数据库,排查了很久才发现是驱动没放。从那以后,我的习惯是每次改完 domain.xml 或连接池配置,立刻手动打包一份配置快照,再做变更操作。这个习惯帮我少熬了不少夜,希望也能帮到你。
本文还有配套的精品资源,点击获取