86、Jython在Web开发中的应用:Servlet与JSP实践

Jython在Web开发中的应用:Servlet与JSP实践

1. Jython在Web开发中的定位

Jython适用于Java适用的任何Web开发场景,尤其在需要快速开发和增加灵活性的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等。

1.1 Jython Servlet容器

Jython可以与任何兼容的Java Servlet容器配合使用,常见的有以下几种:
- Jakarta’s Tomcat :是Servlet和Java Server Pages规范的参考实现,本章使用的版本是3.2.3,支持2.2 Servlet和1.1 JSP规范。Tomcat 4.0将实现2.3 Servlet和1.2 JSP规范。所有示例都应能在任何符合Servlet 2.2/JSP 1.1规范的容器中运行。Tomcat发行版包含本章所需的Servlet和JSP类,无需额外下载。
- Apache JServ :是为Apache创建的Servlet(版本2.0)引擎,常用于Jython Servlet开发。需要单独下载Java Servlet Development Kit 2.0,Java Server Pages需要一个外部模块。
- Jigsaw :是W3C的实验性Web服务器,实际上比“实验性”这个标签所暗示的更成熟。它是一个完全符合HTTP/1.1的Web服务器和缓存代理服务器,支持Servlet 2.2规范和Java Server Pages 1.1。
- Jetty :是一个紧凑高效的Java Web服务器,支持Servlet 2.2和JSP 1.1规范,支持HTTP 1.1,包含SSL支持,并能轻松与EJB服务器集成。

这些工具的安装指南可从各自的网站获取。

2. 定义简单的Servlet类

2.1 简单的Java Servlet

以下是一个基本的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>"); 
    } 
} 

该示例使用了 GenericServlet 作为基类, service 方法是抽象的,必须在子类中实现,用于响应Web请求。

2.2 简单的Jython Servlet

对应的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的语法。

2.3 测试Java和Jython Servlet

2.3.1 安装Tomcat
  • 从http://jakarta.apache.org 下载Tomcat,建议下载jakarta - tomcat - 3.2.3版本。
  • 解压文件到有足够权限的目录,设置 TOMCAT_HOME 环境变量。
    • Windows系统: set TOMCAT_HOME=c:\jakarta - tomcat - 3.2.3
    • *nix系统: export TOMCAT_HOME=/usr/local/jakarta - tomcat3.2.3
  • 设置 JAVA_HOME 环境变量,例如使用JDK1.3.1:
    • Windows系统: set JAVA_HOME=c:\jdk1.3.1
    • *nix系统: export JAVA_HOME=/usr/java/jdk1.3.1
  • 启动Tomcat:
    • Windows系统: %TOMCAT_HOME%\bin\startup.bat
    • *nix系统: $TOMCAT_HOME/bin/startup.sh
  • 停止Tomcat:
    • Windows系统: %TOMCAT_HOME%\bin\shutdown.bat
    • *nix系统: $TOMCAT_HOME/bin/shutdown.sh
2.3.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 ,应看到消息“This is a Java Servlet”。
2.3.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 ,应看到消息“This is a Servlet of the Jython variety”。

3. 关于GenericServlet

GenericServlet 是一个不特定于任何协议的Servlet类, HttpServlet 更常用于Web编程,它是 GenericServlet 的子类。以下是 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 |

3.1 计数器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() 

该示例展示了 init service destroy 方法的使用,这些方法对应Servlet生命周期的不同阶段。

4. HttpServlet

GenericServlet 适用于任何聊天类型的协议,而Web开发主要使用HTTP协议,因此 javax.Servlet.http.HttpServlet 是更好的选择。 HttpServlet GenericServlet 的子类,它为每个HTTP方法定义了方法,如 doGet doPost 等。

4.1 HttpServlet方法

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):

4.2 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 

该示例实现了 doGet doPost 方法,展示了如何根据客户端请求类型调用不同的方法。

5. HttpServletRequest和HttpServletResponse

