Java Web、Tomcat、Servlet、JSP


Web服务器

WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源。

Internet上供外界访问的Web资源分为:

  • 静态资源:html页面
    • 实现:HTML
  • 动态资源:由程序产生,会经过程序实时处理
    • 实现:JSP/Servlet、ASP、PHP、ruby、python

在Java中,动态web资源开发技术统称为Java Web

主要服务器

服务器是一种被动程序:只有当Internet上运行在其他计算机中的浏览器发出请求时,服务器才会响应。

  • weblogic :BEA公司产品,被Oracle收购,全面支持JavaEE规范,收费软件,企业中非常主流的服务器、网络上文档非常全面
  • WebSphere :文档非常少,IBM公司产品,价格昂贵,全面支持JavaEE 规范
  • Tomcat :开源小型web服务器 ,完全免费,主要用于中小型web项目,只支持Servlet和JSP 等少量javaee规范 ,Apache公司jakarta 一个子项目
  • Jboss :hibernate公司开发。不是开源免费。J2EE容器

这里重点说广泛使用的Tomcat

Apache Tomcat是用Java语言编写的一个开源的Serlvet容器。
由于Tomcat本身也内含了HTTP服务器,因此也可以视作单独的Web服务器。
截至至2016年,Tomcat的市场占有率为58.22%,高出第二名近三倍

值得注意的是,Tomcat并不是严格意义上的服务器,只是个运行在物理服务器上的容器程序,是一个Servlet容器。

容器和服务器的联系:
容器和服务器的联系

容器是位于组件和服务器平台之间的接口集合,使得组件可以方便部署到服务器上运行。

更多关于各种容器与服务器的区别与联系参考这篇大佬的文章-> Servlet容器、WEB容器、Java EE容器、应用服务器、WEB服务器、Java EE服务器 的区别与联系

配置Tomcat

  1. 从官网 tomcat.apache.org 中下载tomcat安装程序。
  2. 解压安装,注意路径中不要包含空格与中文。
  3. 配置环境变量
    1. 确保有 JAVA_HOME 变量
    2. 新建 CATALINA_HOME 变量,值为Tomcat的根路径
    3. 新建 CATALINA_BASE 变量,值为Tomcat的根路径
  4. 双击tomcat/bin/startup.bat启动,如果在浏览器中访问 localhost:8080 能正确打开tomcat网页,则代表没问题了。
  5. 以下针对 InteliJ IDEA 配置Tomcat:
    1. "Run" -> "Edit Configurations" -> "+" -> "Tomcat Server";
    2. 配置路径与版本;
    3. "Deployment" -> "+" -> "Artifact...";

具体参考 IntelliJ IDEA配置Tomcat

Tomcat基本框架

Tomcat基本框架

  • Server :整个Servlet容器组件,是最顶层元素,包含多个Service元素
  • Service :一个Engine元素以及多个Connector元素
  • Connector :和客户端程序交互的组件,负责接收客户端请求和向客户端返回响应
  • Engine: :可以包含多个Host,Host表示一个虚拟主机,他可以包含多个web应用
  • Context: :运行在虚拟主机上的单个web应用
Tomcat目录结构

|--bin —— 它里面装入的是可执行的命令 如 startup.bat。
|--conf —— 它里面是一个相关的配置文件,我们可以在里面进行例如端口、用户信息的配置。
|--lib —— tomcat类库。
|--logs —— tomcat 日志文件
|--temp —— 临时文件
|--webapps —— 它里面放的是的 web site(web项目)
|--work —— 存放的是页面(例如 jsp)转换成的.class文件。

Tomcat配置文件

关于conf目录下的配置文件解析

Server.xml

server.xml 是Tomcat中最重要的配置文件
server.xml 的每一个元素都对应了Tomcat中的一个组件
因此可以通过对xml文件元素的配置,实现对Tomcat中各个组件的控制

正因为xml的结构与Tomcat的框架结构所对应,所以server.xml整体核心结构如下:

<Server> 	——代表整个Tomcat容器,必须唯一
    <Service>	——可以多个
        <Connector />	——可以多个
        <Connector />
        <Engine>	——一个service只能有一个组件
            <Host>	——可以多个,代表Engine的虚拟主机
                <Context />	——现在常常使用自动部署,强烈不推荐配置Context元素
            </Host>
        </Engine>
    </Service>
</Server>

Web.xml

启动web项目容器时,首先去读取的配置文件,但web.xml不是必须的

