JSP(JavaServer Pages)是一种用于开发动态Web页面的技术,它允许在HTML页面中嵌入Java代码。在JSP技术中,指令是向JSP容器提供特殊指示的关键元素,它们不直接生成可见输出,而是控制JSP页面的整体行为。JSP指令主要有三种:page指令、include指令和taglib指令。本文将详细解析这三大指令的功能特点、语法属性以及实际应用场景,帮助开发者更好地理解和运用JSP指令。

1. page指令详解

page指令是JSP中最常用的指令之一,用于定义整个JSP页面的属性。它通常放在JSP页面的顶部,一个页面可以有多个page指令,但其中的属性(除了import)只能设置一次。

1.1 功能特点

page指令的主要功能包括:

  • 设置页面的脚本语言
  • 导入需要的Java类
  • 指定页面输出的内容类型和字符编码
  • 设置会话管理
  • 错误页面配置
  • 缓冲控制
  • 线程安全设置

1.2 语法结构

<%@ page attribute1="value1" attribute2="value2" ... %> 

1.3 主要属性详解

1.3.1 基本属性

  1. language:指定页面使用的脚本语言,默认为”java”。

    <%@ page language="java" %> 
  2. extends:指定JSP页面生成的Servlet类所继承的父类。

    <%@ page extends="com.example.CustomHttpJspPage" %> 
  3. import:导入页面中需要使用的Java类,可以指定多个类,用逗号分隔。

    <%@ page import="java.util.List, java.util.ArrayList" %> 

1.3.2 会话与缓冲属性

  1. session:设置页面是否参与HTTP会话,值为”true”或”false”,默认为”true”。

    <%@ page session="true" %> 
  2. buffer:设置输出缓冲区的大小,以kb为单位,默认为”8kb”,也可以设置为”none”。

    <%@ page buffer="16kb" %> 
  3. autoFlush:设置缓冲区满时是否自动刷新,值为”true”或”false”,默认为”true”。

    <%@ page autoFlush="true" %> 

1.3.3 线程与错误处理属性

  1. isThreadSafe:设置页面是否是线程安全的,值为”true”或”false”,默认为”true”。

    <%@ page isThreadSafe="true" %> 
  2. errorPage:指定当页面发生未捕获的异常时要转发的错误页面。

    <%@ page errorPage="error.jsp" %> 
  3. isErrorPage:设置当前页面是否为错误处理页面,值为”true”或”false”,默认为”false”。

    <%@ page isErrorPage="true" %> 

1.3.4 内容与编码属性

  1. contentType:设置响应的MIME类型和字符编码。

    <%@ page contentType="text/html; charset=UTF-8" %> 
  2. pageEncoding:设置JSP文件本身的字符编码。

    <%@ page pageEncoding="UTF-8" %> 
  3. isELIgnored:设置是否忽略EL表达式,值为”true”或”false”,默认为”false”。

    <%@ page isELIgnored="false" %> 
  4. info:设置页面的描述信息,可以通过Servlet.getServletInfo()方法获取。

    <%@ page info="This is a sample JSP page" %> 

1.4 实际应用示例

1.4.1 基本页面配置示例

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.List, java.util.ArrayList, java.util.Date" %> <%@ page session="true" buffer="16kb" autoFlush="true" %> <!DOCTYPE html> <html> <head> <title>Page Directive Example</title> </head> <body> <h1>Current Date: <%= new Date() %></h1> <% List<String> items = new ArrayList<>(); items.add("Item 1"); items.add("Item 2"); items.add("Item 3"); %> <h2>Items List:</h2> <ul> <% for(String item : items) { %> <li><%= item %></li> <% } %> </ul> </body> </html> 

在这个示例中,我们使用了多个page指令属性:

  • 设置了语言为Java
  • 指定了内容类型和字符编码为UTF-8
  • 导入了需要的Java类
  • 启用了会话管理
  • 设置了缓冲区大小为16kb
  • 启用了自动刷新功能

1.4.2 错误页面配置示例

