我知道类似以下三行

<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>

是一种老式的编码方式,在JSP版本2中存在一种避免JSP文件中出现Java代码的方法。什么是可选的JSP2行,这种技术叫什么?


当前回答

如果我们在JavaWeb应用程序中使用以下内容,Java代码可以从JSP文件的前台删除。

为web应用程序使用MVC架构使用JSP标记

a.标准标签

b.自定义标签

表达式语言

其他回答

学习使用JSTL自定义和编写自己的标记

注意,EL是EviL(运行时异常和重构)。

Wicket也可能是邪恶的(对于小型应用程序或简单的视图层来说,性能和费力)。

java2s示例

必须将其添加到web应用程序的web.xml中

<taglib>
    <taglib-uri>/java2s</taglib-uri>
    <taglib-location>/WEB-INF/java2s.tld</taglib-location>
</taglib>

在/WEB-INF中创建文件java2s.tld/

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<!-- A tab library descriptor -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>Java2s Simple Tags</short-name>

    <!-- This tag manipulates its body content by converting it to upper case
    -->
    <tag>
        <name>bodyContentTag</name>
        <tag-class>com.java2s.BodyContentTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
          <name>howMany</name>
        </attribute>
    </tag>
</taglib>

将以下代码编译为WEB-INF\classes\com\java2s

package com.java2s;

import java.io.IOException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class BodyContentTag extends BodyTagSupport{
    private int iterations, howMany;

    public void setHowMany(int i){
        this.howMany = i;
    }

    public void setBodyContent(BodyContent bc){
        super.setBodyContent(bc);
        System.out.println("BodyContent = '" + bc.getString() + "'");
    }

    public int doAfterBody(){
        try{
            BodyContent bodyContent = super.getBodyContent();
            String bodyString  = bodyContent.getString();
            JspWriter out = bodyContent.getEnclosingWriter();

            if ( iterations % 2 == 0 )
                out.print(bodyString.toLowerCase());
            else
                out.print(bodyString.toUpperCase());

            iterations++;
            bodyContent.clear(); // empty buffer for next evaluation
        }
        catch (IOException e) {
            System.out.println("Error in BodyContentTag.doAfterBody()" + e.getMessage());
            e.printStackTrace();
        } // End of catch

        int retValue = SKIP_BODY;

        if ( iterations < howMany )
            retValue = EVAL_BODY_AGAIN;

        return retValue;
    }
}

启动服务器并在浏览器中加载bodyContent.jsp文件:

<%@ taglib uri="/java2s" prefix="java2s" %>
<html>
    <head>
        <title>A custom tag: body content</title>
    </head>
    <body>
        This page uses a custom tag manipulates its body content.Here is its output:
        <ol>
            <java2s:bodyContentTag howMany="3">
            <li>java2s.com</li>
            </java2s:bodyContentTag>
        </ol>
    </body>
</html>

您可以将JSTL标记与EL表达式一起使用,以避免混合Java和HTML代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
    <head>
    </head>
    <body>

        <c:out value="${x + 1}" />
        <c:out value="${param.name}" />
        // and so on

    </body>
</html>

从技术上讲,JSP在运行时都转换为Servlet。

JSP最初创建的目的是按照MVC模式分离业务逻辑和设计逻辑。因此,JSP在运行时技术上都是Java代码。

但为了回答这个问题,标记库通常用于将逻辑(删除Java代码)应用于JSP页面。

经验表明,JSP有一些缺点,其中之一是很难避免将标记与实际代码混合。

如果可以,那么可以考虑使用专门的技术来完成您需要做的事情。在JavaEE6中有JSF2.0,它提供了许多不错的功能,包括通过#{bean.method(argument)}方法将Javabean与JSF页面粘合在一起。

自2001年taglibs(如JSTL)和EL(表达式语言,那些${}的东西)诞生以来,在JSP中使用scriptlets(那些<%%>的东西)确实非常不受欢迎。

scriptlets的主要缺点是:

可重用性:不能重用scriptlets。可替换性:不能使scriptlets抽象。OO能力:你不能利用继承/组合。可调试性:如果scriptlet中途抛出异常,则只会得到一个空白页面。可测试性:scriptlets不是可单元测试的。可维护性:每个saldo需要更多的时间来维护混合/杂乱/重复的代码逻辑。

Sun Oracle本身也建议在JSP编码约定中避免在(标记)类可能具有相同功能时使用scriptlet。以下是几个相关的例子:

根据JSP1.2规范,强烈建议在web应用程序中使用JSP标准标记库(JSTL),以帮助减少页面中对JSP脚本的需求。通常,使用JSTL的页面更容易阅读和维护。...如果可能,只要标记库提供了等效的功能,就避免使用JSP脚本。这使得页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分开,并将使您的页面更容易演变为JSP 2.0风格的页面(JSP 2.0规范支持但不强调scriptlets的使用)。...本着采用模型-视图-控制器(MVC)设计模式来减少表示层与业务逻辑之间的耦合的精神,JSP脚本不应用于编写业务逻辑。相反,如果需要,可以使用JSP脚本将处理客户端请求返回的数据(也称为“值对象”)转换为适合客户端的格式。即使这样,使用前端控制器servlet或自定义标记也会更好。


如何替换scriptlet完全取决于代码/逻辑的唯一目的。这段代码通常被放在一个完整的Java类中:

