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
- 从官网 tomcat.apache.org 中下载tomcat安装程序。
- 解压安装,注意路径中不要包含空格与中文。
- 配置环境变量
- 确保有 JAVA_HOME 变量
- 新建 CATALINA_HOME 变量,值为Tomcat的根路径
- 新建 CATALINA_BASE 变量,值为Tomcat的根路径
- 双击tomcat/bin/startup.bat启动,如果在浏览器中访问 localhost:8080 能正确打开tomcat网页,则代表没问题了。
- 以下针对 InteliJ IDEA 配置Tomcat:
- "Run" -> "Edit Configurations" -> "+" -> "Tomcat Server";
- 配置路径与版本;
- "Deployment" -> "+" -> "Artifact...";
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中有三大组件:Servlet、Filter、Listener。
其中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的生命周期
- (启动)Tomcat在加载Web应用时,就会把相应的web.xml文件中的数据读入到内存中。
- 客户端向服务器发送请求数据。
- 服务器将获取到的url与内存中的xml映射数据进行匹配。
- 匹配到后根据映射名称找到相对应的Servlet路径。
- 服务器先检查要访问的servlet是否已经装载并创建了Servlet对象。
- 如果没有则创建该Servlet的实例对象(先执行构造方法,再执行init初始化方法)。
++注意:Servlet属于单例模式,初始化和实例化只执行一次++ - 调用service方法,一般根据父类已重写的方法来判断请求的类型。
- 执行相对应请求的doGet、doPost方法
服务器向客户端响应数据。 - 当web容器退出,servlet销毁的时候,destroy方法执行。
++只会消亡(destroy)一次++
Servlet单实例多线程的特点
- Servlet单实例,减少了产生servlet的开销;
- 通过线程池来响应多个请求,减少了请求的响应时间;
- Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet,直接分配给它一个新的线程;如果是同一个Servlet的多个请求,那么Servlet的service方法将在多线程中并发的执行,因此,它也是线程不安全的;
Servlet内对象的作用域
作用域就是一个存储信息的空间,并且可以在范围内访问信息。
ServletContext(Application)
ServletContext被称之为Servlet上下文对象,也就是对每一个servelt提供一个配置信息的环境,让每一个servlet都能够在它的背景之下。
ServletContext是全项目性的,随着服务器的创建而创建,随着服务器的消亡而消亡
生命周期:从web应用加载进容器时,到服务器关闭或web应用被移除时。
作用范围:整个web应用(相当于整个服务器)
作用:
- 作为作用域进行存取值,这是servletContext最重要的作用。
- 获取服务器的信息
- 相对路径转绝对路径
- 获取全局初始化参数(在所在项目的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
生命周期:从设置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 标签
更详细的内容参考: