[Java Web]JSP基础部分 | JSP:一项较老但仍具学习意义的技术

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:Java Web

1、JSP概述

1.1、简单介绍

  JSP(JavaServer Pages)是Java Web技术体系中的一种动态网页开发技术,它允许开发人员在HTML网页中嵌入Java代码以及Java EE标签库,从而实现动态生成网页内容的功能。
  与静态网页不同,JSP页面可以包含Java代码片段、Java EE标签库以及HTML标签。在客户端请求访问该JSP页面时,Java代码片段将会被服务器端解释执行并生成动态内容,然后将生成的结果作为HTML网页返回给客户端。
  在JSP中,可以使用Java语言的所有功能和API。它还提供了许多内置的对象,例如request、response、session、application等,这些对象可以用来处理用户请求和响应的数据。
  除了使用Java代码,JSP页面还可以使用标签库(Tag Library)来简化开发。标签库是一组可重用的代码块,类似于HTML的标签,它们可以在JSP页面中使用,以简化常见的Web开发任务。
  总之,JSP是Java Web技术体系中重要的组成部分之一,它使得开发人员可以使用Java语言和API来开发动态Web页面,同时也提供了许多内置的对象和标签库,以简化开发过程。

1.2、优点

  1. 简单易学:JSP技术基于HTML,开发人员可以使用标准的HTML标记语言进行开发,同时可以嵌入Java代码来实现动态内容生成。
  2. 可重用性:通过使用JSP标签库(Tag Library),可以将代码块打包成可重用的组件,这些组件可以在不同的JSP页面中共享,提高了代码的可重用性。
  3. 高性能:JSP页面在首次访问时需要编译成Java Servlet,之后每次访问时就不需要再次编译,因此可以提高Web应用程序的性能。
  4. 容易维护:JSP页面的代码可以分离出来,从而实现模块化和易于维护。

  JSP的使用,相比于单纯使用Servlet开发,效率和开发质量大大提升。
  在之前直接使用Servlet对浏览器写前端网页的内容,如果内容较多的情况下,会显得非常繁琐,并且代码是写死在程序里面的,不好维护,可读性也较差。因为之前是需要通过Servlet的response对象来向浏览器响应数据的,因此调用的是response.getWriter().write()方法来写,写出来的效果就像这样:
JSP&Servlet

  可以看到一个非常简单的前端表单页面,直接拿Servlet响应请求给浏览器显得多么复杂。JSP在当时,就很好的解决了这个问题。

1.3、缺点

缺点:

  1. 容易混淆:JSP页面中包含了Java代码和HTML标记,代码量较大时容易混淆。
  2. 容易滥用:JSP页面可以包含大量的Java代码,导致页面变得臃肿,降低可读性和可维护性。
  3. 不够灵活:JSP技术可能会受到容器限制,例如无法使用自定义的Java类加载器。
  4. 不够安全:由于JSP页面允许包含Java代码,如果编写不当,可能会导致安全漏洞。因此,需要使用安全的编程实践来确保应用程序的安全性。

1.4、逐渐淘汰

在现代Web开发中,越来越多的开发人员转向了使用前端框架来构建动态Web应用,如React、Vue.js等。
同时,Java EE平台在2019年被Oracle放弃维护,取而代之的是Jakarta EE平台,也就是Eclipse基金会维护的Java EE的开源版本。

