[TOC]

0x01 快速入门

描述:在进行JavaWeb开发学习的时候必不可少就是Tomcat Web 容器服务器,因为它开源免费、便于上手,并且使用安装简单。

简单介绍:
Tomcat是一款开源软件、免费的Web容器软件,由Apache基金会旗下采用JAVA进行开发,Tomcat是一款Web容器自身不能作为负载均衡的软件;
主要应用于发布网页代码,提供网页信息服务,用户通过浏览可以实现界面的访问;Tomcat默认可以处理静态的网页,也可以处理动态的网页;

在这里我们就详细演示安装流程了,在我其他的Tomcat运维文章中有它的详细以及优化配置等.

学习环境准备:

WeiyiGeek.Tomcat

WeiyiGeek.Tomcat

Tomcat 7.0目录结构说明

1
2
3
4
5
6
7
* bin:Tomcat 启用binary以及一些启动jar包
* conf:Tomcat配置文档
* lib:Tomcat运行所依赖的jar文件
* logs:运行过程中的日志文件
* temp:临时文件
* webapps:项目发布的目录,以及war解压的目录;
* work:JSPbuild成为java文件的临时存放地


0x02 项目发布

描述:如何将项目发布到Tomcat中运行?

方式1:移动项目到Webapps目录(使用较多)
描述:冷部署可以直接将项目文件夹拖入Webapps目录之中并需要重新启动Tomcat;

1
2
3
4
#Tomcat控制台提示
# 信息: 把web 应用程序部署到目录 [W:\apache-tomcat-7.0.100\webapps\Demo1]
# 二月 15, 2020 10:40:11 下午 org.apache.catalina.util.SessionIdGeneratorBase crea
# eSecureRandom

WeiyiGeek.方式1

WeiyiGeek.方式1


方式2:Host配置虚拟路径
描述:通过在Conf/Server.xml配置文件中HOST元素接地添加上<Content>属性 130 行左右;该方式的区别不同于第一种是在于它不会在Tomcat控制终端日志中显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context
docBase="D:\apache-tomcat-7.0.100\webapps\Demo1"
path="/Demo2"
override="true">
</Context>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

<!--
属性解释:
1.docBase=存放web应用程序的文档基础(也称为上下文根)目录或者web应用程序存档文件(如果此web应用程序直接从WAR文件执行)。
2.path=网页访问的路径
3.override=设置为true可以忽略全局或主机默认上下文中的任何设置。默认情况下将使用默认上下文的设置,但是可能会被上下文的设置显式地覆盖相同的属性。
-->

参考地址:http://127.0.0.1:8080/docs/config/context.html#Defining_a_context
访问地址效果如上面图显示一致:http://127.0.0.1:8080/Demo2/index.xml


方式3:Engine配置虚拟路径
描述:与方式2大致相同不同的是进行加载的目录不同而已依赖于Engine进行单独的上下文元素可将Context元素复制到引擎文件中即可;

1
2
3
4
5
6
7
8
9
10
#$CATALINA_BASE/conf/[enginename]/[hostname]/
Server.xml
<Engine name="Catalina" defaultHost="localhost">
..
</Engome>

#由此可知路径为 Conf/Catalina/localhost 并在目录下建立一个Demo3.xml (访问时候便以/Demo3作为访问路径)
#即D:\apache-tomcat-7.0.100\conf\Catalina\localhost\Demo3.XML注意docBase指向Webapps中目录会报错
<?xml version='1.0' encoding='utf-8'?>
<Context docBase="D:\xampp\htdocs" override="true"></Context>

WeiyiGeek.Engine配置虚拟路径

WeiyiGeek.Engine配置虚拟路径


根据上面的分析可以总结出Tomcat主要包含了 2 个核心组件:连接器(Connector)容器(Container),

  • 1.一个Tomcat是一个Server,
  • 2.而一个Server下有多个Service,也就是我们部署的多个应用,一个应用下有多个连接器(Connector)和一个容器(Container)容器下有多个子容器;
  • 3.Engine下有多个Host子容器,Host下有多个Context子容器,Context下有多个Wrapper子容器。
WeiyiGeek.

WeiyiGeek.


0x03 项目打包

描述:在实际的开发中我们需要将我们的web工程打压成为war包或者jar包进行tomcat部署或者在jvm虚拟机中运行;

问:如何将项目打包成为jar?

  • 1.右键工程选择Export选择other在java目录下选择jar;
WeiyiGeek.

WeiyiGeek.

  • 2.进行导出jar配置一般默认就行;
WeiyiGeek.

WeiyiGeek.


0x04 Servlet基础

描述:Servlet[ /ˈsɜːvlɪt/ ] API 是运行在Tomcat Web服务器容器中的小型Java程序伺服小程式;小服务程,通过HTTP(超文本传输协议)接收和响应来自Web客户端的请求;更多的是配合动态资源做项目,当然也可以使用到Servlet只不过在Tomcat里面已经定义了一个DefaultServlet;


