java - jsp教學 - jsp tag




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

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>

The samples are very simple but you can do lots of complicated tasks here. 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)

我是Java EE的新手,我知道類似於以下三行

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

是一種古老的編碼方式,在JSP版本2中存在一種避免JSP文件中的Java代碼的方法。 有人可以告訴我可選的JSP 2行,以及這種技術被稱為什麼?


Wicket也是將java與HTML完全分離的一種替代方案,因此設計者和程序員可以一起工作,並且可以在不同的代碼集之間相互理解。

看看Wicket。


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

除了表達式語言( EL )之外,您還可以使用選項卡庫標記(如JSTL )。 但是EL不適合JSP。 因此,完全放棄JSP並使用Facelets可能會更好。

Facelets是第一個為JSF(Java Server Faces)設計的非JSP頁面聲明語言,與JSP相比,它為JSF開發人員提供了一個更簡單,更強大的編程模型。 它解決了用於Web應用程序開發的JSP中出現的不同問題。


作為保障:禁用好的Scriptlets

另一個問題正在討論中,您可以並且始終應該禁用web.xml Web應用程序描述符中的scriptlet。

我總是這樣做,以防止任何開發人員添加腳本,特別是在大型公司,遲早你會失去概述。 web.xml設置如下所示:

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

學習使用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>

從技術上講,JSP在運行時都轉換為Servlet 。 遵循MVC模式,JSP最初是為了解耦業務邏輯和設計邏輯而創建的。 所以JSP在運行時在技術上都是Java代碼。 但要回答這個問題,標籤庫通常用於將邏輯(刪除Java代碼)應用於JSP頁面。


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.


來自Python世界的簡潔思想是模板屬性語言 ; TAL由Zope引入(因此又稱為“Zope頁面模板”,ZPT),並且是一個標準,同時還實現了PHP,XSLT和Java(我已經使用了Python / Zope和PHP化身)。 在這類模板語言中,上面的例子可能如下所示:

<table>
    <tr tal:repeat="product products">
        <td tal:content="product/name">Example product</td>
        <td tal:content="product/description">A nice description</td>
        <td tal:content="product/price">1.23</td>
    </tr>
</table>

代碼看起來像普通的HTML(或XHTML)加上XML命名空間中的一些特殊屬性; 它可以通過瀏覽器查看並安全地由設計師調整。 還支持宏和i18n:

<h1 i18n:translate="">Our special offers</h1>
<table>
    <tr tal:repeat="product products">
        <td tal:content="product/name"
            i18n:translate="">Example product</td>
        <td tal:content="product/description"
            i18n:translate="">A nice description</td>
        <td tal:content="product/price">1.23</td>
    </tr>
</table>

如果內容翻譯可用,則使用它們。

不過,我對Java的實現不太了解。


只需使用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. 表達語言


您可以將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文件中的java代碼java現在提供了像JSTL這樣的標籤庫java還提供了JSF,你可以在其中用標籤的形式編寫所有的編程結構


無論您嘗試避免多少,當您與其他開發人員一起工作時,其中一些人仍然會偏好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使用scriptlet (那些<% %>東西)的確非常令人沮喪。

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