与客户端的通信通过 HttpServletRequest HttpServletResponse 对象进行,它们提供了更高级的HTTP特定方法。

5.1 HttpServletRequest方法和属性

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 值创建新会话
getUserPrincipal() / userPrincipal 返回包含当前认证信息的 java.security.Principal 对象
isRequestedSessionIdFromCookie() 根据当前会话ID是否来自cookie返回1或0
isRequestedSessionIdFromURL() 根据当前会话ID是否来自请求的URL字符串返回1或0
isRequestedSessionIdValid() 根据请求的会话ID是否仍然有效返回1或0
isUserInRole(role) 指示用户是否在指定角色中返回1或0

5.2 HttpServletResponse方法和属性

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 设置响应状态码

5.3 HttpServletResponse状态码

Error Code Status
100 SC_CONTINUE
101 SC_SWITCHING_PROTOCOLS
200 SC_OK
201 SC_CONTINUE
202 SC_ACCEPTED
203 SC_NON_AUTHORITATIVE_INFORMATION
204 SC_NO_CONTENT
205 SC_RESET_CONTENT
206 SC_PARTIAL_CONTENT
300 SC_MULTIPLE_CHOICES
301 SC_MOVED_PERMANENTLY
302 SC_MOVED_TEMPORARILY
303 SC_SEE_OTHER
304 SC_NOT_MODIFIED
305 SC_USE_PROXY
400 SC_BAD_REQUEST
401 SC_UNAUTHORIZED
402 SC_PAYMENT_REQUIRED
403 SC_FORBIDDEN
404 SC_NOT_FOUND
405 SC_METHOD_NOT_ALLOWED
406 SC_NOT_ACCEPTABLE
407 SC_PROXY_AUTHENTICATION_REQUIRED
408 SC_REQUEST_TIMEOUT
409 SC_CONFLICT
410 SC_GONE
411 SC_LENGTH_REQUIRED
412 SC_PRECONDITION_FAILED
413 SC_REQUEST_ENTITY_TOO_LARGE
414 SC_REQUEST_URI_TOO_LONG
415 SC_UNSUPPORTED_MEDIA_TYPE
500 SC_INTERNAL_SERVER_ERROR
501 SC_NOT_IMPLEMENTED
502 SC_BAD_GATEWAY
503 SC_SERVICE_UNAVAILABLE
504 SC_GATEWAY_TIMEOUT
505 SC_HTTP_VERSION_NOT_SUPPORTED

5.4 流程图:Servlet请求处理流程

graph TD;
    A[客户端请求] --> B[HttpServletRequest];
    B --> C{请求类型};
    C -- GET --> D[doGet方法];
    C -- POST --> E[doPost方法];
    D --> F[处理请求];
    E --> F;
    F --> G[生成响应];
    G --> H[HttpServletResponse];
    H --> I[发送响应到客户端];

6. PyServlet

Jython发行版包含 org.python.util.PyServlet ,它可以加载、执行和缓存Jython文件,无需中间编译步骤。使用 PyServlet 需要进行Servlet映射,将特定的URL模式(如 *.py )与 PyServlet 关联。

6.1 PyServlet的限制

Jython文件必须包含一个继承自 javax.servlet.http.HttpServlet 的类,类名必须与文件名(不含 .py 扩展名)匹配。除了这个类,使用模块全局标识符是不安全的。

6.2 安装PyServlet

  • 定义Servlet映射,在 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> 
  • 确保 jython.jar 文件在上下文的 lib 目录中。

6.3 测试PyServlet

# 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 映射正确。

7. Cookies

Cookies允许在客户端机器上存储信息,以便在后续请求中包含。可以使用 javax.Servlet.http.Cookie 类创建和操作Cookies。

7.1 创建和发送Cookie示例

from javax.servlet import http 
name = "book1" 
value = "Lewis Carroll, Alice In Wonderland" 
MyNewCookie = http.Cookie(name, value) 
res.addCookie(MyNewCookie) 

7.2 Cookie属性