JSP为什么会逐渐被淘汰?主要有以下原因:
  1. 前后端分离:随着前端框架的发展和普及,越来越多的开发人员开始采用前后端分离的开发模式。在这种模式下,后端提供API接口,前端通过AJAX等方式获取数据并动态渲染页面,这种方式更加灵活,更容易实现复杂的Web应用。
  2. 低效的开发:JSP中的Java代码片段可能会导致页面变得混乱,降低了可读性和可维护性。同时,使用JSP时,开发人员需要手动处理各种请求和响应,这可能会导致开发效率低下。
  3. 安全问题:由于JSP允许在页面中嵌入Java代码,如果编写不当,可能会导致安全漏洞,如SQL注入、XSS等。
  4. Java EE平台的变化:Java EE平台在2019年被Oracle放弃维护,取而代之的是Jakarta EE平台,这也导致开发人员不得不转向使用新的技术栈来开发Web应用。
  因此,尽管JSP作为一种经典的Web开发技术在一定程度上仍然被广泛应用,但随着前端技术和后端技术的发展,越来越多的开发人员转向使用前后端分离的方式来构建动态Web应用。

1.5、学习的意义

学习JavaWeb的过程,就像在学习Web开发领域的发展史,以史为鉴,从历史中总结学习经验,取其精华去其糟粕。因此,时至今日学习JSP仍然具有一定的意义,尤其对于初学者和想要了解Web开发历史和演变过程的人来说。
以下列举几个学习JSP技术的意义所在:
  1. 掌握Java Web开发的基本原理:JSP是Java Web开发的重要组成部分,学习JSP可以帮助初学者掌握Java Web开发的基本原理,包括Servlet、HTTP协议、JSP的工作原理等。
  2. 理解现代Web开发的演变过程:JSP曾经是Java Web开发的主流技术之一,但现在已经被前后端分离、模板引擎等技术所替代。学习JSP可以帮助人们了解现代Web开发的演变过程,以及现在的Web开发趋势。
  3. 学习JSP的一些基本概念对于维护和升级已有的JSP应用程序仍然有用。在很多公司中,可能还有一些老的JSP应用程序需要维护和升级,因此对于熟悉JSP的开发人员来说,仍然具有一定的价值。
  总的来说,虽然JSP已经不再是主流的Web开发技术,但学习JSP仍然有其独特的价值,能够帮助开发人员理解Java Web开发的基本原理和现代Web开发的演变过程。

2、JSP的简单样例