1.Hello World

描述:在上面Tomcat安装好后它给我们提供了一个示例页面,进行使用和学习 Servlet ( Servlet Examples with Code ) ;
地址: http://127.0.0.1:8080/examples/servlets/

静态项目创建流程:

  • 1.采用Eclipse建立一个Web工程,首先切换到 Java EE 视图,然后选择Server选项卡并且配置好Tomcat服务器;
WeiyiGeek.Step1

WeiyiGeek.Step1

  • 2.选择完成的Tomcat 7 Server 进行最后一步配置Web项目存放到新建立的wtpWebapps目录中保存即可,即D:\apache-tomcat-7.0.100\wtpwebapps;
WeiyiGeek.Step2

WeiyiGeek.Step2

  • 3.新建一个动态的Web工程(Dynamic Web Project),设置项目名称为HelloWorld;
WeiyiGeek.Step3

WeiyiGeek.Step3

  • 4.测试一个Web工程,在WebContent目录中建立一个静态资源index.html来测试应用服务器,选择项目进行Run on Server也可以快捷键ALT+SHIFT+X,R,第一次运行按照提示即可发布;
    访问地址:http://localhost:8080/HelloWorld/
WeiyiGeek.Step4

WeiyiGeek.Step4

注释:实际上是采用上面部署的第二种方法,在Server.xml中添加了一句<Context docBase="D:\apache-tomcat-7.0.100\wtpwebapps\HelloWorld" path="/HelloWorld" reloadable="true" source="org.eclipse.jst.jee.server:HelloWorld"/>


Servelet项目创建流程:

  • 5.在项目的JAVA Resource中的src中创建一个测试Servlet的pakeage,并且建立一个java文件实现Servlet;

/HelloWorld/src/cn/weiyigeek/servlet/HelloWorld.java

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
package cn.weiyigeek.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
* @author Administrator
* 说明:测试验证Servlet
*/
// 1.实现Servlet接口
public class HelloWorld implements Servlet {

//2.重写里面的方法
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("<p style='color:red'>Hello World, Servlet!</p>");

}
@Override
.....
}

WeiyiGeek.Step5

WeiyiGeek.Step5

  • 6.配置Servlet项目在WebContent > WEB-INF > web.xml添加Servlet的名称以及类.包名称cn.weiyigeek.servlet.HelloWorld,然后添加注册一个Servlet映射url访问路径,重新发布项目即可;
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>HelloWorld</display-name>
    <!-- 默认的首页地址 -->
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>

    <!-- 1.告知Tomcat我们应用中有个Servlet,名字叫做HelloHelloWorld,以及包.类的路径名称; -->
    <servlet>
    <servlet-name>HelloWorld</servlet-name>
    <servlet-class>cn.weiyigeek.servlet.HelloWorld</servlet-class>
    </servlet>


    <!-- 2.注册Servlet映射以及URL匹配访问的路径 -->
    <servlet-mapping>
    <servlet-name>HelloWorld</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <web-app>
WeiyiGeek.Step6

WeiyiGeek.Step6

响应流程:
(1) 当用户访问http://127.0.0.1:8080/HelloWorld/hello时候,匹配到了注册的Servlet映射的url路径,反向找到servlet-name应用名称
(2) 再从Servlet应用中查询出该servlet name名称的应用,并反向找到其包名.类名称;
(3) 编译并且创建cn.weiyigeek.servlet.HelloWorld该类的实例,然后Tomcat通过Java VM虚拟机运行其所产生的字节码文件;
(4) 继而执行该Servlet中的services方,并且反馈输出到我们的控制台之中;


2.HttpServlet

描述:在Eclipse中选择接口名称按CTRL+T键,显示其继承层级已经实现了Servlet接口的一些通用写法,来避免重复重写Servlet中的方法;

1
Servlet 接口 -> GenericServlet -> HttpServlent (用于处理HTTP的请)

WeiyiGeek.通用写法

WeiyiGeek.通用写法

我们参照Tomcat给我们提供的helloworld.html示例文档进行实现,在上面的基础之上添加一个新的class文件,进行继承HttpServlet以及复习doGet和doPost方法;

基础示例:

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
// cn.weiyigeek.servlet.HttpHelloWorld
package cn.weiyigeek.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @author Administrator
* 说明:实现 Generial通用httpServet继承的复写
*/

public class HttpHelloWorld extends HttpServlet {

//1.get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应文本内容类型
resp.setContentType("text/html");
//实例化并设置响应文本内容
PrintWriter out = resp.getWriter();
out.print("<!DOCTYPE HTML><html><head><title>HttpServlet Hello</title></head><body><h1>Hello World,HttpServlet!</h1></body></html>");
}

//2.post请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}

// Web.xml:添加 servlet 映射
<web-app ...>
<servlet>
<servlet-name>HttpHelloWorld</servlet-name>
<servlet-class>cn.weiyigeek.servlet.HttpHelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HttpHelloWorld</servlet-name>
<url-pattern>/web</url-pattern>
</servlet-mapping>
</web-app>