<?xml version="1.0" encoding="UTF-8"?> 
<web-app>	——根
    <welcome-file-list>	——项目打开的第一个页面
		<welcome-file>index.jsp</welcome-file>	——可以多个,按顺序查找
	</welcome-file-list>
        <servlet>	——servlet
		    <servlet-name>Servlet</servlet-name>
		    <servlet-class>com.zohn.servlet</servlet-class>
        </servlet>
 	<servlet-mapping>	——servlet映射
 		<servlet-name>Servlet</servlet-name>
		<url-pattern>/servlet</url-pattern>
 	</servlet-mapping>
	………………
</web-app>

Servlet

JavaWeb中有三大组件:ServletFilterListener
其中Servlet是最重要的组件.

Servlet主要功能在于交互式地浏览和修改数据,生成动态Web内容。

SUN公司定义了两个默认实现类,分别为:

  • GenericServlet(代理了ServletConfig的所有功能)
  • HttpServlet

而HttpServlet是能够处理Http请求的Servlet,在原有Servlet接口上添加了一些Http协议处理方法。

狭义的Servlet就是一个接口,全名Servlet.class,位于Servlet-api.jar包里的javax.servlet包内。
广义的Servlet是指任何实现了这个Servlet接口的类。一般情况下,人们将Servlet理解为后者。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

那servlet可以直接处理请求吗?
那是不可以的,相信我,你从来不会在servlet中写什么监听8080端口的代码,servlet不会直接和客户端打交道。

那请求怎么来到servlet呢?答案是上面已经提到过的,servlet的容器,比如Tomcat。

tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url信息和web.xml配置文件匹配,
确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,
tomcat再把这个response返回给客户端

工作机制图解如下:

Servlet的生命周期

Servlet的生命周期

  1. (启动)Tomcat在加载Web应用时,就会把相应的web.xml文件中的数据读入到内存中。
  2. 客户端向服务器发送请求数据。
  3. 服务器将获取到的url与内存中的xml映射数据进行匹配。
  4. 匹配到后根据映射名称找到相对应的Servlet路径。
  5. 服务器先检查要访问的servlet是否已经装载并创建了Servlet对象。
  6. 如果没有则创建该Servlet的实例对象(先执行构造方法,再执行init初始化方法)。
    ++注意:Servlet属于单例模式,初始化和实例化只执行一次++
  7. 调用service方法,一般根据父类已重写的方法来判断请求的类型。
  8. 执行相对应请求的doGet、doPost方法
    服务器向客户端响应数据。
  9. 当web容器退出,servlet销毁的时候,destroy方法执行。
    ++只会消亡(destroy)一次++
Servlet单实例多线程的特点
  1. Servlet单实例,减少了产生servlet的开销;
  2. 通过线程池来响应多个请求,减少了请求的响应时间;
  3. Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet,直接分配给它一个新的线程;如果是同一个Servlet的多个请求,那么Servlet的service方法将在多线程中并发的执行,因此,它也是线程不安全的

Servlet内对象的作用域

作用域就是一个存储信息的空间,并且可以在范围内访问信息。

ServletContext(Application)

ServletContext被称之为Servlet上下文对象,也就是对每一个servelt提供一个配置信息的环境,让每一个servlet都能够在它的背景之下。

ServletContext是全项目性的,随着服务器的创建而创建,随着服务器的消亡而消亡

生命周期:从web应用加载进容器时,到服务器关闭或web应用被移除时。

作用范围:整个web应用(相当于整个服务器)

作用

  1. 作为作用域进行存取值,这是servletContext最重要的作用。
  2. 获取服务器的信息
  3. 相对路径转绝对路径
  4. 获取全局初始化参数(在所在项目的web.xml配置文件中,我们可以给整个项目配置参数)

常用方法

public class ContextServlet1 extends HttpServlet {
 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext sc = this.getServletContext();
		
		/*
		 * 作用1:获取全局初始化参数
		 * 在web.xml文件中进行全局初始化参数配置,然后通过配置名来获取
		 * 
		 *  <context-param>
			<param-name>path</param-name>
			<param-value>D:/</param-value>
			</context-param>
		 */
		String path = sc.getInitParameter("path");
		System.out.println(path);
		
		/*
		 * 作用2:获取服务器的信息
		 */
		System.out.println("服务器信息:"+sc.getServerInfo());
		
		/*
		 * 作用3:相对路径转绝对路径
		 */
		String realPath = sc.getRealPath("upload");
		System.out.println(realPath);
		
