java - jsp写html - jsp页面




如何避免JSP文件中的Java代码? (19)

我是Java EE的新手,我知道类似于以下三行

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

是一种古老的编码方式,在JSP版本2中存在一种避免JSP文件中的Java代码的方法。 有人可以告诉我可选的JSP 2行,以及这种技术被称为什么?


JSP 2.0有一个名为“标记文件”的功能 ,您可以在没有外部java代码和tld情况下编写标记。 您需要创建一个.tag文件并将其放入WEB-INF\tags您甚至可以创建目录结构来打包标签。

例如:

/WEB-INF/tags/html/label.tag

<%@tag description="Rensders a label with required css class" pageEncoding="UTF-8"%>
<%@attribute name="name" required="true" description="The label"%>

<label class="control-label control-default"  id="${name}Label">${name}</label>

像使用它

<%@ taglib prefix="h" tagdir="/WEB-INF/tags/html"%>
<h:label  name="customer name" />

您也可以轻松阅读标签主体

/WEB-INF/tags/html/bold.tag
<%@tag description="Bold tag" pageEncoding="UTF-8"%>
<b>
  <jsp:doBody/>
</b>

用它

<%@ taglib prefix="h" tagdir="/WEB-INF/tags/bold"%>
<h:bold>Make me bold</h:bold>

这些样本非常简单,但您可以在这里做很多复杂的任务。 Please consider you can use other tags (eg: JSTL which has controlling tags like if/forEcah/chosen text manipulation like format/contains/uppercase or even SQL tags select/update ), pass all kind parameters, for example Hashmap , access session , request , ... in your tag file too.

Tag File are so easy developed as you did not need to restart the server when changing them, like jsp files. This make them easy for development.

Even if you use a framework like struts 2, which have lots of good tags, you may find that having your own tags can reduce your code a lot. You can pass your tag parameters to struts and this way customize your framework tag.

You can use tag not only to avoid java but also minimize your HTML codes. I myself try to review HTML codes and build tags a lot as soon as see code duplicates start in my pages.

(Even if you end up using the java in you jsp code, which I hope not, you can encapsulate that code in a tag)


Wicket也是将java与HTML完全分离的一种替代方案,因此设计者和程序员可以一起工作,并且可以在不同的代码集之间相互理解。

看看Wicket。


如何避免JSP文件中的Java代码?

除了表达式语言( EL )之外,您还可以使用选项卡库标记(如JSTL )。 但是EL不适合JSP。 因此,完全放弃JSP并使用Facelets可能会更好。

Facelets是第一个为JSF(Java Server Faces)设计的非JSP页面声明语言,与JSP相比,它为JSF开发人员提供了一个更简单,更强大的编程模型。 它解决了用于Web应用程序开发的JSP中出现的不同问题。


从技术上讲,JSP在运行时都转换为Servlet 。 遵循MVC模式,JSP最初是为了解耦业务逻辑和设计逻辑而创建的。 所以JSP在运行时在技术上都是Java代码。 但要回答这个问题,标签库通常用于将逻辑(删除Java代码)应用于JSP页面。


如果有人真的反对使用更多语言进行编程 ,我建议使用GWT,理论上可以避免使用所有JS和HTML元素,因为Google Toolkit将所有客户端和共享代码转换为JS,所以您不会遇到问题,所以你有一个没有任何其他语言编码的web服务。 即使你可以使用某些默认的CSS,因为它是由扩展(smartGWT或Vaadin)给出的。 你不需要学习几十个注释。

当然,如果你愿意的话,你可以深入到代码的深处,注入JS并丰富你的HTML页面,但是如果你愿意的话,你可以避免它,结果会很好,因为它是在任何其他框架中编写的。 我认为值得一试,基本的GWT是有据可查的。

当然,许多程序员在此描述或推荐其他几种解决方案。 GWT适用于那些真正不想处理Web部分或将其最小化的人。


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