WeiyiGeek.

WeiyiGeek.


3.Servlet生命周期

问:什么是生命周期?什么是生命周期方法?

从创建到销毁的一段时间,从创建到销毁的所调用的方法;

Servlet生命周期流程:

  • 1.init() 初始化:在创建该Servlet实例时候执行该方法(也可以提前进行初始化后面代码实现),在生命周期内只会在启动后初次访问时候触发一次,后续访问不会再触发;
  • 2.service() 服务请求:当客户端每来一个请求就要触发执行该方法;
  • 3.destory() 销毁:该项目从tomcat应用中移出的时候以及tomcat服务器正常shutdown的时候会触发执行该方法;


提前Servlet初始化:
描述:在有时候我们可能需要在init初始化这个方法中执行一些初始化工作,甚至做一些比较耗时的操作的逻辑,那么这时我们可以将初始化的时机进行提前到应用启动的时候;
配置:在需要的servlet提前初始化运行的servlet名称下添加load-on-startup元素;

基础示例:
cn.weiyigeek.servlet.Lifecycle

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
package cn.weiyigeek.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
*
* @author Administrator
* 说明:Servlet 声明周期的验证
*/
public class Lifecycle implements Servlet {
static int num = 0;

@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
System.out.println("1.Lifecycle 初始化操作 ...");
}

@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
num++;
System.out.println("2.Lifecycle 服务提供操作,您访问了 "+num+" 次!");
}

@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}

@Override
public void destroy() {
// TODO Auto-generated method stub
System.out.println("3.Lifecycle 销毁操作...");
}
}



//web.xml
<web-app ...>
<!-- 声明周期验证并提前初始化 -->
<servlet>
<servlet-name>Lifecycle</servlet-name>
<servlet-class>cn.weiyigeek.servlet.Lifecycle</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Lifecycle</servlet-name>
<url-pattern>/lifecycletest</url-pattern>
</servlet-mapping>
</web-app>

执行结果:

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
信息: Starting Servlet Engine: Apache Tomcat/7.0.100
二月 17, 2020 12:21:37 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
警告: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [293] milliseconds.

1.Lifecycle 初始化操作 ...
二月 17, 2020 12:21:37 上午 org.apache.catalina.startup.HostConfig deployDescriptor
信息: 部署描述符[D:\apache-tomcat-7.0.100\conf\Catalina\localhost\Demo3.xml]的部署已在[69]ms内完成

二月 17, 2020 12:21:37 上午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextInitialized()


信息: 开始协议处理句柄["http-bio-8080"]
二月 17, 2020 12:21:38 上午 org.apache.catalina.startup.Catalina start
信息: Server startup in 1887 ms

2.Lifecycle 服务提供操作,您访问了 1 次!
2.Lifecycle 服务提供操作,您访问了 2 次!
2.Lifecycle 服务提供操作,您访问了 3 次!

信息: Pausing ProtocolHandler ["http-bio-8080"]
二月 17, 2020 12:32:36 上午 org.apache.catalina.core.StandardService stopInternal
信息: 正在停止服务[Catalina]

3.Lifecycle 销毁操作...
二月 17, 2020 12:32:36 上午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextDestroyed()
二月 17, 2020 12:32:36 上午 org.apache.coyote.AbstractProtocol stop
信息: 正在停止ProtocolHandler ["http-bio-8080"]
二月 17, 2020 12:32:36 上午 org.apache.coyote.AbstractProtocol destroy
信息: 正在摧毁协议处理器 ["http-bio-8080"]

注意事项:

  • 1.生命周期方法是指,从对象创建到销毁一定会执行的方法,而doGet和doPost不算生命周期方法因为他们可能会执行或者不会执行;
  • 2.Servlet初始化提前load-on-startup值越小优先级越高且是一个正值;


4.Servlet配置对象

描述:我们可以利用Servlet配置对象进行获取或检测web.xml中的servlet配置信息;
在未来我们进行实际开发时候,采用其他人开发出来的servlet类,我们使用它的jar进行导入到我们的工程之中,然后它设置的servlet必须注册某一个参数才可以使用,否则爆出异常;

如何声明init-params和init-values?

在Web.xml中的servlet元素中加入<init-param><param-name>name</param-name><param-value>WeiyiGeek</param-value></init-param>等子元素和孙子元素;

基础示例:

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
///HelloWorld/src/cn/weiyigeek/servlet/SevletConfig.java
package cn.weiyigeek.servlet;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @author Administrator
* Descript: 验证ServletConfig的使用获取web.xml配置的信息
*/
public class SevletConfig extends HttpServlet{

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获得Servlet配置对象专门用于配置servlet的信息
ServletConfig config = getServletConfig();

//2.可以获取具体servlet配置中的名称;
String servletName = config.getServletName();
System.out.println("Servlet-Name = " + servletName);

//3.检测参数是否存在被设置进行非法参数异常抛出(值得学习 illeal 英 /ɪˈliːɡl/ )
String myself = config.getInitParameter("WeiyiGeek");
if(myself == null) {
throw new IllegalArgumentException("在Servlet配置中未找到WeiyiGeek参数,请核验Web.xml文件!");
} else {
System.out.println("WeiyiGeek load ....");
}

//4.获取单个具体参数
String name = config.getInitParameter("name");
System.out.println("配置文件中 name 值为" + name);

//5.遍历配置中设置的多个参数
Enumeration<String> para = config.getInitParameterNames();
while(para.hasMoreElements()) {
String key = para.nextElement();
String value = config.getInitParameter(key);
System.out.println("参数"+key+",值="+value);
}
}
}

web.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<web-app>
<!-- 4.验证ServletConfig配置 -->
<servlet>
<servlet-name>ServletConfig</servlet-name>
<servlet-class>cn.weiyigeek.servlet.SevletConfig</servlet-class>
<init-param>
<param-name>WeiyiGeek</param-name>
<param-value>ServletConfig</param-value>
</init-param>
<init-param>
<param-name>name</param-name>
<param-value>WeiyiGeek</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>88</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletConfig</servlet-name>
<url-pattern>/config</url-pattern>
</servlet-mapping>
</web-app>

执行结果:

WeiyiGeek.Servlet配置对象

WeiyiGeek.Servlet配置对象


5.Servlet配置方式

描述:Servlet 配置方式常用的有三种:
1) 全路径匹配

web.xml 中的 <servlet> </servlet> 标签中以 / 开始 /a 或者 /aa/bb
访问: 127.0.0.1:8080/项目名称/aa//bb 访问即可

2)路径匹配,前版本匹配

以 / 开始,但是以 结束 /a/ 或者 /*;
访问: localhost:8080/项目名称/aa/bb

3) 以扩展名开始

没有 / 而是以 开始 .扩展名称,比如 *.aa 或者 .bb
访问:localhost:8080/项目名称/weiyigeek.aa


在下面我演示Servlet的第二种配置方式:

  • 1.右键新建立一个Servlet文件,需要您提供包package和类名称;
WeiyiGeek.

WeiyiGeek.

  • 2.删除建立的ServletMethod文件中不需要的方法,并且查看web.xml 默认生成的servlet配置;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <servlet>
    <description></description>
    <display-name>ServletMethod</display-name>
    <servlet-name>ServletMethod</servlet-name>
    <servlet-class>cn.weiyigeek.servlet.ServletMethod</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>ServletMethod</servlet-name>
    <url-pattern>/ServletMethod</url-pattern>
    </servlet-mapping>
  • 3.验证上面的配置方式的一种将url-pattern进行更改

    1
    2
    3
    -- 1. <url-pattern>/aa/bb</url-pattern>
    -- 2. <url-pattern>/aa/*</url-pattern>
    -- 3. <url-pattern>*.weiyigeek</url-pattern>

执行结果:

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
package cn.weiyigeek.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet implementation(实现) class ServletMethod
*/
public class ServletMethod extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
System.out.println("有人访问了一个请求...!!");
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}

WeiyiGeek.Servlet配置方式

WeiyiGeek.Servlet配置方式


0x05 Servlet进阶

1.ServletContext对象

描述:每个JAVA虚拟机中的WEB application应用工程只有一个ServletContext对象,简单的说就是不管在哪一个servlet里面获得到的这个类的对象都是同一个;

ServletContext对象的作用

  • 1.获取全局配置参数
  • 2.获取Web工程中的资源
  • 3.存取数据Servlet间共享数据(域对象)

ServletContext 生命周期
问题:ServletContext何时创建,何时销毁?

创建:服务器启动的时候,会为托管的每一个Web应用程序,创建一个ServletContext对象;
销毁:从服务器移除托管或者是关闭服务器;

ServletContext 作用范围:同一个项目之中共享数据,但是如果是不同的项目之间存取值是不一样的(取不到),因为ServletContext的对象不同;


1) 采用ServletContext获取资源文件
在工程中的web.xml中建立的context-param参数是全局的上下文参数或者在WebContext中建立一个文件夹存储Properties文件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 1.在Servlet中采用getServletContext获取对象-->
ServletContext context = getServletContext();

<!-- 2.全局上下文参数-->
<context-param>
<param-name>name</param-name>
<param-value>WeiyiGeek</param-value>
</context-param>

<context-param>
<param-name>age</param-name>
<param-value>100</param-value>
</context-param>

<!--3.在WebContext目录中建立一个目录和prop文件-->
Config/WeiyiGeek.Properties;


基础示例:

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
package cn.weiyigeek.servlet;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Servletcontext1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//方式1:ServletContext获取资源文件
Scmethod1(response);

//方式2:ServletContext + Propersties 获取资源文件
Scmethod2(response);

//方式3:ServletContext资源文件获取
ServletContext sc = getServletContext();
Properties prop = new Properties();
//给相对路径然后直接获取文件绝对地址和读取文件转化成为流对象
InputStream is = sc.getResourceAsStream("Config/WeiyiGeek.Properties");
prop.load(is);
System.out.println("\n方式三:ServletContext资源文件获取\n");
System.out.println("Name = " + prop.getProperty("name") + "\n");
System.out.println("Age = " + prop.getProperty("age"));
}


//方式2
public void Scmethod2(HttpServletResponse response) throws FileNotFoundException, IOException {
//1.获取上下文对象
ServletContext context = getServletContext();
//2.或者Properties中的参数值采用ServletContext进行读取
Properties prop = new Properties();
String proppath = context.getRealPath("Config/WeiyiGeek.Properties"); //获取到了WeiyiGeek.Properties绝对路径
System.out.println("Properties 配置文件路径 = " + proppath);
//Properties 配置文件路径 = D:\apache-tomcat-7.0.100\wtpwebapps\HelloWorld\Config\WeiyiGeek.Properties

//3.指定properties数据源(但是需要注意这里是web应用,如果想获取web工程的下资源需要采用ServletContext获取配置文件路径)
InputStream is = new FileInputStream(proppath);
prop.load(is);

//4.获取属性的值
String name = prop.getProperty("name");
String age = prop.getProperty("age");
response.getWriter().append("\n ------------------ \n");
response.getWriter().append("ServletContext-> Properties Param Name = ").append(name + "\n");
response.getWriter().append("ServletContext-> Properties Param Age = ").append(age);
}

//方式1
public void Scmethod1(HttpServletResponse response) throws IOException {
//1.获取上下文对象
ServletContext context = getServletContext();

//2.获取web.xml中Context-param中设置全局配置参数的key和value;
response.getWriter().append("ServletContext Param Name = ").append(context.getInitParameter("name") + "\n");
response.getWriter().append("ServletContext Param Age = ").append(context.getInitParameter("age"));
}
}

WeiyiGeek.

WeiyiGeek.


注意事项:

  • 1.在Web工程按照我们前面学习的Properites进行读取配置文件是不行,如果您将prop文件建立在src中在web项目部署的时候会保存到WEB-INF/CLASSES/目录中,导致FileInputStream不能够正常读取到该文件则properties方式也不能获取到参数的值,因为此时FIleInputStream流程读取是bin/src/xxx.Properties;
  • 2.解决注意1的方式是采用ServletContext中的getRealPath方法获取到webContext的绝对路径,然后在指定dir/xx.Properties之后常规读取即可
  • 3.对注意2进行优化可以直接采用ServletContext中的getResoureAsStream方法读取文件流,然后采用prop.load()加载此对象即可;


2) 使用ClassLoader获取资源文件
描述:采用ClassLoader可以直接读取当前工程下的字节码文件和配置文件;

基础案例:

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
package cn.weiyigeek.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Servletcontext2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.打印getClassLoader路径
System.out.println(this.getClass().getClassLoader());

//2.实例化Properties和InputStream对象
Properties prop = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream("../../Config/WeiyiGeek.Properties");
prop.load(is);
System.out.println("姓名:" + prop.getProperty("name") + ",年龄 : " + prop.getProperty("age") );
is.close();
}
}

访问:

1
2
3
4
5
6
7
8
9
10
#运行结果:
WebappClassLoader
context: /HelloWorld
delegate: false
repositories:
/WEB-INF/classes/ #因为默认是在这个目录之中,而我们建立的是在WebContext中则需要两次回到上级目录之中
----------> Parent Classloader:
[email protected]

姓名:WeiyiGeek,年龄 : 2020


3) ServletContext存取数据
描述:此处采用ServletContext进行获取登录成功的总数,具体流程如下:

  1. 获取提交过来的数据
  2. 判断账号密码数据是否有误
  3. 如果正确进行页面的跳转并且输出”该用户是网站成功登陆的第几人”,采用Servlet记录其值;
  4. 如果错误输出”登录失败”;

补充说明:一个请求URL对应一个Servlet文件进行处理;

基础语法说明:

1
2
3
4
5
//获取设置的属性值
getServletContext().getAttribute("key")

//设置属性值
getServletContext().setAttribute("key",value);

实例演示:

1
2
3
4
5
6
7
//Servlet Class 类
Login
CountTotal

//HTML页面
Login.html
Success.html

WeiyiGeek.

WeiyiGeek.

基础代码:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Login.html -->
<!-- 注意 action 这里由于login.html 与 Login 是处于同一级目录的,您也可以写 /项目名称/Login -->
<form action="Login" method="get">
用户名:<input type="text" name="username"/>
密 码:<input type="password" name="password"/>
<input type="submit" value="登录"/>
</form>

<!-- Success.html -->
<h1> 尊敬的用户您已成功登陆! </h1>
<hr/>
<a href="CountTotal">获取成功登陆的次数</a>

java代码:

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
//Servlet -> Login
public class Login extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取登录页面传递的参数
String user = request.getParameter("username");
String pass = request.getParameter("password");
System.out.println("Get 请求获取的参数 :user = " + user + "?pass = " + pass );

//2.判断用户输入账号密码是否正确
if("admin".equals(user) && "123".equals(pass))
{

//3.登录成功在响应和终端控制台进行打印
response.getWriter().append("登录成功!");

//4.记录和获取在Servlet中存储登录成功的值
int count = 0;
Object ob = getServletContext().getAttribute("total"); //获取ServletContext属性
if(ob != null )
{
count = (int) ob;
}
getServletContext().setAttribute("total", ++count); //设置存储ServletContext属性
System.out.println("登录成功! 您累积成功登陆 " + count +"次");

//5.设置登录成功跳转页面
response.setStatus(302); //设置状态码
response.setHeader("Location", "success.html"); //设置跳转头
}else {
response.getWriter().print("登录失败,账号或者密码错误!");
System.out.println("账号或者密码错误!");
}
}
}


//Servlet -> CountTotal
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.在ServletContext中获取成功登陆的次数
int total = (int) getServletContext().getAttribute("total");

//2.打印输出成功的次数
response.getWriter().append("尊敬的用户您成功登陆 " + total +" 次");
}

WeiyiGeek.


2.HttpServletRequest

1) Request获取请求头
基础语法:

1
2
request.getHeaderNames(); //返回一个枚举合集Enumeration请求头
request.getHeader("请求头部"); //获取该请求的响应头值


2) Request请求信息提取
基础语法:

1
2
3
4
5
6
System.out.println("当前客户端协议  :" + request.getProtocol());
System.out.println("当前请求编码 :" + request.getCharacterEncoding());
System.out.println("项目名称 :" + request.getContextPath());
System.out.println("项目URL:" + request.getRequestURI());
System.out.println("本机信息 :" + request.getLocalName() + " - " + request.getLocalAddr() + " - " + request.getLocalPort());
System.out.println("客户端信息 : " + request.getRemoteUser() + " - " + request.getRemoteAddr() + " - " + request.getRemoteHost() + " - " + request.getRemotePort());


3) Request请求数据拿取
基础语法:

1
2
3
4
5
6
7
//请求参数值获取单个
request.getParameter("key")
//方式1.获取多个请求参数为集合
request.getParameterMap(); // Map Set Iterator 等
//方式2:获取多个请求参数为集合
request.getParameterNames(); // 获得参数名称
request.getParameterValues("key") //利用参数名称查询其值


4) Request请求中文乱码解决
描述:在客户端进行Get/POST请求的时候在URL地址已经经过了url编码,而Tomcat获取到的这批数据getParameter默认使用ISO-8859-1去解码,所以会导致答应中文乱码;

解决办法:

1
2
3
4
5
6
7
8
9
//1.终端/网页显示不乱码(推荐形式-成功)
String val = new String(value[i].getBytes("ISO-8859-1"),"utf-8");

//2.网页回显不乱码(POST 请求 - 未验证成功)
request.setCharacterEncoding("UTF-8"); //输入的格式不乱码 (需要写在获取参数之前 - POST 请求使用)

//3.直接在Tomcat中进行配置以后GET请求的数据永远都是UTF-8编码
//conf/server.xml
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URLEncoding="UTF-8" />


基础示例(1):

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
package cn.weiyigeek.httpServletRequest;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HttpHeader extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//0.采用网页进行回显
request.setCharacterEncoding("utf-8"); //输入的格式不乱码
response.setContentType("text/html"); //输出格式

//1.枚举集合
response.getWriter().append("------- 请求头获取 ------- <br/>");
Enumeration<String> headers = request.getHeaderNames();

//2.循环迭代请求头
while(headers.hasMoreElements())
{
String reqname = headers.nextElement();
String reqvalue = request.getHeader(reqname);
//3.向网页中进行输出
response.getWriter().append(reqname + ":" + reqvalue + "<br/>");
}

response.getWriter().append("<br/> ------- 获取多个参数 ------- <br/>");
//4.方式1:获取多个请求参数(值得学习)
Map<String, String[]> map= request.getParameterMap(); //map 集合
Set<String> keySet = map.keySet(); //Set 集合
Iterator<String> iterator = keySet.iterator(); //迭代器
while(iterator.hasNext()) {
String key = (String) iterator.next();
String[] value = map.get(key); //默认以首次出现的参数名称为准

//5.验证存在多个相同的参数
if(value.length > 1){
for (int i = 0; i < value.length; i++) {
String val = new String(value[i].getBytes("ISO-8859-1"),"utf-8"); // 防止输入输出的中文乱码(终端|网页)
response.getWriter().append(key+ ":" + val + "<br/>");
}
}else {
response.getWriter().append(key+ ":" + value[0] + "<br/>");
}
}