每个Cookie实例可以使用get和set方法或Jython的自动bean属性设置以下属性:
- comment
- domain
- maxAge
- name
- path
- secure
- value
- version

7.3 Cookies示例

# File: cookies.py 
from javax import servlet 
from javax.servlet import http 
class cookies(http.HttpServlet): 
    def doGet(self, req, res): 
        res.setContentType("text/html") 
        out = res.getOutputStream() 
        print >>out, "<html><head><title>Cookies with Jython</title></head>" 
        print >>out, """ 
            <body>\n<H2>Cookie list:</H2> 
            Remember, cookies must be enabled in your browser.<br><br>""" 
        for c in req.cookies: 
            print >>out, """ 
                <b>Cookie Name</b>= %s &nbsp; 
                <b>Value</b>= %s<br><br>""" % (c.name,c.value) 
        print >>out, """<br><br><br> 
            <HR><P>Use this form to add a new cookie</P> 
            <form action="cookies.py" method="POST"> 
            <P>Name:<br><INPUT type="text" name="name" size="30"></P> 
            <P>Value:<br><INPUT type="text" name="value" size="30"></P> 
            <P>Use the MaxAge field to set the cookie's time-to-expire. 
               A value of "0" deletes the cookie immediately,, a value of 
               "-1" saves the cookie until the browser exits,, and 
               any other integer represents seconds until it expires 
               (i.e.- using "10" would expire 10 seconds after being set)).</P> 
            <P>MaxAge:<br><INPUT type="text" name="maxAge" size="30"></P> 
            <INPUT type="submit" name="button" value="submit"> 
            \n</body> 
            \n</html> 
            """ 
    def doPost(self, req, res): 
        res.setContentType("text/html"); 
        out = res.getWriter() 
        name = req.getParameterValues("name")[0] 
        value = req.getParameterValues("value")[0] 
        maxAge = req.getParameterValues("maxAge")[0] 
        if name: 
            newCookie = http.Cookie(name, value) 
            newCookie.maxAge = int(maxAge or -1) 
            newCookie.comment = "Jython test cookie" 
            res.addCookie(newCookie) 
            print >>out, """ 
                <html><body>Cookie set successfully\n\n 
                <P>click <a href="cookies.py">here</a> 
                to view the new cookie.</P> 
                <P>If cookies are enabled in your 
                browser that is.</P> 
                \n</body> 
                \n</html>""" 
        else: 
            print >>out, """ 
                <html>\n<body> 
                Cookie not set 
                <P>No cookie "Name" provided<</P> 
                <P>click <a href="cookies">here</a> 
                to try again</P> 
                \n</body> 
                /n</html>""" 

测试该示例,确保浏览器允许使用Cookies,访问 http://localhost:8080/jython/cookies.py ,添加Cookie并验证。

8. Sessions

Cookies是创建客户端会话的常见方式,但 Java HttpSession 类使会话跟踪更加容易。

8.1 创建会话示例

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

将该文件保存到 %TOMCAT_HOME%\webapps\jython 目录,访问 http://localhost:8080/jython/session.py ,添加变量到会话并测试。

8.2 流程图:会话管理流程

graph TD;
    A[客户端请求] --> B[检查会话ID];
    B -- 存在 --> C[获取现有会话];
    B -- 不存在 --> D[创建新会话];
    C --> E[处理请求];
    D --> E;
    E --> F[更新会话数据];
    F --> G[生成响应];
    G --> H[发送响应到客户端];

9. 数据库和Servlet

在Jylet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用类似,但管理数据库连接和资源是一个主要问题。

9.1 数据库资源管理方法

  • 每个Jylet一个连接 :在Jylet的 init 方法中建立数据库连接,在Jylet卸载时关闭连接。这种方法消耗资源较多,仅在连接数量在资源限制范围内时适用。
  • 连接池 :维护一定数量的活动数据库连接,Jylet在响应客户端请求时借用连接,完成后归还。这是一种更谨慎的资源管理方法。

9.2 示例:Jython Servlet与数据库连接

