⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:Java Web
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. 简单易学:JSP技术基于HTML,开发人员可以使用标准的HTML标记语言进行开发,同时可以嵌入Java代码来实现动态内容生成。
2. 可重用性:通过使用JSP标签库(Tag Library),可以将代码块打包成可重用的组件,这些组件可以在不同的JSP页面中共享,提高了代码的可重用性。
3. 高性能:JSP页面在首次访问时需要编译成Java Servlet,之后每次访问时就不需要再次编译,因此可以提高Web应用程序的性能。
4. 容易维护:JSP页面的代码可以分离出来,从而实现模块化和易于维护。
JSP的使用,相比于单纯使用Servlet开发,效率和开发质量大大提升。
在之前直接使用Servlet对浏览器写前端网页的内容,如果内容较多的情况下,会显得非常繁琐,并且代码是写死在程序里面的,不好维护,可读性也较差。因为之前是需要通过Servlet的response对象来向浏览器响应数据的,因此调用的是response.getWriter().write()方法来写,写出来的效果就像这样:
可以看到一个非常简单的前端表单页面,直接拿Servlet响应请求给浏览器显得多么复杂。JSP在当时,就很好的解决了这个问题。
缺点:
在现代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应用。
学习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开发的演变过程。
①创建一个Maven的Web项目,编写xml文件(重点是导入jsp的依赖)
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>JSP_demoartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
plugin>
plugins>
build>
project>
JSP是不能像之前的前端三剑客那样,写出来的代码文件可以直接在浏览器运行的,如果直接选择浏览器运行则会报错。
报错信息写的是:没有找到已配置/正在运行的web服务器。
这说明什么问题?说明这个JSP可能本质上就是个Servlet服务端。事实上真是如此,下面会详细讲述。
在访问JSP的时候出现了如下错误:
显示的是无法编译JSP。
出现这个原因是因为使用了低版本的tomcat和高版本的JDK导致的, 解析出错,无法识别System类。可以尝试更换版本解决。
高版本tomcat配高版本JDK,低版本tomcat配低版本JDK。
这里我使用的是JDK11+tomcat7/tomcat8。两个tomcat版本都是可以适用的。
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("\r\n");
out.write("\r\n");
out.write(" Title \r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" hello jsp
\r\n");
out.write("\r\n");
out.write(" ");
System.out.println("hello jsp->java code");
out.write("\r\n");
out.write("\r\n");
out.write("\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来帮助完成。
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()的参数。
在第二部分简单样例的基础上,编写本次的实践样例。
这次的实践样例中,采用了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>
在写java代码的时候,如果想在其中穿插执行前端代码,可以使用<% %>进行截断。