		/*
		 * 作用4:作为作用域进行存值取值
		 * 以键值对的形式进行存储,前面为键,后面为值,获取值时,通过key值来进行获取
		 * 这个是一个范围比较大的作用域,作用范围是整个项目,所以上面获取的时候也是通过this对象来进行获取
		 * 在这个项目的所有servlet中都可以获取到这个作用域中的值。不过需要先进行获取,然后再去调用取值的servelt,
		 * 否则会报空指针异常
		 * 取得参数也是全局初始化参数
		 */
		sc.setAttribute("lufei", "xiangjiao");
		sc.setAttribute("suolong", "hedaoyiwenzi");
		List<String> list = new ArrayList();
		list.add("lufei");
		list.add("suolong");
		list.add("shanzhi");
		
		sc.setAttribute("caomao", list);
		
		String lufei = (String)sc.getAttribute("lufei");
		System.out.println(lufei);
		
		List caomao = (List) sc.getAttribute("caomao");
		for(String s : list ) {
			System.out.println(s);
		}
		
	}
 
}

注意:ServletContext其实就是application
区别只是:application用在jsp中,servletContext用在servlet中

Session

生命周期:从第一次调用getSession()方法时,到默认时间(30分钟)或服务器非正常关闭

作用范围:一次会话中

常用方法

设置:setAttribute( String , Object );
获取:getAttribute( String );
移除:removeAttribute( String );

Session信息

返回Session的ID:getId()
返回Session的创建日期:getCreationTime()
返回Session的最后活跃时间:getLastAccessedTime()
返回Session的超时时间:getMaxInactiveInterval()
设置Session的超时时间:setMaxInactiveInterval()

注意

  • 当要创建一个session时,服务器先检查这个请求中是否已包含一个sessionId的cookie。
    如果已经有了,就用这个id检索session出来使用。
    如果不包含才会创建一个session,因此id值是一个不会重复,也不易找规律的字符串。
  • 如果浏览器禁用了cookie,session也不能起作用。

生命周期:从设置Cookie开始,到设定时间结束(不设定时间则为关闭会话时)

作用范围:在设定时间内

常用方法

构造方法(名,值):new Cookie( String , String );
设置最大时间(秒):cookie.setMaxAge( int )

如果设置的时间为负数,则代表有效时间是当前会话结束

注意

  • 一个站点最多存放20个Cookie,每个Cookie大小最多只能4kb
  • 设置的最大时间如果是负数,则代表是当前会话结束,如果是0,则删除
  • 删除Cookie时,path必须一致
  • Cookie的效率会比Session高,但安全性就会低

Response

生命周期:从客户端发送请求开始,到得到服务器结果反馈

作用范围:一次请求响应

常用方法

设置头信息:response.setHeader( 内容 , 参数 );
刷新:response.setHeader( "refresh" , "1" );
按时跳转:response.setHeader( "refresh" , "2;url=?" );
添加Cookie给客户端:response.addCookie( Cookie );
向客户端打印数据:response.getWriter().write( String );
将请求重定向给别的资源:response.sendRedirect( String );

Request

生命周期:从客户端发送请求开始,到一个请求结束(转发将会继续用该请求链)

作用范围:整个请求链

常用方法

将数据作为属性放到request对象中:request.setAttribute( String , Object );
根据参数获取存放在request对象的属性值:request.getAttribute( String );
移除存放在request对象中参数所对应的属性值:request.removeAttribute( String );

从客户端请求中获取属性对应的值:request.getParameter( String );
从客户端请求中获取属性对应的数组值:request.getParameterValues( String );

将请求转发给别的资源:request.getRequestDispatcher( String ).forward( request , response );

获得客户端信息

客户端发出请求时的完整URL:getRequestURL
请求行中的资源名部分:getRequestURI
请求行中的参数部分:getQueryString
发出请求的客户机的IP地址:getRemoteAddr
发出请求的客户机的完整主机名:getRemoteHost
客户机所使用的网络端口号:getRemotePort
WEB服务器的IP地址:getLocalAddr
WEB服务器的主机名:getLocalName
客户机请求方式:getMethod

PageContext

生命周期:从对JSP请求时开始,到响应结束时销毁

作用范围:仅在JSP页面内

作用范围最小,很少使用。

总结

作用域能用小的,尽量用小的,不用大的,可以节约服务器的内存

JSP & EL & JSTL

JSP是用java做页面的一种常用方式。

(不过,大人,时代变了,由于各种原因已经逐渐被舍弃了,所以下面也是随便说说)

顺带一提,JSP实际上经过编译处理后还是Servlet。

JSP表达式

名称 语法 说明
JSP脚本表达式 <%= 变量或表达式 %> 里面不需要分号
JSP脚本片段 <% 多行Java代码 %> 里面必须遵循Java语法
JSP声明 <%! Java代码 %> 将默认翻译到service方法中的代码翻译到方法外面
JSP注释 <%-- 注释信息 --%> 翻译成Servlet程序时,忽略JSP注释中的内容
JSP指令 <%@ 指令 属性名="值" %> 为JSP引擎而设计告诉引擎如何处理JSP其余部分
<!--page指令:用于定义JSP页面的各种属性,作用整个页面,一般习惯应该放页面起始位置-->
<%@ page import="java.util.Date,java.sql.*,java.io.*"%>