# file: DBDisplay.py 
from javax.servlet import http 
from com.ziclix.python.sql import zxJDBC 
class DBDisplay(http.HttpServlet): 
    def init(self, cnfg): 
        url = "jdbc:mysql://192.168.1.77/products" 
        usr = "productsUser" 
        passwd = "secret" 
        driver = "org.gjt.mm.mysql.Driver" 
        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() 

创建数据库和表的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 查看数据库数据。

10. JSP

Java Server Pages(JSP)是Sun推荐的与Servlet互补的模板系统。目前Tomcat仅在JSP中实现Java语言,使用Jython与JSP有以下几种方式:

10.1 jythonc编译的类与JSP

  • 创建与Java兼容的Jython类,类名与模块名相同,继承自Java类,并为每个非从超类派生的方法包含 @sig 字符串。
  • jythonc 生成的类文件放在上下文的 classes 目录中,JSP页面可以像使用任何本地Java类一样使用这些类。
  • JSP文件可以通过两种方式使用 jythonc 编译的类:
    • 作为脚本片段 :可以使用任何类。
    • 作为Bean :Jython类必须符合Bean约定,为每个读写属性包含 getProperty setProperty 方法。

示例:

<%@ page import="fully.qualified.path.to.class" %> 
<html> 
<body> 
<% ClassName obj = new ClassName(); %> 
<%= obj.method() %> 
</body> 
</html> 

10.2 嵌入PythonInterpreter在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.printIn('Hello from JSP and the Jython interpreter.')"); %> 
</body> 
</html> 

interp.jsp 文件放在上下文的根目录,访问 http://localhost:8080/jython/interp.jsp 查看消息。

10.3 Jython标签库

可以使用 jythonc 将Jython标签库模块编译成Java类,创建自定义标签库。

10.3.1 创建标签库示例
# 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 
10.3.2 编译标签库
jythonc -w %TOMCAT_HOME%\webapps\jython\WEB-INF\classes JythonTag.py 
10.3.3 创建标签库描述文件
<?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 - XML\jython - taglibs.tld

10.3.4 在JSP中使用标签库
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %> 
<html> 
<body> 
<jython:message /> 
</body> 
</html> 

test.jsp 文件放在上下文的根目录,访问 http://localhost:8080/jython/test.jsp 查看自定义标签消息。

10.4 BSF

IBM的Bean Scripting Framework(BSF)支持Jython,结合BSF标签库可以在JSP文件中直接插入Jython脚本片段和表达式。

10.4.1 安装BSF相关文件
  • 下载 bsf.jar 文件,放在上下文的 lib 目录中。
  • 下载BSF标签库和 bsf.tld 文件,分别放在上下文的 lib 目录和 WEB - INF 目录中。
10.4.2 在JSP中使用BSF脚本片段
<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %> 
<html> 
<body> 
<bsf:scriptlet language="jython"> 
import random 
print >> out, random.randint(1, 100) 
</bsf:scriptlet> 
</body> 
</html> 

scriptlets.jsp 文件放在上下文的根目录,访问 http://localhost:8080/jython/scriptlets.jsp 查看结果。

通过以上内容,我们详细介绍了Jython在Web开发中与Servlet和JSP的结合应用,包括容器选择、Servlet编写、数据库连接和JSP使用等方面,希望能帮助你更好地利用Jython进行Web开发。

11. 总结与最佳实践

在使用 Jython 进行 Web 开发时,有一些总结和最佳实践可以帮助我们更高效地完成项目。

11.1 容器选择
  • Tomcat :作为参考实现,稳定性和兼容性较好,适合大多数开发场景。尤其对于初学者和小型项目,其易于安装和配置的特点使其成为首选。
  • Jetty :如果项目对性能和资源占用有较高要求,Jetty 的紧凑和高效特性会是一个不错的选择。它还能轻松与 EJB 服务器集成,适用于企业级应用。
