💭〰〰〰前言〰〰〰💭
JSP 作为已经被逐渐放弃的技术,深究它并没有太大意义,不过它涉及到的 Tomcat、Servlet、Session、Cookie、JDBC 等知识点依然是学习当下主流框架的基础,因此花点时间来了解一下它还是有必要的。加上现在很多高校的计算机课也还在教 JSP 这门技术,这篇文章也非常适合期末复习突击的小伙伴。
(本文涵盖了本科阶段学习 JSP 时的绝大部分知识点,阅读完需要 30 分钟左右)
💭〰〰〰〰〰〰〰〰💭
Java web是指用java技术来解决web互联网领域的技术栈。一个web项目包含了web客户端与web服务器端两个部分(即我们常说的前端、后端),web客户端的技术主要有HTML,CSS,JavaScript等等,web服务端的技术主要有Servlet,JSP,以及Spring框架等很多技术,其中后端的一些技术就是我们所说的Java web。
Internet上供外界访问的Web资源分为:静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变。动态web资源:指web页面中供人们浏览的数据是由程序产生的,不同时间点访问web页面看到的内容各不相同。静态web资源开发技术:HTML、CSS。动态web资源开发技术:JavaScript、JSP/Servlet、ASP、PHP等。在Java中,动态web资源开发技术统称为Java web。(来源——百度百科)
客户端和Web服务器(比如我们常说的Tomcat)通过HTTP协议进行通信。Web服务器也叫HTTP服务器或Web容器。HTTP协议采用请求/响应模式。即客户端发起HTTP请求,web服务器接收并解析处理HTTP请求,然后将HTTP响应发送给客户端。
上图中的Web服务器是一个软件程序,比如Tomcat服务器,本质上是一个软件,而单说服务器的话才指的是硬件设备,一个主机。Web服务器用来管理web应用,当客户端发出了HTTP请求,web服务器接收到请求以后,会调用对应的web应用程序进行处理,然后web服务器将处理的结果再通过HTTP协议返回给客户端。
Tomcat:
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML页面的访问请求。(来源——百度百科)
安装Tomcat:
在官网下载压缩文件。
解压缩,会得到如下的文件夹。
bin:存放各个平台下启动和停止Tomcat服务的脚本。
conf:存放Tomcat的配置文件。
lib:存放Tomcat服务器所需要的jar包。
webapps:存放允许客户端访问的资源(java程序)。
在Java Web工程中配置Tomcat:
以IDEA为例,选择java enterprise,在Project template中选择Web application。
在Application server中选择new --> tomcat server。
在弹出的页面选择刚刚解压出来的Tomcat文件夹,这样就将Tomcat服务器配置到了IDEA的JavaWeb工程中。
Servlet:
Servlet是什么?
Servlet是Java Web开发的基础,与平台无关的服务器组件,运行在Servlet容器(Web服务器,Tomcat),负责与客户端和服务端进行通信。
Servlet能做什么?
怎样使用Servlet?
Servlet是一组接口,我们去实现Servlet接口,让他能接受客户端的请求以及做出响应。但是浏览器不能直接访问Servlet,需要通过映射的方式间接访问Servlet,也就是说我们要给Servlet配置一个地址,通过这个地址再来访问Servlet。映射的配置方式有两种:1.通过注解,2.修改.xml配置文件。
import javax.servlet.*;
import java.io.IOException;
@WebServlet("/test-servlet")
public class TestServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
上面代码中的@WebServlet("/test-servlet")
就是通过注解的方式给Servlet配置了一个地址,通过修改.xml文件也可以做到同样的效果,比如下面这样:
<servlet>
<servlet-name>testservlet-name>
<servlet-class>com.example.TestServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>testservlet-name>
<servlet-pattern>/test-servletservlet-pattern>
servlet-mapping>
当前Servlet的类名字是TestServlet,放在了com.example包下,映射地址是test-servlet,那么通过浏览器访问test-servlet就可以映射到TestServlet。
Servlet的生命周期:
Servlet创建以后给了我们六个方法让我们来实现,实际上真正有用的方法一共有三个,即init()
,service()
,destory()
,他们是Servlet的生命周期方法。
init()
方法只调用一次,初始化对象。service()
调用n次,每访问一次Servlet都会调用service()
方法,用来处理业务逻辑。destory()
方法只调用一次,用来销毁对象。HttpServlet:
在实际的开发中我们并不需要自己实现Servlet中的方法,只需要继承HttpServlet
,可以简单理解为它已经帮我们实现好了Servlet接口,使用的时候只需要重写其中的doGet()
,与doPost()
方法就可以了。
Servlet的层次结构:
Servlet --> GenericServlet --> HttpServlet
更为准确的说法是:GenericServlet
实现了Servlet接口,它屏蔽了不常用的方法,子类只需要重写service()
方法;HttpServlet
又继承了GenericServlet
,里面含有doGet()
,与doPost()
两个方法,它根据请求类型进行分发处理,浏览器发来的get请求交给doGet()处理,post交给doPost()
处理。
HttpServlet示例:
首先在HTML页面定义一个form
表单。
action
指定了此表单要请求test-servlet
处理,method
指定了此表单以post
请求的方式发送给Servlet。当用户点击”登录“按钮以后,浏览器会将表单通过HTTP协议发送给Tomcat,Tomcat会调用test-servlet
所映射的HTTPServlet
类,并根据表单中method
属性确定由doGet()
还是doPost()
方法来处理这个表单。
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/test-servelt")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Ta点了登录");
}
}
用户每访问一次Servlet,都会调用其中的doPost()
方法,在控制台上输出:”Ta点了登录“。
JSP:
JSP全称为Java Server Pages,指的就是java服务器端页面。我们可以理解为它就是一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码。浏览器是不能直接处理.jsp文件的,当我们访问.jsp文件的时候,Tomcat会先把它转换成Servlet,再将要展示的页面返回给浏览器。
MVC:
MVC指的是应用设计框架,用于应用的分层开发。MVC全称是Model-View-Controller(模型,视图,控制器),它将我们的代码分为了三个层次,实现了代码的分离。最典型的MVC就是jsp+servlet+javabean模式,jsp只用来展示数据,servlet用来接收并处理用户的请求,javabean作为模型,用来封装数据业务。
首先给出一个实现用户注册功能的Servlet示例:
package com.example.demoboard;
import com.example.demoboard.JavaBean.UserBean;
import com.example.demoboard.useBean.UserDB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "registerServlet", value = "/register-servlet")
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
System.out.println("ta点了注册!");
String username = request.getParameter("username");
String password = request.getParameter("password_1");
UserBean user = new UserBean(username, password);
UserDB.insert(user);
}
}
UserDB.insert(user);
这一段是关于数据库的内容,暂且不管。
其中doGet()
,doPost()
是继承了HttpServlet以后需要我们自己实现的方法,request,response是常用的两个对象,除此之外HttpServlet自带的一些方法可以通过this.func()
的方式调用。具体内容下面会逐一给出。
How to create and map a servlet?
浏览器是不能直接访问Servlet的,需要通过映射的方式间接访问Servlet。同时一个Web项目通常会有多个Servlet供Tomcat调用,那么如何创建Servlet的对应关系呢?我们需要先给Servlet配置一个地址,通过这个地址再来访问Servlet。映射的配置方式有两种:1.注解,2.修改web.xml。
通过注解(map a servlet with an annotation)
package com.example.webstudy;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
private String message;
public void init() {
message = "Hello World!";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("");
out.println(""
+ message + "");
out.println("");
}
public void destroy() {
}
}
这是IDEA在创建Web项目时为我们准备的例子,其中@WebServlet(name = "helloServlet", value = "/hello-servlet")
即为配置Servlet映射的注解,Servlet的名字为helloServlet
,与它对应的地址是/hello-servlet
。
*tips:*注解也可以写成@webServlet("/hello-servlet")
跟上面是一样的效果。
修改web.xml文件(map a servlet with the web.xml file)
<servlet>
<servlet-name>helloServletservlet-name>
<servlet-class>com.example.webstudyservlet-class>
servlet>
<servlet-mapping>
<servlet-name>helloServletservlet-name>
<servlet-pattern>/hello-servletservlet-pattern>
servlet-mapping>
通过修改xml文件的方式需要指定Servlet所在的包名(package),
,
中填写要给Servlet映射的地址。
How to request a servlet?
给Servlet配置了地址以后,我们有两种方式请求Servlet:
HttpServlet
在接收到客户端发来的请求以后,会根据请求的方式将其转交给对应的方法来处理。GET请求交给doGet()
方法,POST请求交给doPost()
方法。
How to use the HTTP GET method?
可以简单理解为在客户端通过url
请求Servlet,并传递参数,这种统称为GET方式。
比如我可以直接在浏览器中输入url
:http://localhost:8080/webstudy_war_exploded/hello-servlet?user=admin
,可以直接访问到/hello-servlet
所对应的Servlet,同时将?
后面的内容以键值对(key-value)的形式传给Servlet中的doGet()
方法。
如何在url
后面添加一些参数?
How to append parameters to a URL?
一个参数:/hello-servlet?name=admin
多个参数:/hello-servlet?name=admin&password=123456
如何以GET方式请求Servlet并传递参数?
How to append parameters to a GET request?
在浏览器地址栏中输入URL
Enter the URL into the browser’s address bar
http://localhost:8080/webstudy_war_exploded/hello-servlet?user=admin&password=123456
HTML中的表单,将method属性设置为get
Code a form that uses the GET method
<from action="hello-servlet" method="get">
<input type="text" name="user" value="admin">
<input type="password" name="password">
from>
在超链接中
Code an anchor tag
<a href="hello-servlet?user=admin&password=123456">goto helloServleta>
How to use the HTTP POST method?
POST请求可以在Form表单中定义,与GET方式的区别是GET请求会把传递的参数展示在浏览器的地址栏中,而POST不会,相比之下POST方式更加安全。
HTML中的表单,将method属性设置为post
A Form tag that uses the POST method
<from action="hello-servlet" method="post">
<input type="text" name="user" value="admin">
<input type="password" name="password">
from>
使用get method的情况 | 使用post method的情况 |
---|---|
HTML表单的内容比较少 | 发送更大的数据(>4KB) |
做数据查询时 | 做数据的增加,修改,删除时 |
获取资源,读取数据时 | 要求的安全性表较高时 |
Skills for working with servlets
在实际的Web开发中我们不需要自己实现Servlet,而是继承HttpServlet,它帮我们实现了Servlet并屏蔽了一些不常用的方法,我们只需要完成其中的doGet()
与doPost()
方法即可。
其中request
对象与response
是HttpServletRequest与HttpServletResponse的对象,它们是最常用的两个对象,里面有很多常用的方法。为了使用它们,我们在实现doGet()
,doPost()
方法时要将它们作为参数传给这两个方法。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
...
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
...
}
request
对象:获取客户端数据。response
对象:响应客户端数据,向客户端发送数据。How to get the values of the parameters?
我们通过GET或者POST方式请求了Servlet并传递了key-value型的数据,那么如何得到它呢?
一个key对应一个value时:
假设以GET方式请求Servlet:http://localhost:8080/webstudy/hello-servlet?user=admin
。
String username = request.getParameter("user");
一个key对应多个value时(比如一个多选的Form表单):
假设在HTML页面中定义一个复选框,以POST方式请求Servlet。
<form action="demo-servlet" method="post">
您喜欢的水果?<br>
<label><input name="Fruit" type="checkbox" value="" />苹果 label>
<label><input name="Fruit" type="checkbox" value="" />桃子 label>
<label><input name="Fruit" type="checkbox" value="" />香蕉 label>
<label><input name="Fruit" type="checkbox" value="" />梨 label>
form>
在demo-servlet,可以通过下面的方法获取一组值。
String[] fruits = request.getParameterValues("Fruit");
How to get and set request attributes?
Attribute是服务器端的组件,当我们想要保存某个对象的时候可以使用setAttribute()
方法,将其保存在request对象中,取得已保存的对象可以使用getAttribute()
方法。
以key-value的形式存取对象。
User user = new User(username, password);
request.setAttribute("user1", user);
// 返回的是Object类,需要cast
User user = (User) request.getAttribute("user1");
setAttribute()只能保存对象,如果要保存基本类型的数据,应该使用它们的包装类。
int id = 1;
request.setAttribute("id", new Interger(id));
// 同样需要cast,同时Interger会自动拆箱,将数值赋值给id
int id = (Interger) requset.getAttribute("id");
How to forward requsets?
请求转发:
我们已经在request里存了一些对象,如何在其他地方得到它们呢?
可以使用转发的方式,将request转发到其他地方。
转发给网页
String url = "/demo.jsp";
request.getServletContext.getRequestDispatcher(url).forward(request, response);
转发给其他Servlet
String url = "/webstudy/hello-servlet";
request.getServletContext.getRequestDispatcher(url);
重定向:
重定向仅仅会使浏览器跳转到其他地方,而不会将request对象转发,属于response对象的方法。
重定向到其他Servlet
response.sendRedirect("/hello-servlet");
重定向值某个网页
response.sendRedirect("/demo.jsp");
How to work with the web.xml file?
前文提到通过修改web.xml的方式可以配置Servlet的映射地址,除此之外,你还可以:
配置初始参数(key-value)
对所有Servlet生效
<context-param>
<param-name>usernameparam-name>
<param-value>Zhang sanparam-value>
context-param>
在Servlet中取得初始参数:
// 对全局生效,使用ServletContext
String username = this.getServletContext().getInitParameter("username");
对特定的Servlet生效
<servlet>
<servlet-name>HelloServletservlet-name>
<servlet-class>com.webstudy.HelloServletservlet-class>
<init-param>
<param-name>usernameparam-name>
<param-value>Zhang sanparam-value>
init-param>
servlet>
在Servlet中取得初始参数:
// 对特定servlet生效,使用ServletConfig
String username = this.getServletConfig().getInitParameter("username");
设置欢迎页(默认主页,会从配置的第一个页面开始寻找)
<welcome-file-list>
<welcome-file>Demo.htmlwelcome-file>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
设置错误页面
<error-page>
<error-code>404error-code>
<location>/error_404.htmllocation>
error-page>
How the method of a servlet work?
HttpServlet
在编写Servlet应用程序时,大多数都要用到HTTP,也就是说可以利用HTTP提供的特性,javax.servlet.http包含了编写Servlet应用程序的类和接口,其中很多覆盖了javax.servlet中的类型,我们自己在编写应用时大多时候也是继承的HttpServlet。
当Web服务器接收到一个HTTP请求时,它会先判断请求内容——如果是静态网页数据,Web服务器将会自行处理,然后产生响应信息;如果牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容器会找到对应的处理该请求的Servlet实例来处理,结果会送回Web服务器,再由Web服务器传回用户端。
针对同一个Servlet,Servlet容器会在第一次收到http请求时建立一个Servlet实例,然后启动一个线程。第二次收到http请求时,Servlet容器无须建立相同的Servlet实例,而是启动第二个线程来服务客户端请求。所以多线程方式不但可以提高Web应用程序的执行效率,也可以降低Web服务器的系统负担。
(来源:简书-理解Servlet工作原理 - 简书 )
🎉🎉🎉想详细了解的同学推荐下面博主的文章~
🪧JSP简介|菜鸟教程 https://www.runoob.com/jsp/jsp-intro.html
🪧JavaBean详解-CSDN(顺其自然)https://blog.csdn.net/fuhanghang/article/details/84074561
🪧Java Web之EL表达式和JSTL-CSDN(XeonYu)https://blog.csdn.net/yuzhiqiang_1993/article/details/81274676
JSP:
JSP本质上是一个Servlet,JSP负责与用户交互,将最终的界面呈现给用户,JSP在HTML中嵌入了Java程序,属于HTML与Java代码的混合文件。
当服务器接收到xxx.jsp
的请求时,会将其交给JSP引擎,将JSP翻译为一个Servlet文件,再由Web容器调用Servlet完成响应。
JavaBean:
JavaBean是一个符合一定规范的Java类,通常用于封装数据,在JSP中可以通过
标签使用JavaBean。
EL表达式:
全称Expression Language-表达式语言,用于在JSP页面中取值。
JSTL:
全称JavaServerPages Standard Tag Libary-JSP标准标签库,用于简化JSP代码,常与EL表达式结合使用,EL表达式负责获取属性值,JSTL负责业务逻辑。
How to code a JavaBean?
JavaBean通常应该符合以下特点:
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
public User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
How to use EL to get attributes and JavaBean properties?
${attribute}
。int num = 666;
Integer number = new Interger(num);
request.setAttribute("number", number);
The number is ${number}
${attribute.property}
。User user = new User(username, password);
request.serAttribute("user", user)
Hello ${user.username}
🚀使用EL的时候,默认会以一定顺序搜索四个作用域,将最先找到的变量值显示出来。
🚀搜索顺序:page --> request --> session --> application。
Scope | Description |
---|---|
page | The bean is stored in the implicit PageContext object. |
request | The bean is stored in the HttpServletRequest object. |
session | The bean is stored int the HttpSession object. |
application | The bean is stored in the ServletContext object. |
推荐文章: |
RequestDispatcher实现请求转发 http://c.biancheng.net/view/4013.html
JavaWeb的四大作用域-小白一枚 https://www.cnblogs.com/jerry123/p/6974184.html
requset.getRequestDispatcher(url).forward(request, response);
。How to use the core JSTL library?
taglib
指令。<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${user.username == 'Zhang san'}">
<p>Welcome Zhang san!p>
c:if>
How to use JSP tags?
使用JSP标签不需要使用taglib
指令导包,直接就可以使用。
Tag | Name | Purpose |
---|---|---|
<%@ %> | JSP directive | JSP指令,用来导包,配置JSP的环境等等 |
<% %> | JSP scriptlet | 用来包含Java代码 |
<%= %> | JSP expression | JSP表达式,用来展示字符串或者表达式 |
<%-- --%> | JSP comment | JSP代码的注释 |
<%! %> | JSP declaration | JSP声明,用来声明变量或者方法 |
<%= %>
可以是任何一个类型,但最终都会转换为字符串,不用在结尾写分号。比如:
定义如下的Java代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP scriptlettitle>
head>
<body>
<%
int a = 1;
a++;
%>
<p>a的值是:<%=a%>p>
body>
html>
访问页面:
刷新页面:
可以看到刷新页面a的值不会改变,每次访问页面a的初值都为1,a++后变为2。
但是!!!
先定义JSP声明<%! int a = 1; %>
,再执行a++
这段Java代码。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP declarationtitle>
head>
<body>
<%!
int a = 1;
%>
<%
a++;
%>
<p>a的值是:<%=a%>p>
body>
html>
访问页面:
刷新页面:
🙌可以看到刷新一次页面,a的值会在前一次的基础上加1,这是因为JSP声明中的代码在转换为Servlet后,变量会成为全局变量,作为转换后的类的属性值,每次访问JSP页面,a的值不会刷新,仅会执行后面的Java代码段~
How to use standard JSP tags with JavaBeans?
在EL与JSTL被广泛用于JSP页面之前,访问JavaBean还可以使用standard JSP标签。
<p>
username:${user.username}
p>
<p>
password:${user.password}
p>
<jsp:useBean id="user" scope="request" class="com.example.UserBean"/>
<p>
username:<jsp:getProperty name="user" property="username"/>
p>
<p>
password:<jsp:getProperty name="user" property="password"/>
p>
new
一个新对象。null
值不会自动处理,对用户不友好。<jsp:useBean id="beanName" class="package.Class" scope="scopeValue" />
Value | Description |
---|---|
page | 将JavaBean保存在pageContext对象中(默认会保存在此) |
request | 将JavaBean保存在HttpServletRequest对象中 |
session | 将JavaBean保存在HttpSession对象中 |
application | 将JavaBean保存在ServletContext对象中 |
getProperty与setProperty:
<jsp:getProperty name="beanName" property="propertyName" />
<jsp:setProperty name="beanName" property="propertyName" value="John" />
Escape sequences within attributes
在JSP中,<%
、%>
、'
、"
、\
都有特殊的含义和用途,在使用它们时应修改成转义字符。
Character | Escape sequence |
---|---|
' | \' |
" | \" |
\ | \\ |
<% | <\% |
%> | %\> |
How to include a file in a JSP?
推荐文章:
jsp中引入其他jsp文件-博客园 https://www.cnblogs.com/liangb/p/10017101.html
如果一段代码在JSP页面中重复使用,比如HTML页面与JSP页面的头部或者尾部往往都是重复的代码,为了解决代码复用的问题,JSP页面可以引入其他JSP或者HTML文件。
A header file named header.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
head>
<body>
A footer file named footer.jsp
<%@ page import="java.util.GregorianCalendar, java.util.Calendar" %>
<%
GregorianCalendar currentDate = new GregorianCalendar();
int currentYear = currentDate.get(Calendar.YEAR);
%>
<p>© Copyright <%= currentYear %>p> Zhang san & Associates p>
body>
html>
在JSP页面中将上面两个文件引入
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--导入header.html--%>
<%@ taglib prefix="c" url="http://java.sun.com/jsp/jstl/core" %>
<c:import url="/includes/header.html" />
<jsp:useBean id="beanName" class="package.Class" scope="scopeValue" />
<jsp:getProperty name="beanName" property="propertyName"/>
<%--导入footer.jsp--%>
<c:import url="/include/footer.jsp" />
Method | Syntax |
---|---|
include directive(编译时包含) | <%@ include file="fileLocationAndName" %> |
standard JSP(运行时包含) |
|
JSTL tag(运行时包含) |
|
The difference of compile-time and runtime
.class
文件,被包含的页面都不会重新编译。.class
文件。Common JSP errors:
在查找JSP页面的错误时应该留意url是否正确;HTML,JSP,Java class文件是否在正确的位置。