主页面 (main.jsp):

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page errorPage="error.jsp" %> <!DOCTYPE html> <html> <head> <title>Main Page</title> </head> <body> <h1>Main Page</h1> <% // This will cause an exception int result = 10 / 0; %> <p>Result: <%= result %></p> </body> </html> 

错误页面 (error.jsp):

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page isErrorPage="true" %> <!DOCTYPE html> <html> <head> <title>Error Page</title> </head> <body> <h1>Error Occurred</h1> <p>An exception has occurred:</p> <p><%= exception.getMessage() %></p> </body> </html> 

在这个示例中,main.jsp使用errorPage属性指定了错误处理页面为error.jsp。当main.jsp中发生未捕获的异常(如除零错误)时,容器会自动将请求转发到error.jsp。error.jsp使用isErrorPage=“true”属性声明自己是一个错误处理页面,从而可以访问exception隐式对象。

1.4.3 会话管理示例

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page session="true" %> <!DOCTYPE html> <html> <head> <title>Session Management</title> </head> <body> <h1>Session Management Example</h1> <% // Get session HttpSession currentSession = request.getSession(); // Set session attribute currentSession.setAttribute("username", "JohnDoe"); // Get session attribute String username = (String) currentSession.getAttribute("username"); %> <p>Welcome, <%= username %>!</p> <p>Session ID: <%= currentSession.getId() %></p> <p>Session Creation Time: <%= new Date(currentSession.getCreationTime()) %></p> </body> </html> 

在这个示例中,我们使用session=“true”属性启用了会话管理。然后,我们获取HttpSession对象,设置和获取会话属性,并显示会话ID和创建时间。如果设置session=“false”,则无法使用HttpSession对象,任何尝试访问会话的代码都会导致错误。

2. include指令详解

include指令用于在JSP页面转换阶段(编译阶段)包含其他文件的内容。被包含的文件可以是JSP页面、HTML文件或文本文件。包含的内容会成为JSP页面的一部分,与主页面一起编译。

2.1 功能特点

include指令的主要功能包括:

  • 在JSP转换阶段静态包含其他文件
  • 被包含文件的内容成为主页面的一部分
  • 适用于包含多个页面共用的头部、底部或导航栏
  • 可以提高代码的重用性和维护性

2.2 语法结构

<%@ include file="relativeURL" %> 

其中,file属性指定要包含的文件的相对URL。

2.3 与jsp:include的区别

在JSP中,有两种包含其他文件的方式:include指令和jsp:include动作。它们之间有重要的区别:

特性include指令jsp:include动作
处理阶段转换阶段(编译时)请求阶段(运行时)
包含方式静态包含动态包含
资源处理被包含文件的内容成为主页面的一部分被包含资源的输出被包含到主页面中
性能较高(只编译一次)较低(每次请求都处理)
参数传递不能直接传递参数可以通过jsp:param传递参数
语法<%@ include file="relativeURL" %><jsp:include page="relativeURL" />

2.4 实际应用示例

2.4.1 包含公共头部和底部示例

header.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <header> <h1>My Website</h1> <nav> <ul> <li><a href="home.jsp">Home</a></li> <li><a href="about.jsp">About</a></li> <li><a href="contact.jsp">Contact</a></li> </ul> </nav> </header> <hr> 

footer.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <hr> <footer> <p>&copy; 2023 My Website. All rights reserved.</p> </footer> 

home.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <title>Home Page</title> </head> <body> <%@ include file="header.jsp" %> <main> <h2>Welcome to the Home Page</h2> <p>This is the content of the home page.</p> </main> <%@ include file="footer.jsp" %> </body> </html> 

在这个示例中,我们创建了三个文件:header.jsp(包含网站的头部导航)、footer.jsp(包含网站的底部信息)和home.jsp(主页)。home.jsp使用include指令包含了header.jsp和footer.jsp,实现了代码的重用。当需要修改网站的头部或底部时,只需修改相应的包含文件,所有使用这些文件的页面都会自动更新。

2.4.2 包含公共函数库示例