请注意,EL是EviL (运行时异常,重构)
Wicket也可能是邪恶的(性能,小应用程序或简单的视图层困难)

来自java2s的示例,

这必须添加到Web应用程序的web.xml中

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

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

<!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>

Nothing of that is used anymore my friend, my advice is to decouple the view(css, html, javascript, etc) from the server.

In my case I do my systems handling the view with Angular and any data needed is brought from the server using rest services.

Believe me, this will change the way you design


Using Scriptlets is a very old way and Not recommended. If you want directly output something in your JSP pages just use Expression Language(EL) along with JSTL .

There are also other options such as using a templating engine such as Velocity, Freemarker, Thymeleaf etc. But using plain JSP with EL and JSTL serves my purpose most of the time and it also seems the simplest for a beginner.

Also, take note that it is not a best practice to do business logic in the view layer, you should perform your business logics in the Service layer, and pass the output result to your views through a Controller.


你提出了一个很好的问题,虽然你有很好的答案,但我建议你摆脱JSP。 这是过时的技术,最终会死亡。 使用现代的方法,如模板引擎。 您将业务和表示层分离得非常清晰,模板中肯定没有Java代码,所以您可以直接从Web演示编辑软件生成模板,大多数情况下都可以利用所见即所得。

当然,远离过滤器和前后处理,否则你可能会遇到支持/调试困难,因为你总是不知道变量的价值在哪里。


只需使用JSTL标签和EL表达式即可。



在MVC架构模式中,JSP表示View层。 在JSP中嵌入Java代码被认为是不好的做法。 您可以使用JSTLfreeMarker ,以JSP作为“模板引擎”的velocity 。 这些标签的数据提供者依赖于你正在处理的框架Struts 2webwork作为MVC模式的实现使用OGNL “非常有趣的技术将Bean属性公开到JSP”。


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

  1. 使用MVC体系结构进行Web应用程序

  2. 使用JSP标签

    一个。 标准标签

    湾 自定义标签

  3. 表达语言


当然,请替换<%! counter++; %> <%! counter++; %> <%! counter++; %>通过事件生产者 - 消费者体系结构(其中业务层被通知需要增加计数器),它作出相应反应,并通知演示者,以便他们更新视图。 涉及许多数据库交易,因为将来我们需要知道柜台的新旧价值,他们已经增加了它,并考虑到了什么目的。 显然涉及序列化,因为这些图层完全分离。 你将能够通过RMI,IIOP,SOAP增加你的计数器。 但是只有HTML是必需的,你不会实现,因为它是一个普通的案例。 您的新目标是在新的闪亮E7,64GB RAM服务器上每秒增加250个增量。

我有20多年的编程经验,大部分项目在六重奏之前都失败了:可重用性可替换性面向对象的可调试性可测试性甚至还需要可维护性。 其他项目由只关心功能的人员运作,非常成功。 此外,在项目实施得太早的僵硬的对象结构使得代码无法适应规范(也称为敏捷)的剧烈变化。

所以我认为拖延就是在项目早期或者没有特别要求时定义“层”或冗余数据结构的活动。


我不知道如果我得到这个正确的。

你应该读一些关于MVC的东西。 Spring MVCStruts 2是两种最常见的解决方案。


无论您尝试避免多少,当您与其他开发人员一起工作时,其中一些人仍然会偏好scriptlet,然后将恶意代码插入到项目中。 因此,如果您真想减少scriptlet代码,在第一个符号处设置项目非常重要。 有几种技术可以解决这个问题(包括其他提到的几个框架)。 但是,如果您更喜欢纯粹的JSP方式,那么请使用JSTL标签文件。 关于这一点的好处是你也可以为你的项目设置母版页,所以其他页可以继承母版页

使用以下内容在WEB-INF /标签下创建名为base.tag的母版页

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>

<%@attribute name="title" fragment="true" %>