//6.方式2:获取请求的参数(中文输出不乱码)
Enumeration<String> para = request.getParameterNames();
while(para.hasMoreElements())
{
String paraname = para.nextElement();
String[] value = request.getParameterValues(paraname);
if(value.length > 1) {
for (int j = 0; j < value.length; j++) {
String val = new String(value[j].getBytes("ISO-8859-1"),"utf-8"); // 防止输入输出的中文乱码(终端|网页)
System.out.println(paraname + " = " + val);
}
}else {
System.out.println(paraname + " = " + value[0] + "\n");

}
}
}
}

WeiyiGeek.执行结果

WeiyiGeek.执行结果


基础示例(2):POST请求验证输入输出不乱码和请求信息获取;

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
public class HttpPostInfo extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//2.POST请求输出不乱码
request.setCharacterEncoding("UTF-8");

//3.获取输出POST请求的参数
System.out.println("编码设置后 --- name = " + new String(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8") + "\n----");

//4.获取客户端的信息
System.out.println("当前客户端协议 :" + request.getProtocol());
System.out.println("当前请求编码 :" + request.getCharacterEncoding());
System.out.println("项目名称 :" + request.getContextPath());
System.out.println("项目URL:" + request.getRequestURI());
System.out.println("本机信息 :" + request.getLocalName() + " - " + request.getLocalAddr() + " - " + request.getLocalPort());
System.out.println("客户端信息 : " + request.getRemoteUser() + " - " + request.getRemoteAddr() + " - " + request.getRemoteHost() + " - " + request.getRemotePort());
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.POST 请求入口
System.out.println("POST 请求.....");
System.out.println("编码设置前 --- name = " + request.getParameter("name"));
doGet(request, response);
}
}

//基础信息
POST 请求.....
编码设置前 --- name = ??????
编码设置后 --- name = 张伟
----
当前客户端协议 :HTTP/1.1
当前请求编码 :UTF-8
项目名称 :/HelloWorld
项目URL:/HelloWorld/HttpPostInfo
本机信息 :HackOne - 192.168.1.3 - 8080


3.HttpServletResponse

描述: 服务器端返回给客户端的内容信息;
1) 响应数据
基础语法:

1
2
response.getWriter().write("<h1>字符集</h1>"); //以字符流的方式写数据
response.getOutputStream().write("Hello World!".getBytes()); //字节流的方式写数据


2) 响应中文乱码
描述:在请求响应中有中文字符乱码的存在,在使用Tomcat的Servlet进行写出去的文字默认是以ISO-8859-1编码写出,所以我们需要采用指定编码进行写出防止乱码;
基础语法:

1
2
3
4
5
6
7
8
// 以字符流的方式 
response.setHeader("Content-type","text/html; charset=UTF-8"); //规定浏览器显示什么编码
response.setCharacterEncoding("UTF-8"); //响应内容编码(根据浏览器有关)
response.getWriter().write("<h1>字符集</h1>"); //以字符流的方式写数据

// 以字节流的方式
response.setContentType("text/html; charset=UTF-8"); //响应内容格式和编码
response.getOutputStream().write("中文字符串".getBytes("UTF-8")); //默认输出是UTF-8

总结:

  • 不管是字节流还是字符流直接使用setContentType()方法进行响应格式和编码,之后直接写数据即可;


3) 响应头设置

基础语法:

1
2
response.setStatus(302) //响应状态码设置
response.setHeader("Location","WeiyiGeek.top") //响应头设置


4) Servlet请求重定向和转发
描述:重定向与转发的区别;

  • 1.客户端显示URL不同:前者重定向的地址(此时request对象存储的数据中原来的参数将不会被带人),后者用户访问的Servlet地址(会将参数一起待入到转发的页面);
  • 2.请求次数的不同:前者由于返回302状态码Clint请求了两次,后者只请求了一次;
  • 3.跳转的限制:前者任意工程跳转,后者自己项目工程调整;
  • 4.效率对比:前者效率较后者低;

基础语法:

1
2
3
4
5
6
7
8
9
//重定向早期案例
response.setStatus(302);
response.setHeader("Location","Login_Success.html");
//重定向常用案例(即重新定位方向即Login_Success.html网页地址)
response.sendRedirect("Login_Success.html")

//请求转发案例(但是请求的URL还是原地址不是Login_Success.html,服务器内部进行处理了后续的工作)
//带参数和跳转位置进行拼接到请求的Servlet
response.getRequestDispatcher("Login_Success.html").forward(request,response);

WeiyiGeek.区别

WeiyiGeek.区别


5) 资源下载

  • 1.采用超连接的形式下载定位静态资源,但是遇到jpg或者txt类型的数据还是可以下载,只不过要右键另存为,所以当我们没有写Servlet文件进行处理,也能进行解析,原因是由于Tomcat里有个默认的Servlet叫DefaultServlet专门处理放在Tomcat服务器上的静态资源;
  • 2.手动编码进行下载,设置响应头Content-Disposition: attachment; filename="文件名称";