functions.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.text.SimpleDateFormat" %> <%! // Function to format date public String formatDate(java.util.Date date) { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); return sdf.format(date); } // Function to calculate sum public int calculateSum(int a, int b) { return a + b; } %> 

calculator.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.Date" %> <!DOCTYPE html> <html> <head> <title>Calculator</title> </head> <body> <%@ include file="functions.jsp" %> <h1>Calculator</h1> <% int num1 = 10; int num2 = 20; int sum = calculateSum(num1, num2); Date currentDate = new Date(); %> <p>Today's date: <%= formatDate(currentDate) %></p> <p>The sum of <%= num1 %> and <%= num2 %> is: <%= sum %></p> </body> </html> 

在这个示例中,functions.jsp包含了一些公共函数,如formatDate和calculateSum。calculator.jsp通过include指令包含了functions.jsp,从而可以直接调用这些函数。这种方式可以实现函数的重用,避免在多个页面中重复定义相同的函数。

2.4.3 包含配置信息示例

config.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // Database configuration String dbUrl = "jdbc:mysql://localhost:3306/mydatabase"; String dbUser = "admin"; String dbPassword = "password123"; // Application settings String appTitle = "My Web Application"; String adminEmail = "admin@example.com"; %> 

dataViewer.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.sql.*" %> <!DOCTYPE html> <html> <head> <title><%@ include file="config.jsp" %><%= appTitle %></title> </head> <body> <%@ include file="config.jsp" %> <h1><%= appTitle %></h1> <% Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // Load driver Class.forName("com.mysql.jdbc.Driver"); // Connect to database conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword); // Create statement stmt = conn.createStatement(); // Execute query rs = stmt.executeQuery("SELECT * FROM users"); %> <table border="1"> <tr> <th>ID</th> <th>Name</th> <th>Email</th> </tr> <% while (rs.next()) { %> <tr> <td><%= rs.getInt("id") %></td> <td><%= rs.getString("name") %></td> <td><%= rs.getString("email") %></td> </tr> <% } %> </table> <% } catch (Exception e) { e.printStackTrace(); } finally { // Close resources if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } %> <p>Contact administrator: <%= adminEmail %></p> </body> </html> 

在这个示例中,config.jsp包含了一些配置信息,如数据库连接参数和应用程序设置。dataViewer.jsp通过include指令包含了config.jsp,从而可以使用这些配置信息。这种方式可以实现配置的集中管理,便于维护和修改。

3. taglib指令详解

taglib指令用于声明JSP页面所使用的标签库,并指定标签库的前缀。通过使用标签库,可以在JSP页面中使用自定义标签,简化页面开发,提高代码的可读性和可维护性。

3.1 功能特点

taglib指令的主要功能包括:

  • 引入标签库描述符(TLD)文件
  • 指定标签库的前缀
  • 支持JSTL(JSP Standard Tag Library)等标准标签库
  • 支持自定义标签库
  • 简化JSP页面的开发,减少Java代码的使用

3.2 语法结构

<%@ taglib uri="tagLibraryURI" prefix="tagPrefix" %> 

其中:

  • uri属性指定标签库的唯一标识符,可以是绝对URL、相对URL或TLD文件的路径。
  • prefix属性指定标签库的前缀,用于在JSP页面中引用标签库中的标签。

3.3 常用标签库介绍

3.3.1 JSTL Core标签库

JSTL Core标签库提供了基本的流程控制、迭代和变量操作等功能。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 

常用标签:

  • <c:out>:输出表达式的值
  • <c:set>:设置变量值
  • <c:if>:条件判断
  • <c:choose>, <c:when>, <c:otherwise>:多重条件判断
  • <c:forEach>:循环
  • <c:forTokens>:迭代令牌
  • <c:url>:创建URL
  • <c:redirect>:重定向
  • <c:param>:设置参数

3.3.2 JSTL Format标签库

JSTL Format标签库提供了国际化和格式化功能。

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> 