<html>
  <head>
    <title>  
       <jsp:invoke fragment="title"></jsp:invoke>
    </title>

  </head>
  <body>
    <div id="page-header">
       ....
    </div>
    <div id="page-body">
      <jsp:doBody/>
    </div>
    <div id="page-footer">
      .....
    </div>
  </body>
</html>

在这个主页上,我创建了一个名为“title”的片段,这样在子页面中,我可以在主页面的这个位置插入更多代码。 此外,标签<jsp:doBody/>将被替换为子页面的内容

在WebContent文件夹中创建子页面(child.jsp):

<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:base>
    <jsp:attribute name="title"> 
        <bean:message key="hello.world" />
    </jsp:attribute>

    <jsp:body>
    [Put your content of the child here]
    </jsp:body>   
</t:base>

<t:base>用于指定要使用的母版页(此时为base.tag)。 标签<jsp:body>所有内容将取代您的母版页上的<jsp:doBody/> 。 您的子页面也可以包含任何标签库,您可以像使用其他提及的那样正常使用它。 但是,如果您在此处使用任何scriptlet代码( <%= request.getParameter("name") %> ...)并尝试运行此页面,则将获得JasperException because Scripting elements ( &lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) are disallowed here 。 因此,其他人无法将邪恶的代码包含到jsp文件中

从您的控制器调用此页面:

您可以轻松地从您的控制器调用child.jsp文件。 这也适用于struts框架


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

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


自从十年前taglibs (如JSTL )和EL ( EL ,这些${}东西)的诞生以来, JSP使用scriptlets (那些<% %>东西)的确非常令人沮丧。

scriptlet的主要缺点是:

  1. 可重用性:您无法重用脚本。
  2. 可替换性:您不能使脚本抽象化。
  3. OO能力:你不能使用继承/组合。
  4. 可调试性:如果scriptlet中途抛出一个异常,你得到的只是一个空白页面。
  5. 可测试性: scriptlet不是单元可测试的。
  6. 可维护性:每个saldo需要更多的时间来维护混合/混乱/重复的代码逻辑。

Sun Oracle本身也建议在JSP编码约定中避免使用scriptlet,只要(tag)类可以实现相同的功能。 这里有几个相关的例子:

从JSP 1.2规范中,强烈建议您在Web应用程序中使用JSP标准标记库(JSTL),以帮助减少对页面中JSP脚本的需求 。 一般来说,使用JSTL的页面更易于阅读和维护。

...

在可能的情况下,每当标记库提供相同的功能时,应避免JSP scriptlet 这使得页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分开,并使您的页面更容易演化为JSP 2.0风格的页面(JSP 2.0规范支持,但是强调使用scriptlet)。

...