11.2 Servlet 编写
  • 继承选择 :如果开发的应用主要基于 HTTP 协议,优先选择 HttpServlet ,它为 HTTP 方法提供了专门的处理方法,如 doGet doPost ,使代码更具可读性和可维护性。
  • 资源管理 :在 init 方法中初始化资源,在 destroy 方法中释放资源,确保资源的正确使用和释放,避免资源泄漏。
11.3 数据库连接
  • 连接池 :对于高并发的 Web 应用,使用连接池是管理数据库资源的最佳实践。它可以减少连接开销,提高性能和资源利用率。
  • 异常处理 :在数据库操作中,要进行充分的异常处理,确保应用在遇到数据库错误时能够稳定运行。
11.4 JSP 使用
  • 避免脚本片段 :脚本片段会使 JSP 页面变得复杂,建议使用 jythonc 编译的 Bean 或自定义标签库来实现动态内容,提高代码的可维护性和可测试性。

12. 常见问题与解决方案

在使用 Jython 进行 Web 开发过程中,可能会遇到一些常见问题,以下是一些解决方案。

12.1 编译问题
  • ClassCastException :如果在编译 Jython Servlet 时出现 ClassCastException ,可能是因为 servlet.jar 文件不在类路径中,或者文件名和类名不一致(包括大小写)。检查类路径和文件名、类名的一致性。
  • AttributeError :如果编译生成的类文件不在上下文的 classes 目录中,可能会出现 AttributeError 。确保类文件正确放置在指定目录中。
12.2 资源访问问题
  • ImportError :如果 Jython 的模块不可用,会出现 ImportError 。可以通过设置 python.home 属性、冻结所需模块或让每个 Servlet 显式地将模块位置添加到 sys.path 来解决。
  • Cookie 和 Session 问题 :如果客户端禁用了 Cookie,会话管理可能会出现问题。可以使用 res.encodeUrl() 方法对 URL 进行重写,支持无 Cookie 会话。
12.3 性能问题
  • 连接开销 :如果每个请求都创建和关闭数据库连接,会导致连接开销过大。可以使用连接池来减少连接开销,提高性能。
  • 脚本片段 :过多使用脚本片段会影响 JSP 页面的性能和可维护性。尽量使用 Bean 或自定义标签库来实现动态内容。

13. 未来发展趋势

随着 Web 开发技术的不断发展,Jython 在 Web 开发中的应用也可能会有一些新的发展趋势。

13.1 与新兴技术的融合
  • 微服务 :Jython 可以与微服务架构相结合,开发独立的微服务。通过容器化技术,如 Docker 和 Kubernetes,可以实现微服务的快速部署和管理。
  • 人工智能和机器学习 :Jython 可以与 Python 的人工智能和机器学习库相结合,在 Web 应用中实现智能功能,如智能推荐、图像识别等。
13.2 性能优化
  • Jython 编译器的改进 :未来 Jython 编译器可能会进行性能优化,提高编译速度和生成代码的执行效率。
  • 异步编程 :引入异步编程模型,提高 Web 应用的并发处理能力,减少响应时间。
13.3 社区支持
  • 开源项目 :随着 Jython 在 Web 开发中的应用越来越广泛,可能会有更多的开源项目出现,为开发者提供更多的工具和资源。
  • 文档和教程 :社区可能会提供更多的文档和教程,帮助开发者更好地使用 Jython 进行 Web 开发。

14. 案例分析

为了更好地理解 Jython 在 Web 开发中的应用,以下是一个简单的案例分析。

14.1 项目背景

某小型电商网站需要开发一个商品展示和管理系统,要求能够快速开发,并且具有一定的灵活性。

14.2 技术选型
  • 服务器端 :选择 Jython 作为服务器端开发语言,结合 Tomcat 作为 Servlet 容器。
  • 数据库 :使用 MySQL 数据库,通过 zxJDBC 进行数据库连接。
  • 前端 :使用 HTML、CSS 和 JavaScript 实现页面的展示和交互。