常用标签:

  • <fmt:formatDate>:格式化日期
  • <fmt:parseDate>:解析日期
  • <fmt:formatNumber>:格式化数字
  • <fmt:parseNumber>:解析数字
  • <fmt:bundle>:指定资源包
  • <fmt:message>:显示消息
  • <fmt:setLocale>:设置区域
  • <fmt:timeZone>:设置时区
  • <fmt:setTimeZone>:设置时区变量

3.3.3 JSTL SQL标签库

JSTL SQL标签库提供了数据库操作功能。

<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> 

常用标签:

  • <sql:query>:执行SQL查询
  • <sql:update>:执行SQL更新
  • <sql:setDataSource>:设置数据源
  • <sql:param>:设置参数
  • <sql:dateParam>:设置日期参数

3.3.4 JSTL XML标签库

JSTL XML标签库提供了XML处理功能。

<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %> 

常用标签:

  • <x:parse>:解析XML
  • <x:out>:输出XML
  • <x:forEach>:迭代XML节点
  • <x:if>:条件判断
  • <x:choose>, <x:when>, <x:otherwise>:多重条件判断
  • <x:transform>:XSL转换

3.4 实际应用示例

3.4.1 使用JSTL Core标签库示例

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <title>JSTL Core Example</title> </head> <body> <h1>JSTL Core Tags Example</h1> <%-- Set variables --%> <c:set var="username" value="JohnDoe" scope="session" /> <c:set var="items" value="${['Item 1', 'Item 2', 'Item 3']}" /> <%-- Output variable --%> <p>Welcome, <c:out value="${username}" /></p> <%-- Conditional check --%> <c:if test="${not empty username}"> <p>You are logged in as ${username}.</p> </c:if> <%-- Multiple conditions --%> <c:choose> <c:when test="${username == 'admin'}"> <p>You have administrator privileges.</p> </c:when> <c:when test="${username == 'JohnDoe'}"> <p>You are a regular user.</p> </c:when> <c:otherwise> <p>You are a guest.</p> </c:otherwise> </c:choose> <%-- Loop through items --%> <h2>Items List:</h2> <ul> <c:forEach var="item" items="${items}"> <li>${item}</li> </c:forEach> </ul> <%-- Loop with step --%> <h2>Numbers from 1 to 10 (even only):</h2> <ul> <c:forEach var="i" begin="1" end="10" step="2"> <li>${i}</li> </c:forEach> </ul> <%-- URL with parameters --%> <c:url value="profile.jsp" var="profileUrl"> <c:param name="id" value="123" /> <c:param name="action" value="view" /> </c:url> <p><a href="${profileUrl}">View Profile</a></p> </body> </html> 

在这个示例中,我们使用了JSTL Core标签库的多个标签:

  • 使用<c:set>设置变量
  • 使用<c:out>输出变量值
  • 使用<c:if>进行条件判断
  • 使用<c:choose>, <c:when>, <c:otherwise>进行多重条件判断
  • 使用<c:forEach>进行循环迭代
  • 使用<c:url><c:param>创建带参数的URL

这些标签大大减少了页面中的Java代码,使页面更加清晰易读。

3.4.2 使用JSTL Format标签库示例

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <!DOCTYPE html> <html> <head> <title>JSTL Format Example</title> </head> <body> <h1>JSTL Format Tags Example</h1> <%-- Set current date --%> <jsp:useBean id="now" class="java.util.Date" /> <%-- Format date in different styles --%> <h2>Date Formatting:</h2> <p>Default: <fmt:formatDate value="${now}" /></p> <p>Short: <fmt:formatDate value="${now}" dateStyle="short" /></p> <p>Medium: <fmt:formatDate value="${now}" dateStyle="medium" /></p> <p>Long: <fmt:formatDate value="${now}" dateStyle="long" /></p> <p>Full: <fmt:formatDate value="${now}" dateStyle="full" /></p> <%-- Custom date pattern --%> <p>Custom pattern: <fmt:formatDate value="${now}" pattern="yyyy-MM-dd HH:mm:ss" /></p> <%-- Format numbers --%> <h2>Number Formatting:</h2> <c:set var="price" value="1234.567" /> <p>Default: <fmt:formatNumber value="${price}" /></p> <p>Currency: <fmt:formatNumber value="${price}" type="currency" /></p> <p>Percent: <fmt:formatNumber value="0.85" type="percent" /></p> <p>Pattern: <fmt:formatNumber value="${price}" pattern="#,##0.00" /></p> <%-- Set locale --%> <fmt:setLocale value="fr_FR" /> <p>French currency: <fmt:formatNumber value="${price}" type="currency" /></p> <p>French date: <fmt:formatDate value="${now}" dateStyle="full" /></p> <%-- Reset locale --%> <fmt:setLocale value="en_US" /> <p>US currency: <fmt:formatNumber value="${price}" type="currency" /></p> <p>US date: <fmt:formatDate value="${now}" dateStyle="full" /></p> <%-- Time zone --%> <fmt:setTimeZone value="GMT" /> <p>GMT time: <fmt:formatDate value="${now}" pattern="HH:mm:ss z" /></p> <fmt:setTimeZone value="America/New_York" /> <p>New York time: <fmt:formatDate value="${now}" pattern="HH:mm:ss z" /></p> </body> </html> 