本着采用模型 - 视图 - 控制器(MVC)设计模式以减少表示层与业务逻辑之间的耦合的精神, JSP脚本不应用于编写业务逻辑。 相反,如果需要使用JSP脚本来将从处理客户端请求返回的数据(也称为“值对象”)转换为适当的客户端就绪格式。 即使如此,使用前端控制器servlet或自定义标签也可以做得更好。

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

  • 如果您希望在每个请求上调用相同的 Java代码,那么不管请求的页面如何,例如检查用户是否已登录,然后实现filter并在doFilter()方法中相应地编写代码。 例如:

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
            ((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page.
        } else {
            chain.doFilter(request, response); // Logged in, just continue request.
        }
    }
    

    当映射到覆盖感兴趣的JSP页面的适当<url-pattern> ,则不需要在所有JSP页面上复制同一段代码。

  • 如果您想调用一些Java代码来预处理请求,例如从数据库中预加载某些列表以显示在某个表中,必要时根据某些查询参数进行显示,然后在doGet()方法中相应地实现一个servlet并编写代码。 例如:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            List<Product> products = productService.list(); // Obtain all products.
            request.setAttribute("products", products); // Store products in request scope.
            request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table.
        } catch (SQLException e) {
            throw new ServletException("Retrieving products failed!", e);
        }
    }
    

    这样处理异常就容易了。 在JSP呈现过程中,数据库不会被访问,而是在JSP显示之前。 只要数据库访问引发异常,您仍然可以更改响应。 在上面的例子中,将会显示默认错误500页面,您可以通过web.xml<error-page>自定义该<error-page>

  • 如果您想调用一些Java代码来后处理请求,例如处理表单提交,那么在doPost()方法中实现一个servlet并相应地编写代码。 例如:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userService.find(username, password);
    
        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            response.sendRedirect("home"); // Redirect to home page.
        } else {
            request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope.
            request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error.
        }
    }
    

    这种方式处理不同的结果页面目的地更容易:在发生错误的情况下重新显示带有验证错误的表单(在这个特定的例子中,您可以在EL使用${message}重新显示它),或者仅在需要的情况下进入所需的目标页面的成功。

  • 如果您想要调用一些Java代码来控制执行计划和/或请求和响应的目的地,请根据MVC的Front Controller Pattern实现一个servlet 。 例如:

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            Action action = ActionFactory.getAction(request);
            String view = action.execute(request, response);
    
            if (view.equals(request.getPathInfo().substring(1)) {
                request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
            } else {
                response.sendRedirect(view);
            }
        } catch (Exception e) {
            throw new ServletException("Executing action failed.", e);
        }
    }
    

    或者只是采用像JSF , Spring MVC , Wicket等MVC框架,以便最终只需一个JSP / Facelets页面和一个Javabean类,而不需要定制servlet。

  • 如果您想调用一些Java代码来控制 JSP页面内的流程 ,那么您需要获取(现有的)流控制标签库,如JSTL内核 。 例如,在表格中显示List<Product>

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    ...
    <table>
        <c:forEach items="${products}" var="product">
            <tr>
                <td>${product.name}</td>
                <td>${product.description}</td>
                <td>${product.price}</td>
            </tr>
        </c:forEach>
    </table>
    

    使用适合所有HTML的XML风格的标签,代码的可读性更好(因此可以更好地维护),而不是一堆带有各种开启和关闭大括号的脚本( “这个大括号属于哪里?” )。 一个简单的帮助是配置您的Web应用程序,以便在通过将以下代码片段添加到web.xml时仍然使用scriptlet时引发异常:

    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <scripting-invalid>true</scripting-invalid>
        </jsp-property-group>
    </jsp-config>
    

    在Facelets ,作为Java EE提供的MVC框架JSF一部分的JSP的继承者,已经不可能使用scriptlet 。 这样你就会自动被迫做“正确的方式”。

  • 如果你想调用一些Java代码来访问和显示 JSP页面中的“后端”数据,那么你需要使用EL(表达式语言)这些${}东西。 例如重新显示提交的输入值:

    <input type="text" name="foo" value="${param.foo}" />
    

    ${param.foo}显示request.getParameter("foo")

  • 如果您想在JSP页面中直接调用一些实用 Java代码(通常是public static方法),那么您需要将它们定义为EL函数。 JSTL中有一个标准的函数taglib ,但你也可以自己轻松地创建函数 。 以下是JSTL fn:escapeXml用于防止XSS attacks的示例。

    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    ...
    <input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
    

    请注意,XSS敏感性与Java / JSP / JSTL / EL /无关,无论如何,在您开发的每个 Web应用程序中需要考虑这个问题。 scriptlet的问题在于它不提供内置的预防方法,至少不使用标准的Java API。 JSP的继任者Facelets已经隐式HTML转义,所以你不需要担心Facelets中的XSS漏洞。

也可以看看:


通过将JSTL标签与EL表达式一起使用,您可以避免这种情况。把下面的东西放在你的jsp页面中:

<%@ 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" %>




scriptlet