①创建一个Maven的Web项目,编写xml文件(重点是导入jsp的依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>JSP_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
        </plugins>
    </build>
</project>

②创建JSP文件,这里创建的是helloJSP.jsp。

③访问JSP

2.1、注意事项

  JSP是不能像之前的前端三剑客那样,写出来的代码文件可以直接在浏览器运行的,如果直接选择浏览器运行则会报错。
  
  报错信息写的是:没有找到已配置/正在运行的web服务器。
  这说明什么问题?说明这个JSP可能本质上就是个Servlet服务端。事实上真是如此,下面会详细讲述。

3、一个常见bug

在访问JSP的时候出现了如下错误:
  
  显示的是无法编译JSP。
  出现这个原因是因为使用了低版本的tomcat和高版本的JDK导致的, 解析出错,无法识别System类。可以尝试更换版本解决。
高版本tomcat配高版本JDK,低版本tomcat配低版本JDK。
  这里我使用的是JDK11+tomcat7/tomcat8。两个tomcat版本都是可以适用的。

4、JSP原理

JSP本质上就是一个Servlet。
浏览器访问JSP的执行流程如下:

  1. 浏览器第一次访问 helloJSP.jsp 页面
  2. tomcat 会将 helloJSP.jsp 转换为名为 helloJSP_jsp.java 的一个 Servlet
  3. tomcat 再将转换的 servlet 编译成字节码文件 hello_jsp.class
  4. tomcat 会执行该字节码文件,向外提供服务
  为了验证上面的执行流程,可以到磁盘中进行查看。进入该项目的target\tomcat\work\Tomcat\localhost\jsp_demo\org\apache\jsp 目录,而这个目录下就能看到转换后的 servlet。我自己目录如下:
  
  
下面贴上一份编译后的helloJSP_jsp.class文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.jsp;

import java.io.IOException;
import java.util.Map;
import javax.el.ExpressionFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import org.apache.jasper.runtime.HttpJspBase;
import org.apache.jasper.runtime.InstanceManagerFactory;
import org.apache.jasper.runtime.JspSourceDependent;
import org.apache.tomcat.InstanceManager;

public final class helloJSP_jsp extends HttpJspBase implements JspSourceDependent {
    private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
    private static Map<String, Long> _jspx_dependants;
    private ExpressionFactory _el_expressionfactory;
    private InstanceManager _jsp_instancemanager;

    public helloJSP_jsp() {
    }

    public Map<String, Long> getDependants() {
        return _jspx_dependants;
    }

    public void _jspInit() {
        this._el_expressionfactory = _jspxFactory.getJspApplicationContext(this.getServletConfig().getServletContext()).getExpressionFactory();
        this._jsp_instancemanager = InstanceManagerFactory.getInstanceManager(this.getServletConfig());
    }

    public void _jspDestroy() {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        JspWriter out = null;
        JspWriter _jspx_out = null;
        PageContext _jspx_page_context = null;

        try {
            response.setContentType("text/html;charset=UTF-8");
            PageContext pageContext = _jspxFactory.getPageContext(this, request, response, (String)null, true, 8192, true);
            _jspx_page_context = pageContext;
            pageContext.getServletContext();
            pageContext.getServletConfig();
            pageContext.getSession();
            out = pageContext.getOut();
            out.write("\r\n");
            out.write("\r\n");
            out.write("<html>\r\n");
            out.write("<head>\r\n");
            out.write("    <title>Title</title>\r\n");
            out.write("</head>\r\n");
            out.write("<body>\r\n");
            out.write("    <h1>hello jsp</h1>\r\n");
            out.write("\r\n");
            out.write("    ");
            System.out.println("hello jsp->java code");
            out.write("\r\n");
            out.write("</body>\r\n");
            out.write("</html>\r\n");
        } catch (Throwable var13) {
            if (!(var13 instanceof SkipPageException)) {
                out = (JspWriter)_jspx_out;
                if (_jspx_out != null && ((JspWriter)_jspx_out).getBufferSize() != 0) {
                    try {
                        out.clearBuffer();
                    } catch (IOException var12) {
                    }
                }

                if (_jspx_page_context == null) {
                    throw new ServletException(var13);
                }

                _jspx_page_context.handlePageException(var13);
            }
        } finally {
            _jspxFactory.releasePageContext(_jspx_page_context);
        }

    }
}

可以发现编译后的class文件中,继承了HttpJspBase,查看HttpJspBase源码:
  

可以看到HttpJspBase是继承了HttpServlet,所以helloJSP.jsp间接继承了HttpServlet,什么JSP本质上就是Servlet。
HttpJspBase源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jasper.runtime;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.HttpJspPage;

import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;

/**
 * This is the super class of all JSP-generated servlets.
 *
 * @author Anil K. Vijendran
 */
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {

    private static final long serialVersionUID = 1L;

    protected HttpJspBase() {
    }

    @Override
    public final void init(ServletConfig config)
        throws ServletException
    {
        super.init(config);
        jspInit();
        _jspInit();
    }

    @Override
    public String getServletInfo() {
        return Localizer.getMessage("jsp.engine.info", Constants.SPEC_VERSION);
    }

    @Override
    public final void destroy() {
        jspDestroy();
        _jspDestroy();
    }

    /**
     * Entry point into service.
     */
    @Override
    public final void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        _jspService(request, response);
    }

    @Override
    public void jspInit() {
    }

    public void _jspInit() {
    }

    @Override
    public void jspDestroy() {
    }

    protected void _jspDestroy() {
    }

    @Override
    public abstract void _jspService(HttpServletRequest request,
                                     HttpServletResponse response)
        throws ServletException, IOException;
}

_jspService方法每次访问的时候都会自动执行,和Servlet中的service方法类似。在_jspService方法中则存放着往浏览器写标签的代码。所以实际上就是以前这部分代码是自己写,现在由tomcat来帮助完成。