在这个示例中,我们使用了JSTL Format标签库的多个标签:

  • 使用<fmt:formatDate>以不同样式格式化日期
  • 使用<fmt:formatNumber>格式化数字、货币和百分比
  • 使用<fmt:setLocale>设置不同的区域,实现国际化
  • 使用<fmt:setTimeZone>设置不同的时区

这些标签使得国际化和格式化操作变得非常简单,无需编写复杂的Java代码。

3.4.3 使用JSTL SQL标签库示例

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> <!DOCTYPE html> <html> <head> <title>JSTL SQL Example</title> </head> <body> <h1>JSTL SQL Tags Example</h1> <%-- Set data source --%> <sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydatabase" user="admin" password="password123" var="dataSource" /> <%-- Create table if not exists --%> <sql:update dataSource="${dataSource}"> CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, age INT, registration_date DATE ) </sql:update> <%-- Insert sample data --%> <sql:update dataSource="${dataSource}"> INSERT INTO users (name, email, age, registration_date) VALUES ('John Doe', 'john@example.com', 30, '2023-01-15') </sql:update> <sql:update dataSource="${dataSource}"> INSERT INTO users (name, email, age, registration_date) VALUES ('Jane Smith', 'jane@example.com', 25, '2023-02-20') </sql:update> <sql:update dataSource="${dataSource}"> INSERT INTO users (name, email, age, registration_date) VALUES ('Bob Johnson', 'bob@example.com', 35, '2023-03-10') </sql:update> <%-- Query data --%> <sql:query dataSource="${dataSource}" var="users"> SELECT * FROM users </sql:query> <%-- Display results --%> <h2>Users List:</h2> <table border="1"> <tr> <th>ID</th> <th>Name</th> <th>Email</th> <th>Age</th> <th>Registration Date</th> </tr> <c:forEach var="user" items="${users.rows}"> <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.email}</td> <td>${user.age}</td> <td>${user.registration_date}</td> </tr> </c:forEach> </table> <%-- Parameterized query --%> <sql:query dataSource="${dataSource}" var="userById"> SELECT * FROM users WHERE id = ? <sql:param value="1" /> </sql:query> <h2>User with ID 1:</h2> <c:forEach var="user" items="${userById.rows}"> <p>Name: ${user.name}</p> <p>Email: ${user.email}</p> <p>Age: ${user.age}</p> </c:forEach> <%-- Update data --%> <sql:update dataSource="${dataSource}"> UPDATE users SET age = ? WHERE name = ? <sql:param value="31" /> <sql:param value="John Doe" /> </sql:update> <%-- Delete data --%> <sql:update dataSource="${dataSource}"> DELETE FROM users WHERE name = ? <sql:param value="Bob Johnson" /> </sql:update> </body> </html> 

在这个示例中,我们使用了JSTL SQL标签库的多个标签:

  • 使用<sql:setDataSource>设置数据源
  • 使用<sql:update>创建表、插入数据、更新数据和删除数据
  • 使用<sql:query>执行查询
  • 使用<sql:param>设置参数,防止SQL注入

