探索Jython在服务器端Web编程中的应用
在服务器端Web编程领域,Jython有着独特的应用价值。它在许多方面与Java紧密结合,为开发者提供了更灵活、高效的开发方式。下面我们将深入探讨Jython在服务器端Web编程中的具体应用。
1. Jython在Web开发中的定位
Jython适用于Java适用的任何场景,尤其在需要快速开发和增加灵活性的Java项目中表现出色。服务器端Java Web编程主要通过Servlet和Java Server Pages(JSP)实现,因此Jython的主要应用也是Servlet和JSP。不过,Java的企业级包(j2ee)在Web应用开发中也很重要,Jython在这些技术中同样有效。但需要注意的是,Jython并不适合用于实现CGI脚本,因为JVM的启动时间会导致性能不佳,而Servlet和JSP则具有持久性,能在Web请求之间保持在内存中。
使用Jython进行Web编程有诸多优势。高质量的Servlet容器广泛可用且部署普遍,Java Servlet应用可以受益于Jython的灵活性和高级语言特性,还能利用Java和Jython的库。常见的Servlet容器包括WebLogic、WebSphere、Tomcat、Jigsaw、Resin和Jetty等。
2. Jython Servlet容器
Jython可以与任何兼容的Java Servlet容器配合使用,以下是一些常见的Servlet容器介绍:
| 容器名称 | 描述 | 下载地址 |
| ---- | ---- | ---- |
| Jakarta’s Tomcat | 是Servlet和Java Server Pages规范的参考实现,支持2.2 Servlet和1.1 JSP规范(3.2.3版本),后续版本支持更高规范。 | http://jakarta.apache.org/ |
| Apache JServ | 为Apache创建的Servlet(2.0版本)引擎,常用于与Jython结合使用。 | http://java.apache.org/ |
| Jigsaw | W3C的实验性Web服务器,是完全符合HTTP/1.1的Web服务器和缓存代理服务器,支持Servlet 2.2和Java Server Pages 1.1。 | http://www.w3.org/Jigsaw/ |
| Jetty | 紧凑高效的Java Web服务器,支持Servlet 2.2和JSP 1.1,支持HTTP 1.1,包含SSL支持,可轻松与EJB服务器集成。 | http://jetty.mortbay.com/ |
这些容器的安装指南可从各自的网站获取。
3. 定义简单的Servlet类
下面我们将比较一个简单的Java Servlet和一个简单的Jython Servlet,并介绍如何在Tomcat Web应用中安装jython.jar文件。
3.1 简单的Java Servlet
// Filename: JavaServlet.java
import javax.servlet.*;
import java.io.*;
public class JavaServlet extends GenericServlet {
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter toClient = res.getWriter();
toClient.println("<html><body>" +
"This is a Java Servlet." +
"</body></html>");
}
}
该代码使用了Servlet API中的GenericServlet类,service方法是抽象的,必须在子类中实现,用于响应Web请求。
3.2 简单的Jython Servlet
# Filename: JythonServlet.py
from javax import servlet
import random # Not used, just here to test module imports
class JythonServlet(servlet.GenericServlet):
def service(self, req, res):
res.setContentType("text/html")
toClient = res.getWriter()
toClient.println("""<html><body>
This is a Servlet of the Jython variety.
</body></html>""")
Jython Servlet与Java Servlet在导入、继承和实现上类似,但使用了Jython的语法。
3.3 测试Java和Jython Servlet
测试这些Servlet需要安装Tomcat,并将jython.jar文件包含在Tomcat中。以下是具体步骤:
1. 安装Tomcat :
- 从http://jakarta.apache.org下载Tomcat(建议下载jakarta-tomcat-3.2.3)。
- 解压到有足够权限的目录,设置TOMCAT_HOME环境变量。
- 设置JAVA_HOME环境变量。
- 使用相应的启动脚本启动Tomcat:
- Windows:%TOMCAT_HOME%\bin\startup.bat
- nix:$TOMCAT_HOME/bin/startup.sh
- 看到“date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080”表示Tomcat已启动并监听8080端口。
- 使用相应的关闭脚本停止Tomcat:
- Windows:%TOMCAT_HOME%\bin\shutdown.bat
- nix:$TOMCAT_HOME/bin/shutdown.sh
2. 安装Java Servlet :
- 将JavaServlet.java文件放在%TOMCAT_HOME%\webapps\jython\WEB-INF\classes目录。
- 在该目录下使用以下命令编译:javac -classpath %TOMCAT_HOME%\lib\servlet.jar JavaServlet.java
- 启动Tomcat,在浏览器中访问http://localhost:8080/jython/servlet/JavaServlet查看结果。
3. 安装Jython Servlet :
- 有两种使用Jython Servlet的方法,这里使用jythonc编译。
- 编译Jython Servlet模块:
- 将JythonServlet.py文件放在%TOMCAT_HOME%\webapps\jython\WEB-INF\classes目录。
- 确保CLASSPATH包含servlet.jar,使用以下命令编译:jythonc -w . JythonServlet.py
- 添加jython.jar到类路径,有三种方法:
- 首选将jython.jar添加到上下文的lib目录。
- 不建议将jython.jar添加到Tomcat的lib目录。
- 将jython.jar留在Jython安装目录,但在运行Tomcat前添加到类路径。
- 使Jython的lib目录对Servlet可用,有三种方法:
- 设置python.home属性。
- 冻结所需模块。
- 让每个Servlet显式将模块位置追加到sys.path。
- 测试Jython Servlet,在浏览器中访问http://localhost:8080/jython/servlet/jythonServlet,若出现错误,可能是编译时Servlet.jar不在类路径、文件名和类名不一致或Jython模块不可用等原因。
4. 关于GenericServlet
GenericServlet是一个不特定于任何协议的Servlet类,HttpServlet更常用于Web编程,因为它特定于HTTP协议,但GenericServlet是HttpServlet的超类,了解其方法很重要。以下是GenericServlet的一些方法及在Jython子类中的使用示例:
| Java Signature | Usage in Jython Subclass |
| ---- | ---- |
| public void init(ServletConfig config) throws ServletException; | def init(self, config): |
| public void destroy(); | def destroy(self): |
| public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException; | def service(self, request, response): |
| public String getServletInfo(); | def getServletInfo(self): return “Info string” |
| public void log(String message); | self.log(“message”) |
| public ServletConfig getServletConfig(); | config = self.getServletConfig() |
| public java.util.enumeration getInitParameterNames(); | nameList = self.getInitParameterNames() |
| public String getInitParameter(String name) | param = self.getInitParameter(“paramName”) |
| public ServletContext getServletContext() | context = self.getServletContext |
下面是一个使用GenericServlet的HitCounter Servlet示例:
# filename: HitCounter.py
from javax import servlet
from time import time, ctime
import os
class HitCounter(servlet.GenericServlet):
def init(self, cfg=None):
if cfg:
servlet.GenericServlet.init(self, cfg)
else:
servlet.GenericServlet.init(self)
contextRoot = self.servletContext.getRealPath(".")
self.file = os.path.join(contextRoot, "counterdata.txt")
if os.path.exists(self.file):
lastCount = open(self.file, "r").read()
try:
self.totalHits = int(lastCount)
except:
self.totalHits = 0
else:
self.totalHits = 0
def service(self, req, res):
res.setContentType("text/html")
toClient = res.getWriter()
toClient.println("<html><body>")
toClient.println("Total Hits: %i<br>" % (self.totalHits,))
self.totalHits += 1
toClient.println("</body></html>")
def destroy(self):
f = open(self.file, "w")
f.write(str(self.totalHits))
f.close()
将HitCounter.py文件放在上下文的classes目录,编译后在浏览器中访问http://localhost:8080/jython/servlet/HitCounter测试。
5. HttpServlet
HttpServlet是GenericServlet的子类,更适合用于Web编程,因为它特定于HTTP协议。它为每个HTTP方法定义了方法,如doGet、doPost等。以下是HttpServlet的一些方法及在Jython子类中的使用示例:
| Java Signature | Usage in Jython Subclass |
| ---- | ---- |
| doDelete(HttpServletRequest req, HttpServletResponse resp) | def doDelete(self, req, res): |
| doGet(HttpServletRequest req, HttpServletResponse resp) | def doGet(self, req, res): |
| doHead(HttpServletRequest req, HttpServletResponse resp) | def doHead(self, req, res): |
| doOptions(HttpServletRequest req, HttpServletResponse resp) | def doOptions(self, req, res): |
| doPost(HttpServletRequest req, HttpServletResponse resp) | def doPost(self, req, res): |
| doPut(HttpServletRequest req, HttpServletResponse resp) | def doPut(self, req, res): |
| doTrace(HttpServletRequest req, HttpServletResponse resp) | def doTrace(self, req, res): |
| getLastModified(HttpServletRequest req) | def getLastModified(self, req): |
| service(HttpServletRequest req, HttpServletResponse resp) | def service(self, req, res): |
| service(ServletRequest req, ServletResponse res) | def service(self, req, res): |
下面是一个使用HttpServlet的示例:
# file get_post.py
from time import time, ctime
from javax import servlet
from javax.servlet import http
class get_post(http.HttpServlet):
head = "<head><title>Jython Servlets</title></head>"
title = "<center><H2>%s</H2></center>"
def doGet(self, req, res):
res.setContentType("text/html")
out = res.getWriter()
out.println('<html>')
out.println(self.head)
out.println('<body>')
out.println(self.title % req.method)
out.println("This is a response to a %s request" % (req.getMethod(),))
out.println("<P>In this GET request, we see the following " + "header variables.</P>")
out.println("<UL>")
for name in req.headerNames:
out.println(name + " : " + req.getHeader(name) + "<br>")
out.println("</UL>")
out.println(self._params(req))
out.println("""
<P>The submit button below is part of a form that uses the
"POST" method. Click on this button to do a POST request.
</P>""")
out.println('<br><form action="get_post" method="POST">' +
'<INPUT type="hidden" name="variable1" value="one">' +
'<INPUT type="hidden" name="variable1" value="two">' +
'<INPUT type="hidden" name="variable2" value="three">' +
'<INPUT type="submit" name="button" value="submit">')
out.println('<br><font size="-2">time accessed: %s</font>' % ctime(time()))
out.println('</body></html>')
def doPost(self, req, res):
res.setContentType("text/html");
out = res.getWriter()
out.println('<html>')
out.println(self.head)
out.println('<body>')
out.println(self.title % req.method)
out.println("This was a %s<br><br>" % (req.getMethod(),))
out.println(self._params(req))
out.println('<br> back to <a href="get_post">GET</a>')
out.println('<br><font size="-2">time accessed: %s</font>' % ctime(time()))
out.println('</body></html>')
def _params(self, req):
params = "Here are the parameters sent with this request:<UL>"
names = req.getParameterNames()
if not names.hasMoreElements():
params += "None<br>"
for name in names:
value = req.getParameterValues(name)
params += "%s : %r<br>" % (name, tuple(value))
params += "</UL>"
return params
将get_post.py文件放在$TOMCAT_HOME/webapps/jython/WEB-INF/classes目录,编译后在浏览器中访问http://localhost:8080/jython/servlet/get_post测试。
5. HttpServletRequest和HttpServletResponse
与客户端连接的通信通过HttpServletRequest和HttpServletResponse对象进行,它们为处理请求和响应提供了更高级的HTTP特定方法。
| Method and Property | Description |
| ---- | ---- |
| getAuthType() / AuthType | 返回描述认证类型名称的字符串,用户未认证时为None。 |
| getContextPath() / contextPath | 返回描述请求上下文路径信息的字符串。 |
| getCookies() / cookies() | 返回客户端请求中发送的所有cookie。 |
| getDateHeader(name) | 检索指定头的值作为长类型。 |
| getHeader(name) | 返回指定头的值作为字符串。 |
| getHeaderNames() / headerNames | 返回请求中包含的所有头名称的枚举。 |
| getHeaders(name) | 返回指定头名称的所有值的枚举。 |
| getIntHeader(name) | 检索指定头的值作为Java int。 |
| getMethod() / method | 返回请求的类型。 |
| getPathInfo() / pathInfo | 客户端发送的所有额外路径信息。 |
| getPathTranslated() / pathTranslated | 返回从客户端请求的额外路径信息派生的真实路径。 |
| getQueryString() / queryString | 返回客户端请求的查询字符串。 |
| getRemoteUser() / remoteUser | 返回客户端的登录名,未认证时为None。 |
| getRequestedSessionId() / requestedSessionId | 返回客户端的会话ID。 |
| getRequestURI() / requestURI | 返回协议名称和查询字符串之间的部分。 |
| getServletPath() / servletPath | 返回指定当前Servlet的URL部分。 |
| getSession() / session | 返回当前会话,需要时创建。 |
| getSession(create) | 如果存在则返回当前会话,create为true时创建新会话。 |
| getUserPrincipal() / userPrincipal | 返回包含当前认证信息的java.security.Principal对象。 |
| isRequestedSessionIdFromCookie() | 根据当前会话ID是否来自cookie返回1或0。 |
| isRequestedSessionIdFromURL() | 根据当前会话ID是否来自请求的URL字符串返回1或0。 |
| isRequestedSessionIdValid() | 根据请求的会话ID是否仍然有效返回1或0。 |
| isUserInRole(role) | 指示用户是否在指定角色中返回1或0。 |
HttpServletResponse对象用于将mime编码的流发送回客户端,它定义了一些额外的HTTP特定方法。以下是其部分方法及描述:
| Method and Property | Description |
| ---- | ---- |
| addCookie(cookie) | 向响应中添加cookie。 |
| addDateHeader(headerName, date) | 添加带有日期值的头。 |
| addHeader(headerName, value) | 添加头名称和值。 |
| addIntHeader(headerName, value) | 添加带有整数值的头。 |
| containsHeader(headerName) | 根据指定头是否存在返回1或0。 |
| encodeRedirectUrl(url) | 为sendRedirect方法编码URL。 |
| encodeRedirectURL(url) | 为sendRedirect方法编码URL。 |
| encodeURL(url) | 通过包含会话ID来编码URL。 |
| sendError(sc) | 使用状态码发送错误。 |
| sendError(sc, msg) | 使用指定状态码和消息发送错误。 |
| sendRedirect(location) | 发送临时重定向到指定位置。 |
| setDateHeader(headerName, date) | 设置头名称为指定日期值。 |
| setHeader(headerName, value) | 设置头名称为指定值。 |
| setIntHeader(headerName, value) | 设置头名称为指定整数值。 |
| setStatus(statusCode) / status | 设置响应状态码。 |
HttpServletResponse类还包含与标准HTTP响应代码对应的字段,可用于sendError(int)和setStatus(int)方法。
6. PyServlet
对于Jython程序员来说,HttpServlet编程的一个巨大优势是Jython发行版附带了 org.python.util.PyServlet 这个Servlet。它可以加载、执行和缓存Jython文件,让你无需中间编译步骤就能编写和查看Jython Servlet。这通过Servlet映射实现,即特定Servlet(这里是PyServlet)与特定URL模式(如 *.py )的关联。借助合适的 web.xml 文件,Jython的PyServlet类会映射到所有 *.py 文件,当请求 *.py 文件时,PyServlet类会按需加载、缓存和调用 *.py 文件中的方法进行响应。
不过,PyServlet对它所服务的Jython文件有一些限制。Jython文件必须包含一个继承自 javax.servlet.http.HttpServlet 的类,且类名必须与文件名(去掉 .py 扩展名)匹配。例如, Test.py 文件必须包含名为 Test 的类,且该类必须是 javax.servlet.http.HttpServlet 的子类。此外,除了这个继承自HttpServlet的类,使用模块全局标识符是不安全的。
6.1 安装PyServlet
安装PyServlet只需定义Servlet映射,并确保 jython.jar 文件位于上下文的 lib 目录中。Servlet映射在上下文的部署描述符文件 web.xml 中定义,Jython上下文的 web.xml 文件位于 $TOMCAT_HOME/webapps/jython/WEB-INF/web.xml 。在Tomcat中,若上下文的 web.xml 未明确包含某些设置,则使用 $TOMCAT_HOME/conf/web.xml 中的默认设置。
以下是一个示例部署描述符:
<web-app>
<servlet>
<servlet-name>PyServlet</servlet-name>
<servlet-class>
org.python.util.PyServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>PyServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
</web-app>
PyServlet的Servlet定义可选择包含初始化参数( init-params ),用于在初始化Jython时设置属性,如 python.home 、 python.path 等。例如:
<web-app>
<servlet>
<servlet-name>PyServlet</servlet-name>
<servlet-class>
org.python.util.PyServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>python.home</param-name>
<param-value>c:\jython-2.1</param-value>
</init-param>
<init-param>
<param-name>python.path</param-name>
<param-value>
c:\jython-2.1\lib\site-packages
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>PyServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
</web-app>
需要注意的是,定义Jython资源位置的属性(如 python.home 和 python.path )会影响上下文的自包含性和跨平台可移植性。PyServlet的默认 python.home 值是上下文的 lib 目录,这既保证了上下文的自包含性,又使其具有平台独立性。
6.2 测试PyServlet
可以通过启动Tomcat并在浏览器中查看一个简单的jylet(Jython - Servlet)来确认Servlet映射是否正常工作。以下是一个简单的测试jylet:
# File ServletMappingTest.py
from javax.servlet.http import HttpServlet
class ServletMappingTest(HttpServlet):
def doGet(self, req, res):
out = res.writer
res.contentType = "text/html"
print >> out, "Greetings from a jylet."
将这个测试文件保存为 %TOMCAT_HOME%\webapps\jython\ServletMappingTest.py ,然后在浏览器中访问 http://localhost:8080/jython/ServletMappingTest.py 。如果看到问候消息,则说明PyServlet映射正确。为完成安装,还需创建Jython的 lib 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib ,并将Jython模块放在该目录中。
7. 会话管理
7.1 会话管理的基本概念
会话管理是跟踪客户端信息的一种方式,在Web应用中非常重要。Cookies是创建会话的常用手段,但Java的 HttpSession 类让会话跟踪更加方便。通过 HttpRequest 的 getSession() 方法可以创建一个会话,返回一个 HttpSession 实例。 HttpSession 是会话管理子系统复杂行为的简单接口,在Jython中使用 HttpSession 对象与Java的区别仅在于语法和对象的 get* 方法对应的自动Bean属性。
7.2 会话管理示例
以下是一个会话管理的示例代码:
# File: session.py
from javax import servlet
from javax.servlet import http
class session(http.HttpServlet, servlet.RequestDispatcher):
def doGet(self, req, res):
sess = req.session
res.contentType = "text/html"
out = res.getWriter()
name = req.getParameterValues("name")
value = req.getParameterValues("value")
if name and value:
sess.putValue(name[0], value[0])
print >>out, ("""
<html>
<body>
<H3>Session Test</H3>
Created at %s<br>
Last Accessed = %s<br>
<u>Values:</u>""" %
(sess.creationTime, sess.maxInactiveInterval)
)
print >>out, "<ul>"
for key in sess.getValueNames():
print >>out, "<li>%s: %s</li>" % (key, sess.getValue(key))
print >>out, "</ul>"
print >>out, """
<HR><P>Use this form to add a new values to the session</P>
<form action="session.py" method="GET">
<P>Name:<br><INPUT type="text" name="name" size="30"></P>
<P>Value:<br><INPUT type="text" name="value" size="30"></P>
<INPUT type="submit" name="button" value="submit">
</body>
</html>
"""
将上述代码保存为 session.py 文件,放在 %TOMCAT_HOME%\webapps\jython 目录下。在浏览器中访问 http://localhost:8080/jython/session.py ,使用表单添加一些变量到会话中,以确认会话管理是否正常工作。即使客户端禁用了Cookies,会话对象仍然可以工作,因为所有URL都会通过 encodeUrl 方法重写,确保会话值可以通过URL传递。
8. 数据库与Servlet
在Jython Servlet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用类似,但管理数据库资源是首要问题,因为许多Web应用包含多个使用数据库内容的Jylet。
8.1 数据库资源管理方式
管理数据库资源有多种选择,主要有两种方法:
- 每个Jylet一个连接 :在Jylet的 init 方法中建立数据库连接,并在Jylet卸载时关闭连接。这种方法虽然消除了响应客户端请求时的连接开销,但如果所需连接数量超出资源限制,就不太合适。
- 连接池 :连接池可以维护一定数量的活动数据库连接,Jylet在响应客户端请求时借用连接,完成后归还到池中。这可以实现资源的合理管理,消除连接开销。
8.2 数据库连接示例
以下是一个使用数据库连接的Jylet示例:
# file: DBDisplay.py
from javax.servlet import http
from com.ziclix.python.sql import zxJDBC
class DBDisplay(http.HttpServlet):
def init(self, cnfg):
#define the JDBC url
url = "jdbc:mysql://192.168.1.77/products"
usr = "productsUser" # replace with real user name
passwd = "secret" # replace with real password
driver = "org.gjt.mm.mysql.Driver"
#connect to the database and get cursor object
self.db = zxJDBC.connect(url, usr, passwd, driver)
self.c = self.db.cursor()
def doGet(self, req, res):
res.setContentType("text/html")
out = res.getWriter()
print >>out, """
<html>
<head>
<title>Jylet Database Connection</title>
</head>
<body>
<table align="center">
<tr>
<td><b>ID</b></td>
<td><b>Title</b></td>
<td><b>Description</b></td>
<td><b>Price</b></td>
</tr>"""
self.c.execute("select code, name, description, price from products")
for row in self.c.fetchall():
print >>out, """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>""" % row
print >>out, """
</table>
</body>
</html>"""
def destroy(self):
self.c.close()
self.db.close()
该示例假设存在名为 products 的数据库,其中包含 products 表,表中至少有 code 、 name 、 description 和 price 字段。创建数据库和表的SQL语句如下:
create database products;
CREATE TABLE products (
primarykey int(11) NOT NULL auto_increment,
code varchar(55) default NULL,
name varchar(255) default NULL,
description text,
price float(5,2) default NULL,
PRIMARY KEY (primarykey)
) TYPE=MyISAM;
将 DBDisplay.py 文件放在上下文的根目录下,在浏览器中访问 http://localhost:8080/jython/DBDisplay.py ,即可查看数据库数据。如果Web应用开始使用过多连接,可以考虑使用连接池。一个流行、免费、经过测试且文档完善的连接池工具是 PoolMan 。
9. JSP
Java Server Pages(JSP)是Sun倡导的作为Servlet补充的模板系统。JSP文件包含网页的标记代码和文本,同时包含指定动态内容的特殊标签。目前,Tomcat仅在JSP中实现Java语言,那么如何在JSP中使用Jython呢?目前有几种方法。
9.1 使用jythonc编译的类与JSP
使用jythonc编译的类与JSP结合,需要创建一个与Java兼容的Jython类,该类的名称与模块名称相同,继承自Java类,并为每个非从超类派生的方法包含 @sig 字符串。将jythonc生成的类文件放在上下文的 classes 目录中,JSP页面就可以像使用任何原生Java类一样使用这些类。当然,这也需要将 jython.jar 文件放在上下文的 lib 目录中。
JSP文件可以通过两种方式使用jythonc编译的类:
- 作为脚本片段 :可以使用任何类,通过 <%@ page import="fully.qualified.path.to.class" %> 导入类,然后在脚本标签( <% %> )或表达式标签( <%= %> )中使用Java代码调用该类。
- 作为Bean :Jython类必须遵循Bean约定,为每个读写属性包含 getProperty 和 setProperty 方法。使用 <jsp:useBean> 标签加载Bean。
以下是一个简单的Jython Bean示例:
# file: NameHandler.py
import java
class NameHandler(java.lang.Object):
def __init__(self):
self.name = "Fred"
def getUsername(self):
"@sig public String getname()"
return self.name
def setUsername(self, name):
"@sig public void setname(java.lang.String name)"
self.name = name
将 NameHandler.py 文件放在 %TOMCAT_HOME%\webapps\jython\WEB-INF\classes 目录中,使用以下命令编译:
jythonc -w . NameHandler.py
以下是一个使用该Bean的JSP页面示例:
<!--file: name.jsp -->
<%@ page contentType="text/html" %>
<jsp:useBean id="name" class="NameHandler" scope="session"/>
<html>
<head>
<title>hello</title>
</head>
<body bgcolor="white">
Hello, my name is
<jsp:getProperty name="name" property="username"/>
<br>
No, wait...
<jsp:setProperty name="name" property="username" value="Robert"/>
, It's really <%= name.getUsername() %>.
</body>
</html>
将 name.jsp 文件放在上下文的根目录中,在浏览器中访问 http://localhost:8080/jython/name.jsp ,应该可以看到默认名称 Fred 和修改后的名称 Robert 。
9.2 在JSP中嵌入PythonInterpreter
如果想在JSP脚本片段中使用Jython代码,可以通过 PythonInterpreter 实例间接实现。需要使用导入指令导入 org.python.util.PythonInterpreter 。以下是一个示例:
<!--name: interp.jsp-->
<%@ page contentType="text/html" %>
<%@ page import="org.python.util.PythonInterpreter" %>
<% PythonInterpreter interp = new PythonInterpreter();
interp.set("out", out); %>
<html>
<body bgcolor="white">
<% interp.exec("out.println('Hello from JSP and the Jython interpreter.')"); %>
</body>
</html>
确保 jython.jar 文件位于上下文的 lib 目录中,将 interp.jsp 文件放在上下文的根目录中,在浏览器中访问 http://localhost:8080/jython/interp.jsp ,应该可以看到Jython解释器输出的消息。不过,很多人认为脚本片段不是好的做法,使用Jython在Java脚本片段中增加了复杂性,更好的方法是使用Bean类和标签库。
9.3 Jython标签库
可以使用jythonc将Jython标签库模块编译成Java类,从而在JSP页面中创建自定义标签库。Jython标签库模块必须满足一些限制才能像Java类一样透明工作。模块必须包含一个与模块名称(去掉 .py 扩展名)相同的类,该类必须继承 javax.servlet.jsp.tagext.Tag 接口或实现该接口的Java类。
以下是一个简单的Jython标签库示例:
# file: JythonTag.py
from javax.servlet.jsp import tagext
class JythonTag(tagext.Tag):
def __init__(self):
self.context = None
self.paren = None
def doStartTag(self):
return tagext.Tag.SKIP_BODY
def doEndTag(self):
out = self.context.out
print >>out, "Message from a taglib"
return tagext.Tag.EVAL_PAGE
def release(self):
pass
def setPageContext(self, context):
self.context = context
def setParent(self, parent):
self.paren = parent
def getParent(self):
return self.paren
安装该标签库所需的类,首先确保 jython.jar 文件位于上下文的 lib 目录中,然后使用以下命令编译 JythonTag.py 文件:
jythonc -w %TOMCAT_HOME%\webapps\jython\WEB-INF\classes JythonTag.py
还需要创建一个标签库描述文件,以下是示例:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>JythonTag</shortname>
<info>A simple Jython tag library</info>
<tag>
<name>message</name>
<tagclass>JythonTag</tagclass>
</tag>
</taglib>
将该描述信息保存为 %TOMCAT_HOME%\webapps\jython\WEB-INF\jython-taglibs.tld 。JSP页面使用该标签库时,需要包含一个指令指定标签库描述文件的路径,并为该库指定一个简单名称:
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
然后可以使用标签:
<jython:message/>
以下是一个使用该标签的JSP文件示例:
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
<html>
<body>
<jython:message />
</body>
</html>
10. BSF
IBM的Bean Scripting Framework(BSF)是一个实现各种脚本语言的Java工具,Jython是目前BSF支持的语言之一。Apache Jakarta项目的 taglibs 子项目包含大量可用的Java标签库,其中有趣的是BSF标签库。BSF标签库与BSF的 jar 文件结合,让你可以快速将Jython脚本片段和表达式直接插入JSP页面。
10.1 安装和配置
下载正确的 bsf.jar 文件,将其放在上下文的 lib 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\lib 中。同时,从相关网站下载BSF标签库的 jar 文件和 bsf.tld 文件,将标签库的 jar 文件放在上下文的 lib 目录中, bsf.tld 文件放在上下文的 WEB-INF 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\bsf.tld 中。
10.2 使用Jython脚本片段
在JSP文件中使用Jython脚本片段,首先需要包含一个指令来识别BSF标签库:
<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>
然后可以使用 bsf:scriptlet 标签,在标签体中包含Jython代码:
<bsf:scriptlet language="jython">
import random
print >> out, random.randint(1, 100)
</bsf:scriptlet>
脚本片段中有一些JSP对象会自动设置在解释器中,包括 request 、 response 、 pageContext 等。以下是一个使用 bsf:scriptlet 标签在JSP文件中创建时间戳的示例:
<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>
<html>
<body>
<center><H2>BSF scriptlets</H2></center>
<b>client info:</b><br>
<bsf:scriptlet language="jython">
for x in request.headerNames:
print >>out, "%s: %s<br>\n" % (x, request.getHeader(x))
</bsf:scriptlet>
<br><br>
<b>Time of request:</b><br>
<bsf:scriptlet language="jython">
import time
print >>out, time.ctime(time.time())
</bsf:scriptlet>
</body>
</html>
将上述代码保存为 %TOMCAT_HOME%\webapps\jython\scriptlets.jsp ,在浏览器中访问 http://localhost:8080/jython/scriptlets.jsp ,应该可以看到客户端信息和访问时间。
综上所述,Jython在服务器端Web编程中有着丰富的应用场景,通过与Servlet、JSP等技术的结合,可以为开发者提供更灵活、高效的开发方式。无论是简单的Servlet开发,还是复杂的数据库连接和JSP集成,Jython都能发挥重要作用。
超级会员免费看
78

被折叠的 条评论
为什么被折叠?