5、JSP脚本

JSP脚本是一种在JSP页面中嵌入Java代码的方式,可以在JSP页面中执行Java代码,从而动态生成HTML页面。JSP脚本可以出现在JSP页面的任何位置,包括JSP页面的文本、HTML标记和XML标记之间。JSP脚本可以使用以下语法:
  1. <% … %>:内容会直接放到_jspService()方法之中。这是最常用的JSP脚本标记,可以用来嵌入任意的Java代码,包括声明变量、定义方法、调用Java API等。
  2. <%= … %>:内容会放到out.print()中,作为out.print()的参数。这个标记可以用来在HTML页面中输出Java表达式的值,类似于Java中的System.out.println()语句。例如:<%= name %>可以输出变量name的值。
  3. <%-- --%>:这个标记可以用来注释JSP页面中的代码。
  4. <%! … %>:内容会放到_jspService()方法之外,被类直接包含。这个标记用来定义全局变量和方法,这些变量和方法可以在整个JSP页面中使用。
JSP脚本的执行顺序是从上到下,从左到右,与在Java中的执行顺序相同。

下面依次对这几个脚本进行测试:
  

①<% … %>:上面提到,该脚本会把内容直接放到_jspService()方法之中。
  

②<%= …%>:内容会放到_jspService()方法中out.print()中,作为out.print()的参数。
  

③<%! …%>:内容会直接放在类中。

6、实践样例

  在第二部分简单样例的基础上,编写本次的实践样例。
  这次的实践样例中,采用了JDBC操作数据库的方式,获取到数据库中的数据,在前端页面上进行展示。(JDBC的相关代码直接在JSP中完成)
  准备工作:打开本地的MySQL服务;在pom.xml中配置MySQL驱动依赖

下面对JSP的编写进行解析:
  ①HTML部分:
    写好表头标签:
     

    编写表格内容的标签:
     
  HTML的这部分中,由于后面是要写Java代码来获取数据填充在HTML页面的表格中,所以这里的表格内容要动态获取,不能写死。
  ②java代码:
    编写jdbc实现代码的步骤:注册驱动、获取连接、定义SQL语句、获取执行SQL对象、执行SQL、处理返回的结果、释放资源。

    动态获取数据:
    

    下面是JSP的完整代码:

<%--
  Created by IntelliJ IDEA.
  User: 逐梦苍穹
  Date: 2023/3/19
  Time: 21:59
  To change this template use File | Settings | File Templates.
--%>
<%@ page import="java.sql.*" %>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1" cellspacing="0" width="800">
    <tr>
        <th>序号</th>
        <th>名称</th>
        <th>介绍</th>
        <th>商品数量</th>
        <th>状态</th>
    </tr>

    <%
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL JDBC驱动程序
            Class.forName("com.mysql.jdbc.Driver");
            // 建立与数据库的连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jsp", "root", "你的密码");
            // 创建一个 Statement 对象
            stmt = conn.createStatement();
            // 执行查询语句
            rs = stmt.executeQuery("SELECT * FROM jspDemo");
            // 处理查询结果
            while (rs.next()) {
    %>

    <tr align="center">
        <td><%=rs.getString("id")%></td>
        <td><%=rs.getString("name")%></td>
        <td><%=rs.getString("information")%></td>
        <td><%=rs.getString("number")%></td>
        <td><%=(rs.getString("status")).equals("1") ? "启用":"禁用"%></td>
        <td><a href="#">修改</a> <a href="#">删除</a></td>
    </tr>

    <%
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        } finally {
            // 关闭数据库连接
            try {
                if (rs != null) rs.close();
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    %>

</table>

</body>
</html>

6.1、注意事项

在写java代码的时候,如果想在其中穿插执行前端代码,可以使用<% %>进行截断。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦苍穹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值