这些标签使得数据库操作变得非常简单,无需编写JDBC代码。但需要注意的是,在实际项目中,通常不建议在JSP页面中直接进行数据库操作,而应该将数据访问逻辑放在Servlet或DAO层。

3.4.4 使用自定义标签库示例

假设我们有一个自定义标签库,用于显示用户信息。

UserTag.java (标签处理器):

package com.example.tags; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; import java.io.IOException; public class UserTag extends SimpleTagSupport { private String name; private String email; private String role; public void setName(String name) { this.name = name; } public void setEmail(String email) { this.email = email; } public void setRole(String role) { this.role = role; } @Override public void doTag() throws JspException, IOException { getJspContext().getOut().println("<div class='user-card'>"); getJspContext().getOut().println("<h3>" + name + "</h3>"); getJspContext().getOut().println("<p>Email: " + email + "</p>"); getJspContext().getOut().println("<p>Role: " + role + "</p>"); getJspContext().getOut().println("</div>"); } } 

mytags.tld (标签库描述符文件):

<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>mytags</short-name> <uri>http://www.example.com/tags/mytags</uri> <tag> <name>user</name> <tag-class>com.example.tags.UserTag</tag-class> <body-content>empty</body-content> <attribute> <name>name</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>email</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>role</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib> 

userDisplay.jsp (使用自定义标签的JSP页面):

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://www.example.com/tags/mytags" prefix="my" %> <!DOCTYPE html> <html> <head> <title>Custom Tag Example</title> <style> .user-card { border: 1px solid #ccc; border-radius: 5px; padding: 15px; margin: 10px; width: 300px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .user-card h3 { margin-top: 0; color: #333; } </style> </head> <body> <h1>User Information</h1> <%-- Using custom tag --%> <my:user name="John Doe" email="john@example.com" role="Administrator" /> <my:user name="Jane Smith" email="jane@example.com" role="User" /> <my:user name="Bob Johnson" email="bob@example.com" role="Moderator" /> </body> </html> 

在这个示例中,我们创建了一个自定义标签库,包含一个user标签,用于显示用户信息。UserTag.java是标签处理器,负责生成HTML输出。mytags.tld是标签库描述符文件,定义了标签的属性和行为。userDisplay.jsp使用taglib指令引入自定义标签库,并使用user标签显示用户信息。

自定义标签可以将复杂的Java代码封装起来,提供简洁的接口,使JSP页面更加清晰易读。

4. 三种指令的比较与选择

4.1 功能比较

特性page指令include指令taglib指令
主要功能设置页面级别的属性包含其他文件的内容引入标签库
作用范围整个JSP页面被包含的位置整个JSP页面
处理时机JSP转换阶段JSP转换阶段JSP转换阶段
语法<%@ page attribute="value" %><%@ include file="relativeURL" %><%@ taglib uri="tagLibraryURI" prefix="tagPrefix" %>
可出现次数多次,但部分属性只能设置一次多次多次,每个标签库一次
典型应用场景设置内容类型、导入类、配置错误页面等包含公共头部、底部、导航栏等引入JSTL或自定义标签库

4.2 选择指南

4.2.1 使用page指令的场景

  • 需要设置页面的内容类型和字符编码
  • 需要导入Java类
  • 需要配置会话管理
  • 需要设置错误页面
  • 需要控制缓冲区
  • 需要设置页面是否为线程安全

4.2.2 使用include指令的场景

  • 多个页面需要共享相同的头部或底部
  • 需要在多个页面中使用相同的函数或变量
  • 需要包含配置信息
  • 需要模块化页面内容,提高代码重用性

4.2.3 使用taglib指令的场景

  • 需要使用JSTL标签库简化页面开发
  • 需要使用自定义标签封装复杂功能
  • 需要减少页面中的Java代码,提高可读性
  • 需要使用国际化、格式化等功能

5. 最佳实践和注意事项