<!--include指令:用于引入其他JSP页面,引擎会把这两个JSP翻译到一个Servlet,即静态引入-->
<%@ include file=“被包含页面的绝对URL或相对URL"%>
    
<!--taglib指令:用于在JSP页面中导入标签库-->
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

常用JSP标签

标签 语法 说明
include <jsp:include page=" " /> 把一个资源内容插入当前页面内容中即动态引入
forward <jsp:forward page=" " /> 把请求转发给另一个资源
param <jsp:include page=" "> <jsp:param name=" " value=" " /> </jsp:include> 在上面两个标签引入或转发时可以向资源传递参数

EL表达式

EL表达式语言(expression language)并不是一种开发语言,而是JSP中获取数据的一种规范

获取数据
  • 获取数据语法: $
    • 当语句执行时,会调用pageContext.findAttribute方法寻找标识符对应的对象
    • 寻找顺序是 page、request、session、application 四个域
    • 也可以指定一个域里查找,如:${ requestScope.标识符 }
      注意:找到则返回对象,找不到是返回 "" (不是null)
    • 也可以获取存储的对象的属性值,如:${ 对象.属性值 }
      注意:EL表达式在访问对象时依靠的是重写的get方法,它会先将写在里面的属性首字符大写,再拼接成getXX()方法,利用反射将对象构建出来,再执行getXX()方法。
      如果没有重写get属性的方法的话,EL表达式不能找到属性并报500错误
    • EL 有 11 个保留标识符,对应于 11个EL隐式对象
执行运算

EL表达式支持多种运算写法

中文 符号 英文
除以 $ $
取模 $ $
相等 $ $
不等 $ $
小于 $ $
大于 $ $
小于等于 $ $
大于等于 $ $
逻辑与 $ $
逻辑或 $ $
$ $
判断是否为 null 或 "" $
三目运算符 $
其他 . 、[ ]、()、+、-、*

在EL表达式里,+运算符为单纯相加,不支持拼接

JSTL库

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

核心标签
<!--需导入:-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
标签 描述
<c:out> 用于在JSP中显示数据,就像<%= ... >
<c:set> 用于保存数据
<c:remove> 用于删除数据
<c:catch> 用来处理产生错误的异常状况,并且将错误信息储存起来
<c:if> 与我们在一般程序中用的if一样
<c:choose> 本身只当做<c:when>和<c:otherwise>的父标签
<c:when> <c:choose>的子标签,用来判断条件是否成立
<c:otherwise> <c:choose>的子标签,接在<c:when>标签后,当<c:when>标签判断为false时被执行
<c:import> 检索一个绝对或相对 URL,然后将其内容暴露给页面
<c:forEach> 基础迭代标签,接受多种集合类型
<c:forTokens> 根据指定的分隔符来分隔内容并迭代输出
<c:param> 用来给包含或重定向的页面传递参数
<c:redirect> 重定向至一个新的URL.
<c:url> 使用可选的查询参数来创造一个URL
函数标签
<!--需导入:-->
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
函数 描述
fn:contains() 测试输入的字符串是否包含指定的子串
fn:containsIgnoreCase() 测试输入的字符串是否包含指定的子串,大小写不敏感
fn:endsWith() 测试输入的字符串是否以指定的后缀结尾
fn:escapeXml() 跳过可以作为XML标记的字符
fn:indexOf() 返回指定字符串在输入字符串中出现的位置
fn:join() 将数组中的元素合成一个字符串然后输出
fn:length() 返回字符串长度
fn:replace() 将输入字符串中指定的位置替换为指定的字符串然后返回
fn:split() 将字符串用指定的分隔符分隔然后组成一个子字符串数组并返回
fn:startsWith() 测试输入字符串是否以指定的前缀开始
fn:substring() 返回字符串的子集
fn:substringAfter() 返回字符串在指定子串之后的子集
fn:substringBefore() 返回字符串在指定子串之前的子集
fn:toLowerCase() 将字符串中的字符转为小写
fn:toUpperCase() 将字符串中的字符转为大写
fn:trim() 移除首位的空白符
其他标签
  • 格式化标签
  • SQL 标签
  • XML 标签

更详细的内容参考:

posted @ 2020-10-20 22:10  鹿友  阅读(454)  评论(0编辑  收藏  举报