基础示例:资源下载

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
public class HttpFileDown extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.返回给客户端的文字内容使用的默认编码
response.setCharacterEncoding("UTF-8");

//2.指定浏览器解析数据的格式和编码
//response.setHeader("Content-Type", "text/html; charset=UTF-8");

//3.采用字符流进行输出中文不乱码
String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8"); //下载文件带中文字符
//response.getWriter().write("1.字符流(方式):当前您下载的文件是 " + filename + "<br/>");
//String csn = Charset.defaultCharset().name(); //getBytes()默认码表
//response.getWriter().append("2.getBytes()默认码表:" + csn);

//4.采用字节流进行指定编码输出中文不乱码(其实getBytes()方法的默认码表就是UTF-8与Tomcat默认码表无关系)
//response.setContentType("text/html; charset=UTF-8"); //响应内容格式和编码

String path = getServletContext().getRealPath("Config/"+filename);
String name = "3.字节流(方式):当前您下载的文件路径是 :" + path;
//response.getOutputStream().write(name.getBytes("UTF-8")); //注意不能和字符流同时存在;
//response.getOutputStream().print(name);

//5.设置下载文件的响应头并且如果存在中文名称需要对其进行编码;
/*
* 如果IE或者Chrome使用的URLEncoding编码
如果是Firefox使用的是base64编码
*/

String clientType = request.getHeader("User-Agent");
//注意大小写,当然为了方便您可以
if(clientType.contains("Firefox"))
{
filename = base64EncodeFileName(filename);
} else {
filename = URLEncoder.encode(filename,"UTF-8");
}
response.setHeader("Content-Disposition", "attachment; filename="+filename); //返回文件中文不乱码


//6.读取文件到字节流然后进行下载
InputStream is = new FileInputStream(path); //采用绝对路径进行读取文件
OutputStream os = response.getOutputStream();

int len = 0;
byte[] buf = new byte[1024];
while((len = is.read(buf)) != -1)
{
//7.写出文件
os.write(buf, 0, len);
}
//8.关闭输入输出流
os.close();
is.close();
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}

//进行Base64编码
public static String base64EncodeFileName(String fileName) {
BASE64Encoder base64Encoder = new BASE64Encoder();
try {
return "=?UTF-8?B?" + new String(base64Encoder.encode(fileName.getBytes("UTF-8"))) + "?=";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

执行结果:

1
2
3
4
5
6
#使用getWriter()
1.字符流(方式):当前您下载的文件是 WeiyiGeek.Properties
2.getBytes()默认码表:GBK

#使用getOutPutStream() 注意不能和字符流同时存在
3.字节流(方式):当前您下载的文件路径是 :D:\apache-tomcat-7.0.100\wtpwebapps\HelloWorld\Config\WeiyiGeek.Properties

WeiyiGeek.执行结果

WeiyiGeek.执行结果

注意事项:

  • 针对于浏览器类型对下载的文件名称做编码处理,Fire采用Base64编码而IE和Google采用URLEncoding编码

0x06 本文总结

ServletContext
介绍:什么是ServletContext?

  • 答:服务器在启动的时候给每一个应用程序都创建一个ServletContext,并且有且只有一个;

作用:有什么用?

  • 答:获取全局参数 / 获取工程下的资源 / 存取数据和共享数据

例子:怎么用?

1
2
3
4
5
6
7
8
9
10
11
//获取全局参数
getServletContext().getInitParams();

//获取工程下的资源
getServletContext().getRealPath();
getServletContext().getResourceAsStream();
this.getClass().getClassLoader();

//存取的数据(在同一工程下)
getServletContext.setAttribute()
getServletContext.getAttribute()

实际问题:为什么使用它会产生这个样的样子,其中有其他的参数;


ServletConfig
介绍:什么是ServletConfig?

  • 答:是在项目启动部署的时候我们在web.xml中进行对Servlet的配置当我们需要获取Servlet配置信息的时候就需要它;

作用:有什么用?

  • 答:获取在Servlet配置web.xml文件中参数;

例子:怎么用?

1
2
//获取Web.xml中的Servlet配置信息
getServletConfig().getInitParams();


HttpServletResquest
介绍:

  • 答:一个请求对象用于封装客户端提交过来的信息;

作用:

  • 答:获取头 / 获取客户端参数 / 获取提交过来的数据;

例子:

1
request.getParameter("key");

存在问题: GET/POST请求乱码(需要设置编码)


HttpServletResquest
介绍:

  • 答:这是一个响应对象,是服务器要给客户端返回的数据,都靠这个对象来完成;

作用:

  • 答:返回不同格式的内容 / 页面状态设置和跳转

例子:

1
2
3
4
5
6
7
//返回不同格式(两种方式)
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.setContentType("text/html; charset=UTF-8");

//状态码设置
response.setStatus(302);
response.setHeader("Location","")