Servlet/JSP技术说明

Servlet简介

Java Servlet是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。

使用Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。Java Servlet通常情况下与使用CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。

Servlet可以使用javax.servlet和 javax.servlet.http包创建,它是Java企业版的标准组成部分,Java企业版是支持大型开发项目的Java类库的扩展版本。

Servlet架构

Servlet的架构如下所示:

备注:这里的HTTP Server可以是Tomcat/Jetty等。

Java Servlet HelloWorld程序

(1)编写HelloWorld Servlet程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {
private String message;

public void init() throws ServletException {
message = "Hello World";
}

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("do nothing...");
}

public void destroy() {
// 什么也不做
}
}

(2)在tomcat中配置做Servlet的映射

按如下内容修改tomcat web.xml配置:

1
2
3
4
5
6
7
8
9
10
<web-app>      
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>

(3)启动tomcat并测试

在浏览器中输入http://127.0.0.1/HelloWorld即可访问。如下:

Servlet的生命周期

JAVA Servlet WEB应用程序部署规范

根据J2EE Servlet规范,WEB应用程序有特定的目录结构,将Servlet应用程序部署到Servlet容器(Tomcat/Jetty等)时必须按照这样的目录结构部署,具体结构如下:

当我们使用Eclipse等IDE工具创建WEB工程时,IDE会自动在webapp目录下按照这样的目录结构给开发者组织WEB应用。web.xml很重要,一般使用了Spring等框架时,该配置文件中定义了框架的核心Servlet。

需要注意的是,JAVA Web应用部署到容器后,/WEB-INF/classes/和/WEB-INF/lib/目录都属于CLASSPATH中一部分,JAVA Web应用可以直接使用这些目录中jar包中的类。

备注:我们构建项目生成的WAR包就是这个目录结构,WAR包是可以直接扔到Servlet容器(Tomcat/Jetty等)中被加载起来的。另外WAR包中也可以有META-INF的目录,该目录中会有MANIFEST.MF文件,该文件的内容同普通JAR中是一样的。这里要注意WAR包和JAR包的结构是有差比的。简单来说,WAR包是Java Web程序打的包,WAR包里面包括写的代码编译成的class文件,依赖的包,配置文件,所有的网站页面,包括html/jsp等等,一个WAR包可以理解为是一个WEB项目,里面是项目的所有东西。

其他闲杂知识

(1)Listener

监听器Listener,它是实现了javax.servlet.ServletContextListener接口的服务器端程序,它是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。在Servlet被Servlet容器初始化之前,可以向Servlet容器注册一些Listener来执行一些前置的工作逻辑,该Listener需要继承自javax.servlet.ServletContextListener。比如Spring 框架的监听器ContextLoaderListener会在服务器启动的时候实例化我们配置的bean对象、hibernate的session的监听器会监听session的活动和生命周期,负责创建,关闭session等活动。

以Spring的ContextLoaderListener举例来说:

1
2
3
4
5
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

(2)Filter

Filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个接收请求或者响应请求,它只是修改对某一资源的请求,或者修改对某一资源的响应。Servlet/JSP规范中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等。

JSP

在使用Servlet时,若想给浏览器返回HTML页面,那我们在Servlet中代码中插入HTML页面的字符串,那一个简单的页面都很复杂(当然现在结合模版引擎也很方便啦):

JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头,以%>结束。实际上jsp页面的本质还是Servlet,只不过是为了方面画界面所单独拉出来的一部分而已,到最终的实现还是会回到Servlet当中。

Servlet容器通过解析其中Servlet的别名,再找到对应的Servlet并执行其中的service方法做出处理和响应,那么对于Servlet容器来说它只认识Servlet,那么我们编辑的Jsp到底是如何被服务器识别的?实际上Servlet容器还是将*.jsp当作Servlet的别名在你局部配置下的web.xml找有没有对应的Servlet,如果没有找到对应的Servlet就去Tomcat的公共配置下的web.xml(Tomcat安装目录conf下的web.xml)找对应的Servlet。会找到如下内容:

org.apache.jasper.servlet.JspServlet会将对应的jsp文件转为Servlet。假设一个test.jsp文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
666
</body>
</html>

被org.apache.jasper.servlet.JspServlet转换成Servlet后,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/8.5.46
* Generated at: 2019-10-16 09:05:10 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {

private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();

private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

private static final java.util.Set<java.lang.String> _jspx_imports_packages;

private static final java.util.Set<java.lang.String> _jspx_imports_classes;

static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}

private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}

public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}

public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}

public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}

public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {

final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;


try {
response.setContentType("text/html; charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

out.write("\r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("666\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

但是我们发现这个类并没有继承HttpServlet而是继承了org.apache.jasper.runtime.HttpJspBase并且里面也没有service方法而是_jspservice方法。那么Tomcat就会到它的父类中找service方法并进行调用。实际上它的父类org.apache.jasper.runtime.HttpJspBase继承了HttpServlet。

jsp已经是一门比较落后的技术来,因为html、css、js是前端写的,而后端要在其中加入逻辑判断,那么首先你一定得认识前端语言的代码,否则你如何嵌套?这样很不利于前后端的分离。之所以学习是因为很多框架和项目中依然会用到它,但是最终还是会被时代所淘汰。