如果您希望在每个请求中调用相同的Java代码,无论请求的页面是多少,例如,检查用户是否登录,那么实现一个过滤器并在doFilter()方法中相应地编写代码。例如。:public void doFilter(ServletRequest请求、ServletResponse响应、FilterChain链)抛出ServletException、IOException{if(((HttpServletRequest)请求).getSession().getAttribute(“user”)==null){((HttpServletResponse)响应).sendRedirect(“登录”);//未登录,重定向到登录页面。}其他{chain.doFilter(请求,响应);//已登录,请继续请求。}}当映射到覆盖感兴趣的JSP页面的适当的<url模式>时,就不需要复制粘贴整个JSP页面的同一段代码。如果您想调用一些Java代码来处理GET请求,例如,从数据库中预加载一些列表以显示在某个表中,如果需要,可以基于一些查询参数,然后实现一个servlet并在doGet()方法中相应地编写代码。例如。:protected void doGet(HttpServletRequest请求,HttpServletResponse响应)throws ServletException,IOException{尝试{List<Product>products=productService.List();//获取所有产品。request.setAttribute(“products”,products);//将产品存储在请求范围内。request.getRequestDispatcher(“/WEB-INF/products.jsp”).forward(请求,响应);//转发到JSP页面以在HTML表中显示它们。}catch(SQLException e){抛出新的ServletException(“检索产品失败!”,e);}}这种处理异常的方法更容易。数据库不是在JSP呈现过程中访问的,而是在显示JSP之前访问的。您仍然可以在DB访问引发异常时更改响应。在上面的示例中,将显示默认的错误500页面,您可以通过web.xml中的<errorpage>自定义该页面。如果您想调用一些Java代码来处理POST请求,例如从提交的HTML表单中收集数据并使用它进行一些业务处理(转换、验证、保存在DB中等),那么就实现一个servlet并在doPost()方法中相应地编写代码。例如。:protected void doPost(HttpServletRequest请求,HttpServletResponse响应)throws ServletException,IOException{字符串username=request.getParameter(“username”);字符串密码=request.getParameter(“password”);用户User=userService.find(用户名、密码);if(用户!=空){request.getSession().setAttribute(“user”,用户);//登录用户。response.sendRedirect(“home”);//重定向至主页。}其他{request.setAttribute(“message”,“未知用户名/密码。请重试。”);//将错误消息存储在请求范围中。request.getRequestDispatcher(“/WEB-INF/login.jsp”).forward(请求,响应);//转发到JSP页面以重新显示出现错误的登录表单。}}这种处理不同结果页目的地的方法更容易:在出现错误时重新显示带有验证错误的表单(在这个特定的示例中,您可以使用EL中的${message}重新显示它),或者在成功时只需转到所需的目标页。如果您想调用一些Java代码来控制执行计划和/或请求和响应的目标,那么根据MVC的Front Controller Pattern实现servlet。例如。:受保护的void服务(HttpServletRequest请求、HttpServletResponse响应)引发ServletException、IOException{尝试{Action Action=ActionFactory.getAction(请求);字符串视图=action.execute(请求,响应);if(view.equals(request.getPathInfo().substring(1)){request.getRequestDispatcher(“/WEB-INF/”+view+“.jsp”).forward(请求,响应);}其他{response.sendRedirect(视图);}}catch(异常e){抛出新ServletException(“执行操作失败。”,e);}}或者只是采用一个MVC框架,如JSF、Spring MVC、Wicket等,这样您最终只需要一个JSP/Facelets页面和一个JavaBean类,而不需要定制servlet。如果您想调用一些Java代码来控制JSP页面内的流,那么需要获取一个(现有的)流控制标记库,如JSTL核心。例如,在表格中显示列表<产品>:<%@taglib uri=“http://java.sun.com/jsp/jstl/core“prefix=”c“%>”...<表><c:forEach items=“${products}”var=“product”><tr><td>${product.name}</td><td>${product.description}</td><td>${product.price}</td></tr></c:forEach></table>由于XML样式的标记非常适合所有HTML,因此代码的可读性(因此可维护性更好)比带有各种大括号和大括号(“这个大括号到底属于哪里?”)的一堆scriptlet要好。一个简单的帮助是,通过向web.xml添加以下部分,配置web应用程序,以便在仍然使用scriptlet时抛出异常:<jsp配置><jsp属性组><url模式>*.jsp</url模式><脚本无效>true</脚本无效></jsp属性组></jsp配置>在Facelets(JSP的继承者)中,它是JavaEE提供的MVC框架JSF的一部分,已经不可能使用scriptlets。这样你就自动被迫以“正确的方式”做事。如果您想调用一些Java代码来访问和显示JSP页面中的“后端”数据,那么您需要使用EL(表达式语言),这些${}的东西。例如,重新显示提交的输入值:<input-type=“text”name=“foo”value=“${param.foo}”/>${param.foo}显示request.getParameter(“foo”)的结果。如果您想直接在JSP页面中调用一些实用程序Java代码

另请参见:

JSP、Servlet和JSF之间有什么区别?Servlet、ServletContext、HttpSession和HttpServletRequest/Response是如何工作的?使用JSP、Servlet和JDBC的基本MVC示例Java web应用程序中的设计模式JSP/Servlet的隐藏特性