14.3 实现步骤
  1. 创建 Servlet :使用 Jython 编写 Servlet 来处理商品的展示和管理请求。
  2. 数据库连接 :在 Servlet 的 init 方法中建立数据库连接,在 destroy 方法中关闭连接。
  3. JSP 页面 :使用 JSP 页面作为模板,展示商品信息和管理界面。
  4. 测试和部署 :对系统进行测试,确保系统的稳定性和正确性,然后部署到生产环境中。
14.4 项目成果

通过使用 Jython 进行开发,项目在短时间内完成了开发和部署,并且具有良好的灵活性和可维护性。系统能够稳定运行,满足了电商网站的需求。

15. 对比分析

将 Jython 与其他 Web 开发技术进行对比分析,可以帮助我们更好地了解 Jython 的优势和劣势。

15.1 与 Java 对比
  • 开发效率 :Jython 的语法更加简洁,开发效率更高。它可以减少代码量,提高开发速度。
  • 灵活性 :Jython 具有更高的灵活性,支持动态类型和脚本编程,适合快速迭代的开发项目。
  • 性能 :Java 在性能方面通常优于 Jython,尤其是在处理大规模数据和高并发请求时。
15.2 与 Python 对比
  • 与 Java 生态系统的集成 :Jython 可以无缝集成 Java 生态系统,使用 Java 的类库和框架,这是 Python 所不具备的优势。
  • Web 开发框架 :Python 有许多成熟的 Web 开发框架,如 Django 和 Flask,而 Jython 在 Web 开发框架方面相对较少。

16. 学习资源推荐

如果你想深入学习 Jython 在 Web 开发中的应用,以下是一些学习资源推荐。

16.1 官方文档
  • Jython 官方文档 :提供了 Jython 的详细文档和教程,是学习 Jython 的重要资源。
  • Servlet 和 JSP 官方文档 :了解 Servlet 和 JSP 的规范和使用方法,对于使用 Jython 进行 Web 开发非常有帮助。
16.2 在线教程
  • W3Schools :提供了丰富的 Web 开发教程,包括 Servlet 和 JSP 的教程。
  • Codecademy :提供了交互式的编程学习平台,可以帮助你快速掌握 Jython 和 Web 开发技术。
16.3 书籍
  • 《Effective Jython》:深入介绍了 Jython 的使用技巧和最佳实践。
  • 《Servlet & JSP: A Tutorial》:详细讲解了 Servlet 和 JSP 的原理和使用方法。

17. 总结

Jython 在 Web 开发中具有独特的优势,它结合了 Python 的简洁性和 Java 的强大生态系统。通过本文的介绍,我们了解了 Jython 在 Web 开发中的应用场景、技术细节和最佳实践。希望这些内容能够帮助你更好地利用 Jython 进行 Web 开发,开发出高效、灵活的 Web 应用。

18. 流程图:Jython Web 开发整体流程

graph TD;
    A[需求分析] --> B[技术选型];
    B -- Jython、Servlet、JSP --> C[环境搭建];
    C -- 安装 Tomcat、配置 Jython --> D[Servlet 开发];
    D -- 编写 Java 或 Jython Servlet --> E[数据库连接];
    E -- 选择连接方式 --> F[JSP 开发];
    F -- 使用 jythonc 编译类或自定义标签库 --> G[测试];
    G -- 功能测试、性能测试 --> H[部署];
    H -- 部署到生产环境 --> I[维护和优化];

19. 表格:Jython Web 开发技术对比

技术 优点 缺点 适用场景
Jython Servlet 开发效率高、灵活性好 性能相对较低 快速迭代的小型项目
Java Servlet 性能高、稳定性好 开发效率低、代码量大 高并发的企业级应用
Python Web 框架 丰富的框架和工具 与 Java 生态系统集成困难 纯 Python 开发的 Web 应用

通过以上内容,我们全面介绍了 Jython 在 Web 开发中的应用,从基础概念到实际应用,再到常见问题和未来发展趋势。希望这篇博客能够帮助你更好地理解和应用 Jython 进行 Web 开发。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值