5.1 page指令的最佳实践

  1. 始终设置内容类型和字符编码

    <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 
  2. 合理组织import语句

    <%@ page import="java.util.List, java.util.ArrayList, java.util.Map" %> 
  3. 谨慎使用isThreadSafe属性

    • 默认情况下,JSP页面是线程安全的(isThreadSafe=“true”)
    • 如果页面使用了实例变量,需要确保这些变量的访问是同步的
    • 如果无法确保线程安全,可以设置isThreadSafe=“false”,但这会影响性能
  4. 合理配置错误页面

    <%@ page errorPage="error.jsp" %> 
  5. 根据需要设置缓冲区大小

    <%@ page buffer="16kb" autoFlush="true" %> 

5.2 include指令的最佳实践

  1. 使用相对路径

    <%@ include file="header.jsp" %> <%@ include file="/includes/footer.jsp" %> 
  2. 注意文件扩展名

    • 被包含的文件可以是任何类型,但通常使用.jspf(JSP Fragment)作为片段文件的扩展名
    • 例如:<%@ include file="header.jspf" %>
  3. 避免循环包含

    • 确保文件A包含文件B,文件B不包含文件A,否则会导致无限循环
  4. 注意变量作用域

    • 被包含的文件可以访问主页面的所有变量
    • 主页面也可以访问被包含文件中定义的变量
  5. 考虑使用JSTL的c:import替代

    • 如果需要在运行时动态包含文件,可以使用JSTL的c:import标签
    • 例如:<c:import url="header.jsp" />

5.3 taglib指令的最佳实践

  1. 使用标准前缀

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %> 
  2. 在页面顶部声明所有需要的标签库

    <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> 
  3. 优先使用JSTL标签而非Java代码

    • 使用<c:out>而不是<%= %>
    • 使用<c:if>而不是<% if {} %>
    • 使用<c:forEach>而不是<% for {} %>
  4. 合理使用EL表达式

    <c:if test="${not empty user}"> <p>Welcome, ${user.name}!</p> </c:if> 
  5. 自定义标签的设计原则

    • 保持简单,每个标签只做一件事
    • 提供清晰的属性和文档
    • 遵循命名约定,避免与标准标签冲突

5.4 通用注意事项

  1. 指令位置

    • 通常将指令放在JSP页面的顶部,在任何模板文本之前
    • 这样可以使页面更易于阅读和维护
  2. 大小写敏感

    • 指令名称和属性名称是大小写敏感的
    • 例如,<%@ page %>是正确的,而<%@ Page %>是错误的
  3. 引号使用

    • 属性值必须用单引号或双引号括起来
    • 如果属性值本身包含引号,可以使用另一种引号
    • 例如:<%@ page info="This is a 'sample' JSP page" %>
  4. 避免过度使用

    • 不要在一个页面中使用过多的指令
    • 只使用必要的指令,保持页面的简洁性
  5. 考虑性能影响

    • include指令在转换阶段处理,对性能影响较小
    • 过多的page指令可能会增加JSP页面的编译时间
    • taglib指令引入的标签库可能会增加页面的处理时间

6. 总结

JSP指令是JSP技术中的重要组成部分,它们为JSP页面提供了配置和扩展能力。通过合理使用page、include和taglib这三大指令,可以开发出功能强大、结构清晰、易于维护的JSP应用程序。

page指令主要用于设置页面级别的属性,如内容类型、字符编码、会话管理等。它是JSP页面中最常用的指令之一,几乎每个JSP页面都会使用到。

include指令允许在JSP页面中包含其他文件的内容,有助于代码重用和模块化开发。通过使用include指令,可以将公共的头部、底部、导航栏等内容抽取到单独的文件中,然后在多个页面中共享。

taglib指令用于引入标签库,包括JSTL标准标签库和自定义标签库。通过使用标签库,可以减少JSP页面中的Java代码,提高页面的可读性和可维护性。

在实际开发中,应根据具体需求选择合适的指令,并遵循最佳实践,以确保JSP应用程序的质量和性能。通过合理组合使用这三大指令,可以充分发挥JSP技术的优势,开发出高效、可维护的Web应用程序。