重构项目

This commit is contained in:
2026-02-28 12:07:17 +08:00
parent f3c1fc98f7
commit c5a2ac3e24
7 changed files with 40 additions and 21 deletions

161
enjoy/__init__.py Normal file
View File

@@ -0,0 +1,161 @@
import jpype
import os
from typing import Dict, Any, Optional
class Enjoy:
"""
适配JFinal Enjoy 5.2.2模板引擎的Python调用类
核心类com.jfinal.template.Engine
实现mrzhou@miw.cn
"""
_instance: Optional['Enjoy'] = None
_jvm_started: bool = False
_engine: Optional[Any] = None # 保存JFinal Engine实例
def __new__(cls, jar_path: str = None, template_base_path: str = "./"):
"""单例模式全局唯一Engine实例"""
if cls._instance is None:
cls._instance = super().__new__(cls)
# 如果未提供jar_path使用项目内置的enjoy-5.2.2.jar
if jar_path is None:
# 获取当前文件所在目录然后拼接jar文件名
current_dir = os.path.dirname(os.path.abspath(__file__))
cls._instance.jar_path = os.path.join(current_dir, "enjoy-5.2.2.jar")
else:
cls._instance.jar_path = os.path.abspath(jar_path)
cls._instance.template_base_path = os.path.abspath(template_base_path)
cls._instance._init_jvm()
cls._instance._init_engine() # 初始化JFinal Engine
return cls._instance
def _init_jvm(self):
"""启动JVM并加载enjoy-5.2.2.jar"""
if not self._jvm_started:
try:
# 使用JPype1的自动类路径管理让JPype1自己处理其支持库
jpype.startJVM(
"-ea",
f"-Djava.class.path={self.jar_path}",
"-Dfile.encoding=UTF-8",
convertStrings=False
)
self._jvm_started = True
print(f"JVM启动成功 | 工作目录: {jpype.java.lang.System.getProperty('user.dir')}")
except Exception as e:
raise RuntimeError(f"JVM启动失败: {e}") from e
def _init_engine(self):
"""初始化JFinal Engine实例核心"""
try:
# 加载JFinal Engine类
Engine = jpype.JClass("com.jfinal.template.Engine")
# 获取默认Engine实例JFinal推荐单例使用
self._engine = Engine.use()
# 配置模板基础路径(关键:指定模板文件所在目录)
self._engine.setBaseTemplatePath(self.template_base_path)
# 可选配置:设置编码、开启热加载等
self._engine.setEncoding("UTF-8")
self._engine.setDevMode(True) # 开发模式,便于调试
print(f"Engine初始化成功 | 模板基础路径: {self.template_base_path}")
except jpype.JException as e:
e.printStackTrace()
raise RuntimeError(f"Engine初始化失败: {e.getMessage()}") from e
def _convert_python_to_java(self, obj: Any) -> Any:
"""转换Python数据为Java Map适配Enjoy模板数据格式"""
if isinstance(obj, dict):
# 优先使用JFinal的Map也可用java.util.HashMap
HashMap = jpype.JClass("java.util.HashMap")
java_map = HashMap()
for key, value in obj.items():
# 递归转换嵌套数据
if isinstance(value, (dict, list)):
java_map.put(key, self._convert_python_to_java(value))
else:
java_map.put(key, value)
return java_map
elif isinstance(obj, list):
return jpype.JArray(jpype.JObject)([self._convert_python_to_java(x) for x in obj])
else:
return obj
def render(self, tempStr: str, data: Dict[str, Any]) -> str:
"""
渲染Enjoy模板核心方法
:param tempStr: 模板文件名相对于baseTemplatePath的路径如index.html
:param data: 模板渲染数据Python字典
:return: 渲染后的字符串
"""
if not self._engine:
raise RuntimeError("Engine未初始化请检查Jar包路径")
try:
# 1. 转换Python字典为Java Map
java_data = self._convert_python_to_java(data)
# 2. 获取Template实例Engine.getTemplate
template = self._engine.getTemplate(tempStr)
# 3. 渲染模板使用Writer接收结果
StringWriter = jpype.JClass("java.io.StringWriter")
writer = StringWriter()
template.render(java_data, writer)
# 4. 转换为Python字符串
result = writer.toString()
writer.close()
return result
except jpype.JException as e:
print("\n=== JFinal Enjoy异常详情 ===")
e.printStackTrace()
raise RuntimeError(f"模板渲染失败: {e.getMessage()}") from e
except Exception as e:
raise RuntimeError(f"调用失败: {e}") from e
def close(self):
"""关闭JVM释放资源"""
if self._jvm_started:
jpype.shutdownJVM()
self._jvm_started = False
self._engine = None
self.__class__._instance = None
print("JVM已关闭")
def __del__(self):
"""析构函数:自动关闭"""
self.close()
# ------------------------------
# 标准调用示例适配enjoy-5.2.2.jar
# ------------------------------
if __name__ == "__main__":
# 1. 配置参数仅需配置模板路径jar文件自动使用内置版本
TEMPLATE_BASE_PATH = "./templates" # 模板文件所在目录如index.html放在这个目录下
# 2. 创建Enjoy实例初始化Engine自动使用内置的enjoy-5.2.2.jar
try:
enjoy = Enjoy(
template_base_path=TEMPLATE_BASE_PATH
)
# 3. 准备模板参数
render_data = {
"name": "JFinal Enjoy 5.2.2测试",
"hobbies": ["Python调用Java", "模板渲染", "跨语言开发"],
"user": {"age": 25, "gender": ""}
}
# 4. 渲染模板tempStr是模板文件名相对于TEMPLATE_BASE_PATH
result = enjoy.render(
tempStr="index.html", # 模板文件:./templates/index.html
data=render_data # 渲染数据Python字典
)
# 5. 输出结果
print("\n=== 模板渲染结果 ===")
print(result)
except RuntimeError as e:
print(f"\n执行失败: {e}")
finally:
# 6. 关闭资源
if 'enjoy' in locals():
enjoy.close()

BIN
enjoy/enjoy-5.2.2.jar Normal file

Binary file not shown.

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
#(name)
<hr>
#for(i : hobbies)
#(i)
#end
<hr>
#for(i : scores)
#(i)
#end
<hr>
#for(i : user)
#(i.key) #(i.value)
#end
</body>
</html>