web工程的读书笔记

Xml &Tomcat

Xml

eXtendsible markup language 可扩展的标记语言

XML有什么用?

  1. 可以用来保存数据;

  2. 可以用来做配置文件;

  3. 数据传输载体;

(xml是一个倒置的树形结构)

定义xml

其实就是一个文件,文件后缀为.xml

. 文档声明

简单声明 version:解析这个xml的时候,使用什么版本的解析器解析

<?xml version="1.0"?>

encoding: 解析xml中的文字的时候,使用什么编码来翻译

<?xml version="1.0" encoding="gbk"?>

standalone:no–表示该文档关联其他文件;yes–表示这是一个独立的文档

<?xml version="1.0" encoding="gbk" standalone="no"?>

encoding详解

在解析xml的时候,使用什么编码去解析 ----解码(默认计算机编码:gbk)

计算机存储的不是文字,而是存储这些文字所对应的二进制,文字所对应的二进制到底是什么?根据文件使用的编码来得到

默认文件保存的时候,使用的是GBK编码保存

所以要想让我们的xml能够正常的现实中中文,有两种解决办法:

  1. 让encoding也是gbk或者gb2312;

  2. 如果encoding是utf-8,那么保存文件的时候也必须使用 utf-8;

  3. 保存文件的时候见到的 ANSI 对应的其实是我们的本地编码 GBK。

(为了通用,建议使用utf-8 编码保存,以及encoding都是utf-8。)

元素的定义(标签)

  1. 其实就是里面的标签, <> 括起来的都叫做标签,成对出现,如下:<stu> </stu>

  2. 文档声明下来的第一个元素叫做根元素(根标签)

  3. 标签里面可以嵌套标签
    <stu> <name>张三</name> <age/> </stu>

  4. 空标签(既是开始,又是结束)例如:
    <age/>

  5. 标签可以自己定义(html中标签不能自己定义),所以xml是可扩展标记语言

  6. 标签的命名规则(命名尽量简单,做到见名知意):

    名字可以含有字母、数字以及其他的字符;

    名字不能以数字或者标点符号开始;

    名字不能以字符“xml"(或者XML、Xml)开始;

    名字不能包含空格

简单标签 & 复杂标签

  • 简单标签

元素里面包含了普通的文字

  • 复杂标签

元素里面还可以嵌套其他的元素

属性的定义

定义在元素里面<元素名称 属性名称=“属性的值”></元素名称> 如下:

<?xml version="1.0" encoding="UTF-8"?>
<stus>
	<stu1 id="10086">
		<name>张三</name>
		<age>18</age>
	</stu1>
	<stu2>
		<id>10087</id>
		<name>李四</name>
		<age>19</age>
	</stu2>
</stus>

xml注释

与html注释一样,如下:(快捷键Ctrl+shift+/)

<!-- 这里有两个学生:张三 李四 -->

注释不能再文档的第一行,必须在文档声明的下面

CDATA区

(一般很少出现,配置文件中就不会出现;出现在服务器向客户端传输数据)

  • 非法字符

严格的讲, 在xml中仅有字符"<“和”&"是非法的,省略号、引号、大于号是合法的,但是把它们替换成实体引用是好的习惯。

<   &lt;
&   &amp;
>   &gt;
  • 如果某段字符串里面有过多的字符,并且里面包含了类似标签或者关键字的这种文字,不想让xml解析器去解析,那么可以使用CDATA来包装,不过这个CDATA一般比较少看到,通常在服务器给客户端返回数据的时候看到
    <des><![CDATA[<a href="http://www.baidu.com">黑马训练营</a>]]></des>

xml解析

其实就是获取元素里面的字符数据或者属性元素

xml的解析方式(面试常问)

有很多种,但是常用的有两种:

  • DOM: (document object model)把整个xml全部读到内存当中,形成树形结构,整个文档(即整个树结构)称之为document对象, 属性(如:id)对应Attribute对象,所有的元素节点(如:name age)对应Element对象,文本(如:张三)也可以称之为Text对象。以上所有对象都可以称之为Node节点;

缺点:如果xml特别大,那么就会造成内存溢出.

优点:可以对文档进行增删操作。

  • SAX:(Simple API for xml) 基于事件驱动,读一行,解析一行。

优点: 不会造成内存溢出。

缺点:不可以进行增删,只能查询。

###针对这两种解析方式的API

一些组织或者公司,针对以上两种解析方式,给出的解决方案有哪些?

jaxp  (sun公司做的,比较繁琐)
jdom
dom4j  (使用比较广泛)

Dom4j基本用法

  1. 创建SaxReader对象;

  2. 指定解析的xml文件;

  3. 获取根元素;

  4. 根据根元素获取子元素(或者再根据子元素获取子孙元素)。

     try {
     	//1.创建SAX读取对象,还有DOMReader()
     	SAXReader reader = new SAXReader();
     	//2.指定解析的xml源:reader.read(path|file+inputtStream)
     	Document document = reader.read(new File("src/xml/demo.xml"));
     	//3.得到元素
     	  //3.1得到根元素
     	Element rootelement=document.getRootElement();
     	  //3.2得到根元素下面的子元素,元素名称stu1
     	rootelement.element("stu1");
     	  //3.3 得到根元素下面的子孙元素
     	//System.out.print(rootelement.element("stu1").element("age").getText());
     	  //3.4得到根元素下面的所有元素
     	List<Element> elements=rootelement.elements();
     	for(Element element:elements) {
     		String name =element.element("name").getText();
     		String age=element.element("age").getText();
     		String id=element.element("id").getText();
     		System.out.print("name"+name+";age"+age+";id"+id);
     	}
     	
     } catch (Exception e) {
     	e.printStackTrace();
     }
    

Dom4j的Xpath使用

dom4j里面支持Xpath的写法,Xpath其实是xml的路径语言;

支持我们在解析xml的时候,能够快速的定位到具体的一个元素

  1. 添加jar包依赖;

    jaxen-1.1-beta-6 jar

  2. 在查找指定的节点的时候,根据Xpath语法规则来查找;

  3. 后续的代码和之前的解析代码一样。

     try {
     	//1.创建SAX读取对象
     	SAXReader reader = new SAXReader();
     	//2.指定解析的xml源
     	Document document = reader.read(new File("src/xml/demo.xml"));
    
     	//3.得到元素
     	  //3.1得到根元素
     	Element rootelement=document.getRootElement();
     	// 要想使用Xpath,还得添加支持的jar
     	//获取指定元素
     	Element element=(Element) rootelement.selectSingleNode("//name");
         
     	System.out.println(element.getText());
    
     	//获取所有name元素
         //获取第一个name节点(这里用Element这种类型的对象调用selectNodes()方法,但是定义这个selectNodes的方法是定义在一个接口Node中的,请问这么写有没有错? -----没错:element、attribute、text都是node的子接口,体现了Java的多态)
     	List<Element> list=rootelement.selectNodes("//name");
     	for (Element element2 : list) {
     		System.out.println(element2.getText());
     	}
     	
     } catch (Exception e) {
     	e.printStackTrace();
     }
    

xml约束(了解)

如下的xml文档,属性的ID是一样的,这在生活中是不可能出现的;并且第二个学会说呢过的姓名出现了好几个,这种情况一般也很少。那么如何规定ID的值唯一,或者元素只出现一次,不能出现多次?甚至规定里面只能具体出现的元素的名字

<?xml version="1.0" encoding="UTF-8"?>
<stus>
	<stu id="10086">
		<name>李武</name>
		<age>67</age>
	</stu>
	<stu id="10086">
		<name>李六</name>
		<name>李七</name>
		<name>李八</name>
		<age>67</age>
	</stu>
</stus>

文档约束有两大类:DTD,Schema(面试:了解区别,优缺点)
小结(下面):

  1. DTD:语法自成一派,早期就出现,可读性差
  2. schema:使用xml的语法规则,xml解析器解析起来比较方便它的出现是为了替代DTD,但是schema约束的文本内容比DTD的内容还要多,所以目前也没有真正意义上的替代DTD
  3. 一个xml可以引用多个schema约束,但只能引用一个dtd约束。

DTD

**语法自成一派,早期就出现,可读性差**
  1. 引入网络上的DTD

    <?xml version="1.0" encoding="UTF-8"?>

    引入dtd来约束这个xml:<!文档类型 根标签的名字 网络上的dtd dtd的名称 dtd的路径>

  2. 引入本地的DTD

  3. 直接在xml里面嵌入dtd的约束规则 :

 <!DOCTYPE stus [
    <!ELEMENT stus(stu)>
    <!ELEMENT stu(name,age)>
    <!ELEMENT name(#PCDATA)>
    <!ELEMENT age(#PCDATA)>
    ]>

    <stus>
    	<stu>
    		<name>李武</name>
    		<age>67</age>
    	</stu>
    </stus>

dtd文档中的内容:

<!ELEMENT stus (stu) +>: 如果没有+号(stus下面有一个元素stu,但是只有一个)
<!ELEMENT stu (name, age)>: stu下面有两个元素name,age,顺序必须是name age
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ATTLIST stu id ID #IMPLIED>: stu有一个属性id 属性类型 属性的默认值(IMPLIED 表示属性可有可无)

元素个数:(类似于正则表达式)

+ 表示一个或多个
* 表示零个或多个
?表示零个或一个

属性类型的定义:

CDATA: 属性是普通的文字
ID:属性的值必须唯一

元素的顺序:

<!ELEMENT stu (name , age)>: 按照name age的顺序来
<!ELEMENT stu (name | age)>: 两个中只能包含一个子元素

schema

schema其实是一个xml,使用xml的语法规则,xml解析器解析起来比较方便它的出现是为了替代DTD,但是schema约束的文本内容比DTD的内容还要多,所以目前也没有真正意义上的替代DTD

约束文档:

<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns(xml namespace):表示名称空间/命名空间
targetNamespace: 目标名称空间下面定义的那些元素都与这个名称空间绑定上
elementFormDefault:元素的格式化情况-->

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://www.example.org/teacher" 
elementFormDefault="qualified">

<!-- teachers和teacher是复杂元素,
需要标签complexType:表示这个元素是复杂元素;
需要标签sequence:表示下面的元素是有序的 -->
<element name="teachers">
	<complexType>
		<sequence maxOccurs="unbounded">
			<element name="teacher">
				<complexType>
					<sequence>
						<!-- name和age是简单元素 -->
						<element name="name" type="string"></element>
						<element name="age" type="int"></element>
					</sequence>
				</complexType>
			</element>
		</sequence>
	</complexType>
</element>
</schema>

实例文档:

<?xml version="1.0" encoding="UTF-8"?>

<!-- xnlns:xsi:这里必须是这样的写法,也就是这个值已经是固定的了
xmlns:这里是名称空间,也是固定了, 写的是schema里面的顶部项目名称空间
xsi:schemaLocation:有两段,前半段是名称空间,也就是目标空间的值,后面是约束文档的路径 -->

<teachers
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.example.org/teacher"
	xsi:schemaLocation="http://www.example.org/teacher teacher.xsd"
>
	<teacher>
		<name>历史</name>
		<age>26</age>
	</teacher>
	<teacher>
		<name>物理</name>
		<age>22</age>
	</teacher>
</teachers>

名称空间的作用

一个xml如果指定它的约束规则,假设使用的是dtd,则这个xml只能使用一个dtd,不能指定多个dtd; 但是如果一个xml的约束是定义在schema里面,并且是多个schema,那么是可以的;(简言之:一个xml可以引用多个schema约束,但只能引用一个dtd约束)。

名称空间的作用是:在写元素的时候,可以指定该元素使用的是那一套约束规则。默认情况下,如果只有一套规则,那么都可以这么写

xmlns:aa="http://www.baidu.com/teacher"
xmlns:bb="http://www.baidu.com/teacher"

<teacher>
	<aa:name>历史</aa:name>
	<age>26</age>
</teacher>
<teacher>
	<bb:name>物理</bb:name>
	<age>22</age>
</teacher>

程序架构

  • C/S(client/server)

例如 QQ 微信 LOL就是使用 C/S架构
优点:

有一部分代码写在客户端,用户体验比较好

缺点:

服务器更新,客户端也要跟着更新,占用资源大(比较吃硬盘)
  • B/S(browser/server)目前很多程序朝着B/S的方向发展

例如 网页游戏,网页QQ… 是使用B/S架构
优点:

客户端只要有浏览器就可以了,占用资源少,不用更新

缺点:

用户体验不佳

服务器

实质上服务器就是一台配置比较高的电脑

###Web服务器软件

客户端在浏览器的地址栏上输入地址,然后web服务器软件,接收请求,然后响应消息。

接收并处理客户端的请求,返回资源或信息

web应用 需要服务器支撑 index.html

比较常见的web服务器软件有:

Tomcat apache(免费)
weblogic BEA
WebSphere IBM
IIS 微软

Tomcat安装

  1. 直接解压,然后找到 bin/startup.bat(双击) [startup.sh是在Linux操作系统中使用的];

  2. 可以安装:

启动之后,如果能够正常看到黑窗口,表明安装成功;为了确保万无一失,最好在浏览器的地址栏上输入:http://localhost:8080,如果有看到内容,就表明成功了;

  1. 如果双击了startup.bat 看到一闪而过的情形,一般都是JDK的环境变量没有配置成功

Tomcat目录介绍

bin

包含了一些jar,bat文件(常用startup.bat)

conf

tomcat 的配置 (常用的 server.xml / web.xml /)

lib

Tomcat 运行所需的jar文件

logs

运行的日志文件

temp

临时文件

webapps

发布在Tomcat服务器上的项目,就存放在这个目录(想让另外一台访问哪个项目,就把该项目放到这个文件夹下面)

work(目前不用管)

jsp 翻译成class文件存放地

如何把一个项目发布到Tomcat中

需求:如何能让其他电脑访问我这台电脑上的资源(例如:stus.xml)。

有三种方法解决这个需求:

1. 拷贝这个文件stus.xml到webapps/ROOT文件夹下面,客户端在浏览器中访问:
http://localhost:8080/stus.xml

或者在webapps下面新建一个文件夹xml,然后把stu.xml拷贝到这个新建的文件夹中(先关闭Tomcat,注意浏览器把webapps下的每个文件夹当做是一个项目,所以需要新建文件夹),客户端在浏览器中访问:

http://localhost:8080/xml/stus.xml

注意:这里

http://localhost:8080:其实对应的是到webapps/ROOT下面访问
http://localhost:8080/xml:其实对应的是到webapps/xml下面访问
localhost:本机地址
8080:对应的是Tomcat服务器在这台电脑对应的端口(默认)

或者使用IP地址访问:

http://192.168.37.48:8080/xml/stus.xml
2. 配置虚拟路径

使用localhost:8080打开tomcat首页,在找到tomcat的documentation入口,点击进去后,点击configuration,然后在左侧接着找到Context入口,点击进去。找到Defining a context定义一个虚拟的路径(Inside a Host element in the main conf/server.xml).

http://localhost:8080/docs/config/context.html
  1. 在conf/server.xml 中找到 Host元素节点;

  2. 加入以下内容:

         <!-- docBase:项目的路径地址,如:C:\Java\workspace\XmlDemo\src\xml
         path :对应的虚拟路径,一定要以 / 开头;
         	对应的访问方式为:http:localhost:8080/teacher.xml-->
     	<Context docBase="C:\Java\workspace\XmlDemo\src\xml" path="/a"></Context>
    
  3. 在浏览器地址栏上输入:

    http://localhost:8080/a/teacher.xml

3. 配置虚拟路径
  1. 在Tomcat/conf/catalina/localhost/文件夹下新建一个xml文件,名字可以自己定义person.xml。

  2. 在这个文件里面写入一下内容:

    <?xml version="1.0" encoding="utf-8"?>

  3. 在浏览器上面访问

http://localhost:8080/teacher/teacher.xml

给Eclipse 配置Tomcat

  1. 在server里面 右键新建一个服务器,选择到apache分类,找到对应的Tomcat版本,接着一步步配置即可;

  2. 配置完成后,在server里面右键刚才的服务器,然后open,找到上面的 Server Location,选择中间的 Use Tomcat installation;

  3. 新建一个动态的web工程(other下),在webContent下定义html文件,右键web工程,run as server

总结

xml:
会定义xml;
会解析xml(dom4j解析);

Tomcat:
会安装,会启动,会访问;
会设置虚拟路径;
会给eclipse配置Tomcat。

HTTP协议 & Servlet

HTTP协议

  • 什么是协议

双方在交互、通信的时候,遵守的一种规范、规则;

  • HTTP协议

针对网络上的客户端 与 服务器在执行http请求的时候,遵守的一种规范:
其实就是规定了客户端在访问服务器的时候,需要带上那些东西;服务器返回数据的时候,也要带上什么东西

  • http协议的版本:

1.0 (请求数据,服务器返回后,将会断开连接)

1.1(请求数据,服务器返回后,连接还会保持,除非服务器或客户端关掉。有一定的时间限制,如果都空着这个连接,那么后面会自己断掉)

演示客户端 如何 与服务器通讯

在浏览器地址栏中键入网络地址,回车;或者是平常注册的时候,点击了注册按钮,浏览器都会现实出来一些东西。那么底层的浏览器和服务器是怎么通讯的,它们都传递了那些数据?怎么看呢?如下步骤:

  1. 安装抓包工具 HttpWatch(IE插件)
  2. 打开Tomcat。输入 localhost:8080 打开首页
  3. 在首页上找到examples字样,然后找到 servlets example,点击;
  4. 点击Request Parameters 的 Execute ;到达页面,键入first name和 last name;
  5. 在点击提交之前,先打开HttpWatch抓包工具(位于IE浏览器右上角的小箭头),点击record;再点击提交;然后选中抓包页面的有颜色的进度条,然后点击stream,就可以看到浏览器(左边)和服务器(右边)的交互。

HTTP请求(浏览器向服务器请求)数据解释

请求的数据里面包括三个部分内容:请求行、请求头、请求体

  • 请求行:(第一行)
    POST/examples/servlets/servlet/RequestParamExample HTTP/1.1

POST: 请求方式(以post去提交数据);get也是请求方式;另外还有很多其他的请求方式,如OPTIONS/Delete/trace/put请求方式

/examples/servlets/servlet/RequestParamExample:请求的地址路径,就是要访问哪个地方

HTTP/1.1: 协议版本

  • 请求头:(出来第一行,一直到空行之前,都是请求头)
Accept: application/x-ms-application, image/jpeg,application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: Http://localhost:8080/examples/servlets/servlet/RequestParamExample
Accept-Language:zh-CN
User-Agent:Mozilla/4.0(compatible; MSIE 8.0;Windoms NT 6.1; Wow64; Trident/4.0;SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 31
Connection: Keep-Alive
Cache-control: no-cache

Accept: 客户端向服务器端表示:“我能支持什么类型的数据”。

Referer: 真正请求的地址路径,全路径.

Accept-Language:支持语言格式

User-Agent:向服务器表明,当前来访的客户端信息(是PC端,还是Android/IOS端访问)

Content-Type:提交的数据类型,经过urlencoding编码的form表单数据

Accept-Encoding: gzip, deflate:压缩算法

Host: 主机地址

Content-Length: 内容、数据长度

Connection: 保持连接

Cache-control:对缓存的操作(no-cache:每一次请求这个地址路径,不从缓存中拿数据,而是直接越过缓存,向服务器请求新的数据访问)

  • 请求体:(最后一行)

浏览器真正发送给服务器的数据,发送的数据呈现的是key=value的形式; 如果存在多个数据,那么使用 &;

firstname=zhang&lastname=sansan

HTTP响应(服务器响应浏览器)数据解释

响应的数据里面包含三个部分的内容:响应行,响应头,响应体

HTTP/1.1 200 OK
Server: Apache-Copyto/1.1
Content-Type: text/html;charset=ISO-8859-1
ContenT-LENGTH:673
Date: Fri, 17 Feb 2017 02:53:02 GMT
... ...(这里还有很多数据)
  • 响应行:(第一行)

    HTTP/1.1 200 OK

HTTP/1.1: 协议版本

200: 代表状态码(状态码是指:咱们这次交互到底是什么结果的一个code)
200:表示成功,正常出来,得到数据;
3打头的代表重定向;
403: Forbiddern 拒绝;404: NOT Found; (4打头的代表客户端有问题)
500:(5打头的代表服务器端有问题)

OK: 对应前面的状态码

  • 响应头:

Server: 服务器的类型(tomcat)

Content-Type: 服务器返回给客户端的内容类型

Content-Length: 返回的数据长度

Date: 通讯的日期,响应的时间

post 和 get请求的区别

  1. 请求路径不同:

post请求在URL后面不跟任何的数据;get请求在URL后面跟上数据;

  1. 数据传输方式不同:

带上的数据不同,post请求会使用流的方式写数据;get请求是在地址栏上跟数据(即请求体不同);

  1. 由于post请求使用流的方式写数据,所以一定需要一个Content.Length 的头来说明数据的长度

!小结:(面试会问)

  • post:

-1. 数据以流的方式写过去,不会再地址栏上面显示。现在一般客户端/浏览器 向 服务器 提交数据的都使用post;

-2. 以流的方式写数据,所以数据没有大小限制

  • get:

-1. 会在地址栏后面拼接数据,所以有安全隐患。现在一般从服务器获取数据,并且客户端也不要提交上面数据的时候,可以使用get。

-2. 能够带的数据有限(1kb大小)

web资源

在我们的应用体系(或者http协议)中,规定了请求和响应的双方:客户端/浏览器 和 服务器端。与web相关的资源。

(服务器可以返回的一切东西都可以看做是web资源)

有两种分类:

  • 静态资源:(即使请求访问的时间点不同,产生这份数据的源代码是不变的)
    如: html,js, css

  • 动态资源

如:servlet/jsp [jsp:比如说注册 或者 登录 时输入数据]

注意!!!!(经验总结)

  • 小结1:
  1. 运行web工程前,需要先关掉tomcat server(即叉掉startup.bat窗口);
  2. 如果电脑上装载了两个tomcat,并且都运行过;那么可能会出现startup.bat窗口闪退的情况。这是因为tomcat端口被占用,解决这个问题需要:

打开命令窗口:win+R,然后键入cmd->确定

在窗口输入:C: (敲回车键)

再输入: cd\windows\system32 (敲回车键)

再运行命令: 输入 netstat -ano | findstr 8080 查看占用8080端口的进程;(这里可以看到占用端口的进程PID(进程标识符)为6668)

再运行命令: 输入 tasklist|findstr 6668 查看进程6668的运行程序(这里查到进程的名称为 javaw.exe)

再运行命令: 输入 taskkill /f /t /im javaw.exe 杀死这个进程就可以了。

  • 小结2:myeclipse开启自动提示功能的方法:
  1. 打开MyEclipse,然后单击“window”→“Preferences”。

  2. 单击“java”,展开“Editor”,单击“ContentAssist”。

  3. 找到右下的“Auto activation triggers for Java”这个选项,该选项用来设置触发代码提示的符号。

  4. 在“.”后加上abc字母,然后单击“apply”→“OK”。

  5. 单击“File”→“Export”,在弹出的窗口中选择“Perferences”,点击“下一步”。

  6. 选择导出文件路径,并为文件命名,点击“保存”,例如保存在D盘,名字为java.epf

  7. 用记事本打开java.epf文件按“ctrl+F”快捷键,输入“.abc”,点击“查找下一个”,查找到“.abc”的配置信息。

  8. 把“.abc”改成“.abcdefghijklmnopqrstuvwxyz@(,”,保存并关闭java.epf。

  9. 回到MyEclipse界面,单击“File”→“Import”,在弹出的窗口中选择“Perferences”,点击“下一步”,选择刚已经修改的java.epf文件,点击“打开”,点击“Finish”。

Servlet

  • servlet 是什么?

其实就是一个Java程序运行在我们的web服务器上,用于接收和响应客户端的http请求。

更多的是配合动态资源来做。当然静态资源也需要使用到servlet,,只不过tomcat里面已经定义好了一个DafaultServlet。(Tomcat其实就是一个容器,里面放置servlet。

Hello Servlet

  1. 得写一个web工程,要有一个服务器;
  2. 测试运行web工程。
  1. 新建一个类,实现一个Servlet接口;如下:
public class helloServlet implements Servlet{

	public void destroy() {
		
	}

	public ServletConfig getServletConfig() {
		return null;
	}

	public String getServletInfo() {
		return null;
	}

	public void init(ServletConfig arg0) throws ServletException {
		
	}

	public void service(ServletRequest arg0, ServletResponse arg1)
			throws ServletException, IOException {
		System.out.print("HelloServlet");
		
	}

}
  1. 配置Servlet. 用意就是:告诉服务器,我们的应用有这么一个servlet.(在webcontent/WEB-INF/web.xml 里面写上一下内容:)
 <!-- 向tomcat报告:我这个应用里面有一个servlet,名字叫做helloServlet, 具体的路径com.itheima.servlet.helloServlet -->

  <servlet>
  	<servlet-name>helloServlet</servlet-name>
  	<servlet-class>com.itheima.servlet.helloServlet</servlet-class>
  </servlet>

  <!-- 注册servlet的映射,servlet-name:找到上面注册的具体servlet;url-pattern:在地址栏上的path,一定要以 / 开头-->

  <servlet-mapping>
  	<servlet-name>helloServlet</servlet-name>
  	<url-pattern>/a</url-pattern>
  </servlet-mapping>

Servlet的执行过程

URL:http://localhost:8080/Helloweb/a

  1. 找到tomcat应用;
  2. 找到项目Helloweb(其实就是将web项目放入tomcat应用的wtpwebapps文件夹中);
  3. 找到web.xml配置文件,然后在里面找到 url-pattern, 看看里面有没有 pattern 的内容是 /a 的;
  4. 在配置文件中,找到servlet-mapping中与pattern对应的那个servlet-name[helloServlet];
  5. 在配置文件中,找到servlet元素中的 与上面找到的servlet-name相同的 servlet-name[helloServlet];
  6. 然后根据 对应的 servlet-class (路径)找到类helloServlet, 并开始创建该类的实例;
  7. 继而执行该servlet实例中的service()方法;

Servlet的通用写法:

因为在上面创建Servlet类是实现Servlet接口,必须重写改接口的所有方法,很不方便;所以考虑实现了该接口的类:GenericServlet 和 HttpServlet,创建Servlet类的时候可以继承这两个类,不需要重写所有方法。

Servlet(接口)
|
|
GenericServlet(Servlet的子类,是一个抽象类,实现了Servlet接口/ServletConfig/java.io.Serializable)
|
|
HttpServlet(抽象类,继承GenericServlets, 实现 java.io.Serializable; 用于处理处理http的请求的类)
  1. 定义一个类,继承HttpServlet 复写 doGet 和 doPost 方法;如下:(为什么会执行dopost和doget,而不是service();因为service() 方法的内部,对来访的请求,首先是对 get还是post进行判断,然后分别调用doGet和doPost)
public class helloServlet02 extends HttpServlet{
	//get请求就会到这来
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		super.doGet(req, resp);
		System.out.print("get请求");
	}
	//post请求就会到这来
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		super.doPost(req, resp);
		System.out.print("post请求");
	}
}

servlet的生命周期

  • 生命周期:指从创建到销毁的一段时间

从创建(初始化)、接受和响应消息、到销毁,一定会执行的那些方法,就是servlet的生命周期方法;

  • servlet的生命周期的方法:
  1. init():
/* 1. 在**创建该Servlet的实例的时候,就执行该方法**(类似于构造方法);
	 * 一个servlet只会初始化一次,即init()方法只会执行一次;
	 * 默认情况下是:初次访问该servlet,才会创建实例
	 */
	public void init(ServletConfig arg0) throws ServletException {
		System.out.println("helloServlet03 初始化");
	}
  1. service():
/* 2. 只有客户端来了一个请求,就会执行这个方法service();
 * 	  该方法可以调用很多次,一次请求,对应一次service()方法的调用;
 */
public void service(ServletRequest arg0, ServletResponse arg1)
		throws ServletException, IOException {
	System.out.println("helloServlet03 service()方法调用了");
	
}
  1. destroy():
/* 3. 销毁servlet的时候,就会调用该方法destroy();
	 *   什么时候调用该方法? 如下情形:
	 * 	 (1)该项目从tomcat服务器的里面移除的时候;
	 *   (2)正常关闭服务器(点击shutdown.bat); 注意,使用console界面的红色按钮停止tomcat属于非正常手段关闭;
	 */
	public void destroy() {
		System.out.println("helloServlet03 destroy()方法调用了");
		
	}

doGet 和 doPost 不算是生命周期的方法(它们不一定会执行)

让servlet创建实例的时机提前

为什么需要提前呢?(以下几个原因)

  1. 默认情况下,只有在初次访问servlet的时候,才会执行init方法;有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至做一些比较耗时的逻辑;

  2. 那么初次访问,可能会在init方法中逗留太久的时间,那么有没有方法可以让这个初始化的时机提前一些;

  3. 在配置的时候,使用 load-on-startup 元素来指定让servlet提前创建实例(即提前初始化);给定的数字越小,启动的时机就越早;一般 不写负数,从2开始即可。

  <servlet>
  	<servlet-name>helloServlet04</servlet-name>
  	<servlet-class>com.itheima.servlet.helloServlet04</servlet-class>
	<load-on-startup>2</load-on-startup>
  </servlet>

ServletConfig(使用频率不高)

servlet的配置,通过这个ServletConfig对象,可以获取servlet在配置的时候一些信息

(先说,再说怎么做;最后再写有什么用;)
(新建一个动态web项目,演示servletConfig)

public class helloServletConfig extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//1. 得到servlet配置对象;
		ServletConfig config = getServletConfig();
		//2. 获取到的是配置servlet里面servlet-name的文本内容;
		String servletName=config.getServletName();
		System.out.println("servletName---"+servletName);
		//3. 可以获取具体的某一个参数;
		String adress=config.getInitParameter("adress");
		System.out.println("adress---"+adress);
		//4. 可以获取所有的参数
		Enumeration<String> initParameterNames = config.getInitParameterNames();
		while (initParameterNames.hasMoreElements()) {
			String elem = (String) initParameterNames.nextElement();
			String key=config.getInitParameter(elem);
			System.out.println("elem="+elem+";key="+key);
			
		}
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doGet(req, resp);
	}

}

(将上面的servletconfig web项目导出为jar文件,即右击项目,点击 export-export–java/JAR file–选择保存路径保存–finish)

为什么需要有这个servletConfig?

  1. 未来我们自己开发的一些应用,使用到一些技术,或者一些代码,我们不会,但是有人写出来了,它的代码放置在了自己的servlet类里面;
  2. 刚好这个servlet里面需要一个数字或者叫做变量值。但是这个值不能是固定的。所以要求使用到这个servlet的公司,在注册servlet的时候,必须要在web.xml里面,声明init-params
  1. 请在你的web工程里面,添加jar(具体步骤是:复制上面路径下的jar文件;粘贴到 新建的web项目下的webContent/WEB-INF/lib下);

  2. 在web.xml里面,注册servlet(注意这里servlet-class:是jar包中class文件的路径)

  3. 请添加如下参数

     <init-param>
     	<param-name>number</param-name>
     	<param-value>44</param-value>
     </init-param>
    

总结:

  • http协议:

1.使用httpWatch 抓包看看http请求背后的细节

2.基本了解 请求和响应的数据内容: (请求行 请求头 请求体;响应行 响应头 响应体;)

3.get和post的区别

  • Servlet:

1.会使用简单的servlet:

写一个类,实现接口servlet(或者继承httpservlet)

配置servlet(在web.xml文件中)

会访问servlet

2.servlet的生命周期

— init(): 只运行一次,默认初次访问就会调用;或者通过配置,让它提前 load-on-startup;

— service(): 多次执行,一次请求对应一次service();

— destroy(): 执行一次:销毁的时候、从服务器移除 或者 正常关闭服务器;

3.ServletConfig(对象,类)

获取配置的信息,参数等;

HttpServletRequest 和 HttpServletResponse

Servlet 配置方式

  1. 全路径配置

以 / 开头,例如 /a 或者 /a/bb;
localhost:8080/项目名称/a

  1. 路径匹配,前半段匹配

以 / 开头,但是以 * 结束;例如 /a/* 或者 /*

(*号:其实是通配符;表示该位置及后面位置可以匹配任意文字)

localhost:8080/项目名称/a/bbb

  1. 以扩展名匹配,后半段匹配

写法:(没有/,以*开头。) .扩展名;例如:.aa

ServletContext(使用频率较高)

servlet上下文

虚拟机中的每个web工程都只有一个servletContext对象,说白了就是不管在哪个servlet里面,获取这个servletContext类的对象都是同一个。

servletContext如何得到对象:

	//1. 获取servletContext对象
	ServletContext context = getServletContext();

servletContext有什么用

  1. 可以获取全局配置参数;
  2. 可以获取web工程中的资源;
  3. 存取数据,servlet间共享数据(域对象);
    (具体如下)
1. 可以获取全局配置参数;

web.xml中配置全局参数:

<!-- context-param是全局参数,哪个servlet都可以拿,通过servletContext拿全局参数 -->
<context-param>
<param-name>adress</param-name>
<param-value>陕西西安---</param-value>

servletContext对象获取全局参数:

public class ServletContext02 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//1. 获取servletContext对象
		ServletContext context = getServletContext();
		String adress=context.getInitParameter("adress");
		System.out.println("这是02发布的数据--adress="+adress);
	}  	
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    doGet(request, response);
}   	  	    
2. 可以获取web应用中的资源:

方式1:获取资源在tomcat里面的绝对路径: getRealPath()方法;(利用servletContext先获取绝对路径,然后自己new InputStream()

首先,在下面的类中使用FileInputStream()在web项目中是不OK,如下:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//1. 创建属性对象
	Properties properties=new Properties();
	/*2. 指定载入的数据源
	 * 此处,如果想获取web工程下的资源,用普通的FileInputStream写法是不OK的;
	 * 因为路径不对了。FileInputStream的相对路径,其实是根据jre来确定的。
	 * 但是我们这是一个web工程,jre后面会由tomcat管理;
	 * 所以这里真正的相对路径应该是tomcat里面的bin目录;
	 * 因此,需要将config.properties文件复制到tomcat的斌目录下(可以新建一个classes文件夹
	 * ,把文件粘贴进去;并将下面改为new FileInputStream("classes/config.properties");)
	 */
	InputStream is= new FileInputStream("src/config.properties");
	properties.load(is);
	//3.获取name属性的值
	String name = properties.getProperty("name");
	System.out.println("name="+name);
	
}  	

修改上面的代码,添加如下servletContext对象获取绝对路径:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	//获取servletContext对象
	ServletContext context= getServletContext();
	//获取给定的文件在服务器上面的绝对路径
	String path = context.getRealPath("file/config.properties");
	
	//1. 创建属性对象
	Properties properties=new Properties();
	//2. 指定载入的数据源
	InputStream is= new FileInputStream(path);
	properties.load(is);
	//3.获取name属性的值
	String name = properties.getProperty("name");
	System.out.println("name="+name);

}

注意:这里String path = context.getRealPath(“”),得到的是项目在tomcat里面的根目录;

方式2: 获取资源,并转化为流对象,getResourceAsStream();如下:(根据相对路径直接获取流对象

		//1.获取servletContext对象
		ServletContext context = getServletContext();
		//2. 创建属性对象
		Properties properties = new Properties();
		//3. 获取web工程下的资源,转化成流对象,前面隐藏当前工程的根目录
		
		InputStream is= context.getResourceAsStream("file/config.properties");
		properties.load(is);
		//4.获取name属性的值
		String name = properties.getProperty("name");
		System.out.println("name2=" + name);
		is.close();
	} catch (Exception e) {
		e.printStackTrace();
	}

方式3:(不是利用servletContext去获取web资源的方式)通过classLoad去获取web工程下的资源。 如下:this.getClass().getClassLoader(). getResource AsStream("…/…/file/config.properties")

/**
 * 利用classLoad获取web资源
 */
private void test3() {
	try {

		/*
		 * 注意:
		 * 1. 对servletContext对象而言: 相对路径是---工程在tomcat里面的目录
		 *     即:C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03
		 *     
		 * 2. 对classLoad而言: 相对路径是---工程中类所在目录
		 *     即:C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03\WEB-INF\classes
		 *     
		 * 3. 默认的classLoad 的路径是上面的这个路径,我们必须得回到ServletContext03这个目录下面,才能进入file目录。如何返回上一级目录呢?
		 * 利用 ../ 可以返回到上一级; ../../可以返回到上两级;
		 * 
		 * 这里 ../../ -----返回到了 C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03
		 * 
		 * ../../file/config.properties----达到了C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03file/config.properties
		 */

        //.getClass():表示获取该Java文件的类文件;.getClassLoad():表示获取对应类文件的类加载器

		InputStream is = this.getClass().getClassLoader()
				.getResourceAsStream("../../file/config.properties");
		Properties properties = new Properties();
		properties.load(is);
		String name = properties.getProperty("name");
		System.out.println("name3=" + name);
	} catch (Exception e) {
		e.printStackTrace();
	}
	
}

小知识:

相对路径:有参照物,相对谁?这里相对的是工程在tomcat里面的根目录:(有以下两种形式)

a路径---工程在tomcat里面的目录;

     例如: C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03

b路径---file/config.properties

绝对路径:没有参照物

 C:\Users\RenJuan\Desktop\努力\tomcat服务器\apache-tomcat-6.0.39\wtpwebapps\ServletContext03\file\config.properties        
3. 使用servletContext存取数据(资源存取)

!注意:1 2 属于准备工作,并没有使用servletContext存取数据

1.定义一个登录的html页面,定义一个form表单:

<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="ServletContext04" method="get">
		<h2>请按照下面内容填写,并登录</h2>
	 账户:<input type="text" name="username"/><br/>
	 密码:<input type="password" name="password"/><br/>
	<input type="submit" value="提交">
	</form>
</body>
</html>

2.定义一个Servlet,如下:

public class ServletContext04 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
/* request: 包含请求的信息
 *  reponse: 响应数据给浏览器,就靠这个对象
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//1. 获取数据
		String username=request.getParameter("username");
		String password=request.getParameter("password");
		System.out.println("username="+username+";password="+password);
		PrintWriter writer = response.getWriter();
		//2. 校验数据
		if("zhangsan".equals(username)&&"123".equals(password)){
			//(1)仅向console输出内容: System.out.println("登录成功");
			//(2)向客户端输出内容
			//PrintWriter writer = response.getWriter();
			//writer.write("login success...");
	        //(3)
		        //<1>成功的次数累加
		    Object obj = getServletContext().getAttribute("count");
		        //默认就是0次
		    int totalCount=0;
		    if(obj!=null){
			    totalCount=(Integer) obj;
		    }
		    System.out.println("已经登录成功的次数是:"+totalCount);	
		    getServletContext().setAttribute("count", totalCount+1);
		        //<2>成功就跳转至success.html;
		       //首先,设置状态码(重新定位,设置状态码)
		response.setStatus(302);
		     //然后,定位跳转的位置是那个页面
		response.setHeader("Location", "success.html");
	}else{
		//System.out.println("登录失败");
		writer.write("login faied...");
	}
}  	
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	doGet(request, response);
}   	  	    

}

3.针对成功或者失败,进行判断,然后跳转到不一样的页面;(response.setStatus(302); response.setHeader(“Location”, “success.html”);)

(首先新建一个跳转页面success.html 如下; 然后再servlet中利用response.setStatus 和 respons.setHeader完成页面的跳转)

<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>登录成功了</h2>
	<a href="">获取网站登录成功的总数</a>
</body>
</html>
//(3)成功就跳转至success.html;
	//首先,设置状态码(重新定位,设置状态码)
response.setStatus(302);
	//然后,定位跳转的位置是那个页面
response.setHeader("Location", "success.html");

4.ServletContext获取登录成功总数(存取数据)getServletContext().getAttribute()

新建另一个CountServlet对象, 来获取上面servletContext04对象中得到的登录成功总数.

public class CountServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//1.取值
		int count = (Integer) getServletContext().getAttribute("count");
		//2.输出到界面
		response.getWriter().write("the number of loginning successful:"+count);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}   	  	
}

细节

  • load.html文档表单中的 action 对应的实际是 web.xml 文档中的servlet注册时内容(即所需要的servlet的路径)

    相对路径:

    a路径:servlet的路径 http://localhost:8080/(web.xml 文档中的内容

    b路径: 当前这个html的路径: http://localhost:8080/(success.html即html文档名

ServletContext何时创建?何时销毁?

创建: 服务器启动的时候,会为每一个web应用程序,创建一个ServletContext对象;

销毁: 从服务器移除托管,或者是关闭服务器,则ServletContext销毁

  • ServletContext 的作用范围:

只要在同一个项目里面,都可以取(即同一个项目中的servlet之间可以共享数据,但不同项目之间的servlet之间却不能取数据)。

HttpServletRequest

这个对象封装了客户端提交过来的一切数据(包括 请求行,请求头,请求体)

  1. 可以获取头信息(利用getHeaderNames(); getHeaderName(); getHeader();)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	//1. 得到一个枚举集合,detHeaderNames():获得全部的请求头的名字;getHeader():获得某请求头对应的请求头的值
	Enumeration <String> headerNames = request.getHeaderNames();
	while (headerNames.hasMoreElements()) {
		String name = (String) headerNames.nextElement();
		String value =request.getHeader(name);
		System.out.println("请求头name:"+name+";请求头value:"+value);
	}
}  	
  1. 可以获取客户端提交过来的数据,即获取请求体(利用getParameter(); getParameterNames(); getParameterMap()😉
	//2.获取到的是客户端提交过来的数据,请求体

	  //(1)获取单个数据
	String name=request.getParameter("name");
	String adress=request.getParameter("adress");
	System.out.println("name="+name+";adress="+adress);

	  //(2)获得客户端提交过来的所有参数,得到一个枚举集合
	//Enumeration <String> parameterNames = request.getParameterNames();

	  //(3)获得客户端提交过来的所有参数,得到一个map集合
	Map<String,String[]> parameterMap = request.getParameterMap();
	Set<String> keySet = parameterMap.keySet();
	Iterator<String> iterator=keySet.iterator();
	while (iterator.hasNext()) {
		String key = (String) iterator.next();
		System.out.println(key+"; 它对应的值的总数="+parameterMap.get(key).length);
		for(int i=0;i<parameterMap.get(key).length;i++){
			String value = parameterMap.get(key)[i];
			System.out.println(key+"="+value);
		}
	}
  1. 可以获取客户端提交过来的中文数据,(英语不管在什么编码下都是英文)(利用 username = new String(username.getBytes(“ISO-8859-1”),“UTF-8”);

客户端提交数据给服务器,如果数据中带有中文的话,有可能会出现乱码情况,那么可以参考以下的方法解决。

  • 如果是get方式(form表单的method=“get”)(username = new String(username.getBytes(“ISO-8859-1”),“UTF-8”);)
1. 代码转码
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	System.out.println("username="+username+";password"+password);
	
	//get请求过来的数据,在url地址栏上就已经经过编码了,所以我们取到的就是乱码;
	//tomcat收到了这批数据,getParameter 默认使用 ISO-8859-1 去解码;
	
	//先让文字回到 ISO-8859-1对应的字节数组,然后再按 utf-8 组拼写字符串
	username = new String(username.getBytes("ISO-8859-1"),"UTF-8");
	System.out.println("username="+username+";password"+password);
}  


直接在tomcat里面做配置,以后get请求过来的数据永远都是 utf-8 编码。


2. 可以在tomcat里面做设置处理,conf/servlet.xml,加上URIEncoding="utf-8"如下:

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="utf-8"/>
  • 如果是post方式(request.setCharacterEncoding(“utf-8”);)
	//post请求过来的数据乱码处理;(因为getParameter默认的有一个编码方式,所以我们一定要在读取数据之前改变request的编码方式)

	//注意: 下面这一行说的是设置请求体里面的文字编码。get请求用这行改变编码方式,是没有用的

	request.setCharacterEncoding("utf-8");

	String username = request.getParameter("username");
	String password = request.getParameter("password");
	System.out.println("username="+username+";password"+password);

HttpservletResponse

负责响应或者返回数据给客户端。

输出数据到页面上

	//写文字的话用两种方式都可以,但是文件的话用getWrite()是不行的!

	//以字符流的形式写数据; 
	response.getWriter().write("<h1>hello response....</h1>");
	
	//以字节流的形式写数据
	response.getOutputStream().write("hello response2".getBytes());

响应的数据中有中文,那么很有可能出现中文乱码

  1. 以字符流输出:

     //这里写出去的文字,默认使用的是ISO-8859-1编码; 我们可以指定写出去的时候,使用什么编码
     //Step 1. 指定输出到客户端的时候,这些文字使用utf-8编码
     response.setCharacterEncoding("utf-8");
     //Step 2. 直接规定浏览器看这份数据的时候,使用什么编码来看
     response.setHeader("Content-Type","text/html;charset=UTF-8");
     response.getWriter().write("你好,中国");
    
  2. 以字节流输出:

     //以字节流输出
     /*
      * 如果想让服务器输出的中文,在客户端正常显示,只要确保一点:
      * 出去的时候用的编码和客户端看这份数据用的编码是一样的
      * 默认情况下,getOutputStream 输出使用的是utf-8的编码;如果想指定具体的编码,可以在获取byte数组的时候,指定编码类型
      */
     
     //字符串String这个类里面getBytes()方法使用的码表是utf-8,和tomcat的默认编码无关;tomcat的默认码表还是ISO-8859-1
     String csn=Charset.defaultCharset().name();
     System.out.println("默认的String里面的getBytes方法使用的编码是:"+csn);
    
     //1 指定输出中文用的编码
     response.getOutputStream().write("我爱黑马训练营".getBytes("utf-8"));//这里utf-8可以不用写
     //2 指定浏览器看这份数据使用的编码
     response.setHeader("Content-Type", "text/html;charset=utf-8");
    -------------------------------------------------
     不管是字节流还是字符流,直接使用一行代码就可以了:
     response.setContentType("text/html; charset=utf-8");//设置响应的数据类型html文本,并告知浏览器使用utf-8来编码
    
     再写数据即可
    

演练下载资源(文件下载)

1.直接以超链接的方式下载,不写sevlet(Java)代码,也能够下载东西下来。

让tomcat的默认servlet去提供下载:<br>
<a href="download/aa.jpg">aa.jpg</a><br>
<a href="download/bb.txt">bb.txt</a><br>
<a href="download/cc.rar">cc.rar</a><br>

原因是tomcat里面有一个默认的servlet—DefaultServlet,这个DefaultServlet专门用于处理放在tomcat服务器上的静态资源。

2.手动下载

html文档:

	手动下载,提供下载:<br>
<a href="Demo01?filename=aa.jpg">aa.jpg</a><br>
<a href="Demo01?filename=bb.txt">bb.txt</a><br>
<a href="Demo01?filename=cc.rar">cc.rar</a><br>

sevlet(Java代码):

	//1. 获取要下载的文件名字 ---- InputStream
	String filename=request.getParameter("filename");
	//2. 获取这个文件在tomcat里面的绝对路径地址 
	String path=getServletContext().getRealPath("download/"+filename);
	//让浏览器收到这份资源的时候,以下载的方式提醒用户,而不是直接展示
	response.setHeader("Content-Disposition", "attachment;filename="+filename);
	//3. 转化为输入流
	InputStream is= new FileInputStream(path);
	OutputStream os=response.getOutputStream();
	int len=0;
	byte[] buffer=new byte[1024];
	while( ( len = is.read(buffer) )!= -1 ){
		os.write(buffer,0,len);
	}
	os.close();
	is.close();

3.文件下载(中文名称)
针对浏览器的类型,对中文名称做编码处理 Firefox(Base64),IE/Chrome(URLEncoding).代码如下:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//1. 获取要下载的文件名字 ---- InputStream
	String filename=request.getParameter("filename");
	//来一个get请求,这个filename有中文
	filename = new String(filename.getBytes("ISO-8859-1"),"UTF-8");
	System.out.println("filename="+filename);
	/*
	 * 如果文件的名称中带有中文,那么需要对这个文件进行编码处理
	 * (1) 如果是IE 或者Chrome浏览器,使用URLEncoding 编码
	 * (2)如果是Firefox浏览器,使用Base64编码
	 */
	//获取来访的客户端类型
	String clientType=request.getHeader("User-Agent");
	if(clientType.contains("Firefox")){
		filename=DownloadUtil.base64EncodeFileName(filename);
	}else{
		filename=URLEncoder.encode(filename,"UTF-8");
	}
	//2. 获取这个文件在tomcat里面的绝对路径地址 
	String path=getServletContext().getRealPath("download/"+filename);
	//让浏览器收到这份资源的时候,以下载的方式提醒用户,而不是直接展示
	response.setHeader("Content-Disposition","attachment;filename="+filename);
	//3. 转化为输入流
	InputStream is=new FileInputStream(path);
	OutputStream os=response.getOutputStream();
	int len=0;
	byte[] bytes=new byte[1024];
	while((len = is.read(bytes))!=-1){
		os.write(bytes,0,len);
	}
	os.close();
	is.close();
} 

工具类:

public class DownloadUtil {
 public static String base64EncodeFileName(String fileName){
	BASE64Encoder base64Encoder=new BASE64Encoder();
	try{
		return "=?UTF-8?B?"+new String(base64Encoder.encode(fileName.getBytes("UTF-8")))+"?=";
	}catch(java.io.UnsupportedEncodingException e){
		e.printStackTrace();
		throw new RuntimeException(e);
	}
 }
}

请求转发和重定向

重定向

		/*1.之前的写法:
		*response.setStatus(302);
		*response.setHeader("Location", "success_login.html");
		*/
		//2.重定向的更简单普遍的写法:重新定位方向(参数即跳转的位置)
		response.sendRedirect("success_login.html");

请求转发

 //请求转发的写法:(参数即跳转的位置)
  request.getRequestDispatcher("success_login.html").forward(request,response);

两者的区别:

重定向:
  1. 地址上显示的是最后那个资源的路径地址;(返回302 ok)
  2. 请求次数最少有两次,服务器在第一次请求后,会返回302以及一个地址,浏览器再根据这个地址执行二次访问;
  3. 可以跳转到任意路径,不是自己的工程也可以跳;
  4. 效率稍微低一点,执行两次请求;
  5. 后续的请求无法使用上次request存储的数据,或者没法使用上一次的request对象,因为这是两次不同的请求;
请求转发:(比较常用)
  1. 地址上显示的是请求servlet的地址;(返回200 ok)
  2. 请求的次数只有一次,因为服务器内部帮客户端执行了后续的工作;
  3. 只能跳转到自己项目的资源路径;
  4. 效率上稍微高一点,因为只执行一次请求;
  5. 可以使用上一次的request请求;

界面跳转

#Cookie&Session
##Cookie

饼干,其实是一份小数据,是服务端给客户端的一份数据,并且存储在客户端上的一份小数据

应用场景

自动登录、浏览记录、购物车

为什么要有这个Cookie

http 的请求是无状态的(一次协议交互之后,第二次交互无记忆),客户端和服务器在通讯的时候,是无状态的,其实就是客户端在第二次来访的时候,服务器根本就不知道这个客户端以前有没有来访问过。为了更好地用户体验,更好地交互[自动登录],其实从公司层面讲,就是为了更好地收集用户习惯[大数据]。

Cookie 有什么用?

  • 简单使用:

1.第一次请求:服务器生成cookie,并发送cookie给客户端(addCookie(cookie))

 1.在响应的时候,添加Cookie:
	Cookie cookie=new Cookie("aa","22");
	//给响应,添加一个cookie
	response.addCookie(cookie);
 2.在客户端收到的信息里面,响应头中多了一个字段 Set-Cookie:aa=22

2.第二次请求:获取客户端带过来的cookie(request.getCookies())

 3.获取客户端带过来的cookie
    Cookie[] cookies = request.getCookies();
	if(cookies != null){
		for (Cookie c : cookies) {
			String cookieName = c.getName();
			String cookieValue= c.getValue();
			System.out.println(cookieName+"="+cookieValue);
		}
	}

补充(全代码):

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//Cookie的简单使用
	//cookie 是服务器发送给客户端,并且保存在客户端上的一份小数据
	/*
	 * 方法参数要什么就给什么
	 * 创建一个对象的几种手段:
	 * 1.直接new
	 * 2.单例模式|提供静态方法
	 * 3.工厂模式构建 例如stu对象
	 * stuFactory stuBuilder
	 */		
	response.setContentType("text/html;charset=utf-8");
	//一、发送cookie给客户端
	Cookie cookie=new Cookie("aa","22");
	//给响应,添加一个cookie
	response.addCookie(cookie);
	response.getWriter().write("请求成功了。。。");	
	//二、获取客户端带过来的cookie
	Cookie[] cookies = request.getCookies();
	if(cookies != null){
		for (Cookie c : cookies) {
			String cookieName = c.getName();
			String cookieValue= c.getValue();
			System.out.println(cookieName+"="+cookieValue);
		}
	}
}

3.常用方法:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//1. 获得客户端请求带过来的cookie
	//注:请求cookie最好放在响应cookie前面
	Cookie[] cookies = request.getCookies();
	if(cookies!=null){
		for (Cookie cookie : cookies) {
			System.out.println(cookie.getName()+"="+cookie.getValue());
		}
	}
	//2. 响应cookie,可以给客户端返回两个cookie
	Cookie cookie1=new Cookie("name", "张三");
	response.addCookie(cookie1);
	  //2.1这里为第二个cookie设置有效期: (注:设置有效期的代码需要放在响应cookie代码的前面)
	/*
	 * cookie 的有效期:
	 * 默认情况下,关闭浏览器后,cookie就没有了。----> 针对没有设置cookie有效期的情况
	 * expiry:意思是有效期(以秒为单位)
	 * ---> 正值:表示这个时间过后,cookie将会失效
	 * ---> 负值:表示关闭浏览器之后,cookie就会失效,默认值是-1;
	 * ---> 0:表示删除这个cookie;
	 */
	cookie1.setMaxAge(60*60*24*7);//这里设置一周有效期
	Cookie cookie2=new Cookie("age", "25");
	response.addCookie(cookie2);
	//3.cookie 的其他常用方法
	//3.1 为某个cookie赋新的值
	cookie1.setValue("wangwu");
	//3.2 用于指定: 只有请求了指定的域名,才会带上该cookie
	cookie1.setDomain(".itheima.com");
	//3.3 只有访问该域名下的CookieDemo这个路径地址才会带cookie
	cookie1.setPath("/CookieDemo");
	//例如:访问www.itheima.com/CookieDemo,客户端会带cookie过来	
}

例子1:显示最近访问(上一次访问)的时间

cookie获取上次登录的时间

  1. 判断账号是否正确
  2. 如果正确,则获取cookie,但是的到的cookie是一个数组,我们从数组里面找到我们想要的对象;
  3. 如果找到对象为空,表明第一次登录,那么要添加cookie;
  4. 如果找到的对象不为空,表明不是第一次登录,更新cookie;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	String username=request.getParameter("username");
	String password=request.getParameter("password");
	response.setCharacterEncoding("utf-8");
	response.setContentType("text/html;charset=utf-8");
	if("adrimn".equals(username)&&"123".equals(password)){
		//获取客户端请求是带的cookie数组
		Cookie[] cookies = request.getCookies();
		//利用自己写的工具类
		Cookie findCookie = CookieUtil.findCookie(cookies, "last");
		if(findCookie==null){
			//如果是第一次登录
			//1. 输出到浏览器页面
			response.getWriter().write("欢迎您,"+username);
			//2. 添加cookie到浏览器(并设置有效期)
			Cookie cookie=new Cookie("last",System.currentTimeMillis()+"");
			cookie.setMaxAge(60*60);//一小时
			response.addCookie(cookie);
		}else{
			//如果不是第一次登录(浏览器请求会带有cookie)
			//1.取以前的cookie值
			Long lastVistTime=Long.parseLong(findCookie.getValue());
			//2.输出到浏览器界面
			response.getWriter().write("欢迎您,"+username+"; 上次登录的时间是:"+new Date(lastVistTime));
			//3. 重置登录时间
			findCookie.setValue(System.currentTimeMillis()+"");
			response.addCookie(findCookie);
		}	
	}else{
		response.getWriter().write("登录失败");
	}	
}

例子2: 显示商品的浏览记录

准备工作

  1. 拷贝基础课第一天的html原型文件(包括首页,商品列表页面[里面有浏览记录内容],商品详情页面,用户登录页面,),到webContent里面;

  2. 在webContent目录下面,新建一个jsp文件(product_list.jsp),然后拷贝原来product_list.html内容到jsp里面;建好之后jsp文件中的所有charset和encoding(中的ISO-8859-1)全部改为utf-8;

    拷贝html标签的所有内容,替换jsp的html标签即可

  3. 修改(product_info.html商品详情页面)里面的手机数码超链接地址为product_list.jsp;

  4. 修改页面(index.html首页)顶部的手机数码跳转的位置为 product_list.jsp

注:导入外部项目,若出现中文乱码问题可以:设置Windows–Preferences–General–Content Types–Text–HTML;
或者 Windows–Preferences–General–Content Types–Text–java source file;

分析(见图)

cookie浏览记录分析

代码

1.首先设置product_list.jsp中的商品的超链接为:servlet的url-pattern?id=1(例如 ProductInfo?id=1)

<div style="float: left;height: 480px;width: 15%;">
  <a href="ProductInfo?id=1"><img src="../img/0001.jpg"width="100%"height="100%" /></a>
</div>

2.利用cookie获取浏览记录(名为ProductInfo的servlet代码如下:)

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//获取到当前用户准备浏览的商品id
	String id=request.getParameter("id");
	Cookie[] cookies = request.getCookies();
	Cookie findCookie = CookieUtil.findCookie(cookies, "history");
	//第一次浏览
	if(findCookie==null){
		//1. 响应,并返回cookie
		Cookie cookie=new Cookie("history",id);
		cookie.setMaxAge(60*60);
		response.addCookie(cookie);
		
	}else{
		//第二次浏览
		//1. 获取以前的cookie,因为以前的cookie,包含了浏览记录
		String findValue = findCookie.getValue();
		//2. 让现在浏览的商品,和以前浏览的商品,形成cookie新的值
        //注意:addCookie()之前需要加上有效期
		findCookie.setMaxAge(60*60);
		findCookie.setValue(findValue+"#"+id);
		response.addCookie(findCookie);
		System.out.println(findCookie.getName()+"="+findCookie.getValue());
	}
	response.sendRedirect("product_info.html");
}  

3.JSP显示浏览记录(product_list.jsp页面中的浏览记录显示部分的代码如下:)

<!--:第四部分:浏览记录-->2
<div style="width:1210px;margin:0 auto; padding: 0 9px;border:1px solid #ddd;border-top:2px solid #999;height:246px">
   <div>
    	<h2>浏览记录 </h2>
   </div>
   <div>
      <ul style="list-style: none;">
    	<% 
    	 Cookie[] cookies = request.getCookies();
		 Cookie findCookie = com.itheima.servlet.CookieUtil.findCookie(cookies, "history");
		 //如果findcookie为空,则表明没有浏览记录
		 if(findCookie==null){
    	%>	
		 <h1>您还没有浏览任何商品</h1>
		<% 
		 }else{//如果不为空,则利用.split("#")分离出浏览过的所有商品id
		 //注意:如果重复浏览的商品,则先去掉之前的记录,再在增加新的记录(可以用LinkedList来实现,删除,插入)
		 String[] ids=findCookie.getValue().split("#");
		 for(String id:ids){
		%>
		 <li style="width:150px; height:216; float:left;margin:0 8px 0 0;padding:0 18px 15px;text-align:center;"><img src="000<%=id %>.jpg" width="130px" height="130px"/></li>
		<%		
		  }
		 }
    	%>			
    </ul>
  </div>	
</div>

4.清除浏览记录
其实就是清除cookie,删除cookie是没有什么delete方法的,只有设置setMaxAge(0);

(1) 首先在product_list.jsp页面中增加 清除浏览记录 的按钮,代码如下:

   <div>
    <a href="clearHistory"><h1>清除浏览记录</h1></a>
   </div>

(2) 然后写一个名为clearHistory的servlet,作用是清除cookie,代码如下:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//演练清除cookie
	Cookie cookie = new Cookie("history","");
	cookie.setMaxAge(0);//设置立即删除cookie
	response.addCookie(cookie);
	response.sendRedirect("product_list.jsp");
}  	

jsp 里面使用Java代码

jsp(Java server Pager—> 最终会翻译成一个类,就是servlet)
定义全局变量:

<%! int a=99;%>

定义局部变量 :

<% int b=999;%>

在jsp页面上,显示a或者b的值,如下:

<%=a %>
<%=b %>

小结(Cookie):

  1. cookie是服务器发送给客户端的一份小数据,并且存放在客户端上。
  2. 获取cookie,添加cookie:
String[] cookies=request.getCookies();
Cookie cookie=new Cookie("cookie名","cookie值")
response.add(cookie);
  1. Cookie 分类
会话cookie: 默认情况下,关闭了浏览器,那么cookie就会消失;
持久cookie:在有效期限内,有效,并且会保存在客户端
cookie.setMaxAge(0);//设置立即删除
cookie.setMaxAge(60*60);//有效期是60*60秒
  1. cookie 的好处是:用户体验会更好
  2. cookie的缺陷有:

由于cookie会保存在客户端上,所以有安全隐患

还有一个问题,cookie的大小和个数有限制。(浏览器应该支持每台web服务器有20个cookie,总共有300个cookie,并且可能将每个cookie的大小限定为4KB)。

为了解决这个问题,所以有了------> Session

Session

翻译过来是:会话;Session是基于Cookie的一种会话机制(Cookie本身就是一种会话机制),Session是数据存放在服务器端(Cookie是服务器返回一小份数据给客户端,并且存放在客户端)

  • 常用的API :

      HttpSession session = request.getSession();
      //得到会话id
      session.getId();
      //存值
      session.setAttribute("name", "value");
      //取值
      session.getAttribute("name");
      //移除值
      session.removeAttribute("name");
    
  • Session 何时创建?何时销毁?

1.创建:

如果有在servlet里面调用了 request.getSession();

2.销毁:

session是存放在服务器内存中的一份数据。当然可以持久化(Redis): 即使关闭了浏览器,session也不会销毁。

销毁的方法如下:

(1) 关闭服务器;

(2) session会话时间过期了,有效期过了。默认有效期是:30分钟(servlet中的web.xml文件中自己定义的30分钟,可以设置)

例子3:简单购物车

分析(见图)

Session购物车例子分析

代码

  • cartServlet代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	response.setContentType("text/html;charset=utf-8");
	//1.获取要添加到购物车的商品id
	Integer id = Integer.parseInt(request.getParameter("id"));
	String[]names={"iPhone11","小米8","华为9","三星note11","vivo9"};
	//取到id对应的商品名称
	String name=names[id];
	//2.获取购物车存放东西的session Map<String,Integer> iPhone11 3
	//session只是一个容器,里面存放的是一个map对象,并且保证只存放一次
	Map <String,Integer>map = (Map<String, Integer>) request.getSession().getAttribute("cart");
	//首先判断session里面有没有存放任何东西
	if(map==null){
		map=new LinkedHashMap<String, Integer>();
        //setAttribute():即表示在session里面放一个map
		request.getSession().setAttribute("cart", map);
	}
	//3.判断购物车里面有没有商品
	if(map.containsKey(name)){
		map.put(name,map.get(name)+1);
	}else{
		map.put(name, 1);
	}
	response.getWriter().write("<a href='product_list.jsp'><h2>继续购物</h2></a>");
	response.getWriter().write("<a href='cart.jsp'><h2>去购物车结算</h2></a>");
} 
  • 页面显示(cart.jsp代码):
<%@ page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.Set"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h2>您的购物车商品如下:</h2><br>
    <%
	    //注意,要想获取购物车中的商品(名称,数量),就要先获得map;要想获得map,就要先获得session
	    Map<String,Integer>map=(Map<String,Integer>)session.getAttribute("cart");
	    if(map!=null){
		    Set<String> set=map.keySet();
		    for(String name:set){//name:商品名称,value:商品个数
			    int value=map.get(name);
    %>
    <h3>名称:<%=name %> 数量:<%=value %></h3>
    <% 
		    }
	    }
    %>
</body>
</html>
  • 清除购物车(session.invalidate();session.removeAttribute(“cart”);)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//清除购物车,其实就是清除session里面的map
	HttpSession session = request.getSession();
	//方法1:强制干掉session会话,里面存放的任何数据都没有了
	session.invalidate();
	/*//方法2:从session里面移除某一个数据
	session.removeAttribute("cart");
	response.sendRedirect("cart.jsp");*/
}  

总结:

  • 请求转发和重定向(面试常问)

  • Cookie:(更重要)

服务器发送给客户端的一份小数据,存放在客户端;

基本用法:

 添加cookie
 获取cookie

演练例子:

  1.获取上次访问时间;
  2.获取商品浏览记录;

什么时候生成cookie?

  response.addCookie(new Cookie())

cookie分类

会话cookie: 默认情况下,关闭了浏览器,那么cookie就会消失;
持久cookie:在有效期限内,有效,并且会保存在客户端
cookie.setMaxAge(0);//设置立即删除
cookie.setMaxAge(60*60);//有效期是60*60秒
  • Session:(靠cookie去传的)

基于cookie的一种会话技术(会话:在同一个网站上多次访问上面的一些超链接,属于一次会话);数据存放在服务器端

会在cookie里面添加一个字段JKFHSJAGFHAJ, 是tomcat服务器生成的;

  setAttribute();
  getAttribute();
  getSession().getId();//得到会话id
  removeAttribute();//移除数据
  invalidate();//强制使会话失效

创建和销毁:

 调用request.getSession()创建
 服务器关闭,或者会话超时(30);则Session销毁
 注:setAttribute存放的值,在浏览器关闭之后,还有(在服务器上),就算客户端把电脑砸了,也还有。

jsp & EL & JSTL

#JSP

Java Server Page

  • 什么是jsp?

从用户角度看待,就是一个网页;从程序员角度来看,其实就是一个Java类,它继承了servlet,所以可以直接说jsp就是一个servlet

  • 为什么会有jsp?(为了更好地用户体验)

html多数情况下用来显示静态内容,一成不变的;

但是有时候我们需要在网页上显示一些动态数据,比如:查询所有的学生信息,根据姓名去查询具体某个学生。这些动作都需要去查询数据库,然后在网页上显示。

html是不支持写Java代码的,但是jsp里面可以写Java代码。

怎么用jsp?

  • jsp的三大指令:page指令;include指令;taglib指令。
  • jsp的动作标签:(重点讲3个)jsp:forword;jsp:include;jsp:param。
  • jsp的内置对象:

四个作用域: pageContext; request; session; application.

指令的写法:

<%@ 指令名字 %>

1.page指令

<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8" extends="httpjspbase" import="" session="true或者false" errorPage="error.jsp"%>
  • language:

表明jsp页面中可以写什么语言

  • contentType:

其实就是说这个文件是什么类型,告诉浏览器我是什么内容类型,即怎么打开它,以及使用什么编码

contentType="text/html;charset=utf-8"
//text/html:MIME Type 这是一个文本,html网页
//或者可以是 video/mp4: 表示这是一个音频文件
  • pageEncoding:

jsp文件下面的[如<body><head>中]的内容编码

  • entends:

用于指定jsp翻译成Java文件后,继承的父类是谁,一般不会改。

  • import:

导包使用的,一般不用手写。(快捷键:alt+/)

  • session:

值可选的有true/false

用于控制在这个jsp页面里面能否直接使用session(true:可以直接用session.getAttribute())

具体的区别是:请看具体的tomcat/work/Catalina/localhost/具体项目名称/具体名称.jsp java文件;

(1)如果值为true: 在jspService里面看到session=pageContent.getOut();---->创建session;

(2)如果值为false: 没有上面的那一行代码;那么就没有该方法调用,也就是没有session对象了,在页面上自然也就不能直接使用session.getAttribute();

  • errorPage:

指的是错误的页面,值需要给出:显示错误信息的页面的路径(比如error.jsp;在error.jsp中的page指令中可以配合使用 isErrorPage属性,令isErrorPage=“true”)
error.jsp页面的代码如下:

<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8" isErrorPage="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/"
<html>
   <head>
   <meta http-equiv="Content-Type" content=:text/html;charset=utf-8">
   <title>Insert title here</title>
   </head>
   <body>
        出错了:<%=exception.toString()%>
   </body>
</html>
  • isErrorPage:

上面的errorPage用于指定错误的时候跑到哪个页面去,那么这个isErrorPage,就是声明某一个页面到底是不是错误的页面。

2.include 指令

<%@include file="另外一个文件的路径"%>

包含另外一个jsp的内容写进来(即另外一个页面上的内容在该页面显示),include可以写在jsp文档的任意位置,如<body>标签中

背后细节:---------> 静态包含:就是把另外一个页面的源代码拿过来一起输出,所有的标签元素都包含进来;

3.taglib 指令(引入标签库)

<%@ taglib prefix="" uri="" %>
uri:标签库的路径
prefix:标签库的别

jsp的动作标签

  • 很多,在<body>中写 <jsp: 就会提示很多;这里主要讲3个,如下:
<jsp:include page="指定的包含页面的路径"></jsp:include>
<jsp:param value="" name=""/>
<jsp:forword page="指定的跳转至的页面的路径"></jsp:forword>
注:Ctrl+c+/:jsp 中注释的快捷键

1. jsp:include 动态标签

<jsp:include page=""></jsp:include>
 意思是:包含指定的页面。 

注意:这里是动态包含,也就是不是把包含在页面里的所有元素标签全部拿过来,而是把它的运行结果拿过来。(查看源码就可以看出来)

等同于前面的include指令的效果,不同之处是:include指令是静态包含;而jsp:include是动态包含。

2. jsp:forword 动态标签

<jsp:forword page=""></jsp:forword>
意思是: 前往哪一个页面(跳转页面至---)。

等同于以下代码:(实际上jsp:forword的底层源代码就是下面的代码)

<% 
  //请求转发
  request.getRequestDispatcher("跳转至的页面.jsp").forword(request,response);
%>

3. jsp:param 动态标签

<jsp:param value="" name=""/>
 意思是:  在包含某个页面的时候,或者再跳转到某个页面的时候,加入这个参数。

other.jsp跳转至other2.jsp页面,并且携带参数value=“beijing” name="address"过去;代码如下:

 <jsp:forword page="other2.jsp"<jsp:param value="beijing" name="address"/>
    </jsp:forword>

other2.jsp里面获取参数,代码如下:

 <%=request.getParameter("address")%>

jsp的内置对象[9个](重点)

所谓的内置对象:就是我们可以直接在jsp页面中使用这些对象,不用创建

首先,包括四个作用域: pageContext、request、 session、application

作用域:其实就是表示这些对象可以存值,它们的取值范围有限定。通过 .setAttribute() 存值;通过 .getAttribute() 取值;具体如下:

使用作用域来存储数据:<br>
<% 
 pageContext.setAttribute("name","page");
 request.setAttribute("name","request");
 session.setAttribute("name","session");
 application.setAttribute("name","application");
%>
取出四个作用域中的值:<br>
<%=pageContext.getAttribute("name")%>
<%=request.getAttribute("name")%>
<%=session.getAttribute("name")%>
<%=application.getAttribute("name")%>

四个作用域的区别:

1. pageContext(PageContext类型)

作用域仅限于当前页面

2. request(HttpServletRequest类型)

作用域仅限于一次请求,只要服务器对该请求作出了回应,这个域中存的值就没有了。

3. session(HttpSession类型)

作用域仅限于一次会话(多次请求与响应都属于一次会话,但只要关闭浏览器,再次请求就属于第二次会话)当中,一次会话结束后(关闭浏览器,或者会话过期),这个域中的值就没有了。

4. application(ServletContext类型)

整个工程都可以访问,服务器关闭后就不能访问了

平时比较少用的3个内置对象:

5. page(Object类型,就是这个jsp翻译成Java类的实例对象)
6. config(ServletConfig类型)
7. exception(Throwable类型)

(在page指令中有isErrorPage="true"时,按快捷键alt+/,提示才会显示exception内置对象)

剩下的内置对象:

8. out(JspWriter类型)
9. response(HTTPServletResponse类型)

两者的区别是:当两种输出方式同时出现在同一个jsp文档中时,先输出到页面response本身要输出的东西,然后才输出out里面的内容。(实际上,out.write()背后是 —> 把out对象输出的内容放置到response的缓冲区去)

EL(Expression Language)

为了简化jsp代码,具体一点就是为了简化在jsp里面写的Java代码;
* 写法格式: ${ 表达式 }

取值用法

  • 如何使用?(EL表达式没有快捷键)

1. 使用EL表达式取出4个作用域中存放的值

使用作用域来存储数据:<br>
<% 
 pageContext.setAttribute("name","page");
 request.setAttribute("name","request");
 session.setAttribute("name","session");
 application.setAttribute("name","application");
%>
按普通手段取出四个作用域中的值:<br>
<%=pageContext.getAttribute("name")%>
<%=request.getAttribute("name")%>
<%=session.getAttribute("name")%>
<%=application.getAttribute("name")%>
使用EL表达式取出作用域中的值:<br>
//注:如下如果提示错误,就删掉,重新粘贴
${ pageScope.name }
${ requestScope.name }
${ sessionScope.name }
${ applicationScope.name }

2. 如果作用域中存的值是数组/集合/map,则如下取:

<%
  String[] a={"aa","bb","cc",""dd};
  pageContext.setAttribute("array",a);
%>

使用EL表达式取出作用域中数组的值:<br>
${array[0]},${array[1]},${array[2]},${array[3]}

<%
  List list=new ArrayList();
  list.add("11");
  list.add("22");
  list.add("33");
  list.add("44");
  pageContext.setAttribute("li",list);
%>

使用EL表达式取出作用域中集合中的值:<br>
${li[0]},${li[1]},${li[2]},${li[3]}

<%
  Map map=new HashMap();
  map.put("name","zhangsan");
  map.put("age",15);
  map.put("adress","beijing");
  map.put("adress.aa","shenzhen");//注意这个
  pageContext.setAttribute("ma",map);
%>

使用EL表达式取出作用域中集合中的值:<br>
${ma.name},${ma.age},${ma.adress},${ma["adress.aa"]}

3.取值细节

(1)

从域中取值,得先存值
<% 
  pageContext.setAttribute("name","zhangdan");
  session.setAttribute("name","lisi");
%>

//直接指定了说:到pageContext这个作用域里面去找这个name:
	${pageScope.name}

//先从page里面找,没有;再去request里面找,没有;再去session中找,没有;再去application里面找;(注意,有的话,就不会接着找下去了)
 	   ${name}

//指定从session中取值
	${sessionScope.name}

(2)取值方式

* 如果这个值有下标,那么直接使用[];

* 如果值没有下标,直接使用.的方式去取;

//单个对象的存取属性值:
  //先见一个class文件,新建一个User类,里面定义属性name,age(set,get方法,以及含参构造方法)
  //再写jsp文件如下:
<%
  User user=new User("zhangsan",28);
  session.setAttribuet("u",user);
%>
${u.name},${u.age}

//判断对象是否为空
${empty u }

//做逻辑判断,或者算术运算
${a||b },${a>b },${a+b }
  • 一般使用EL表达式,用的比较多的,都是从一个对象中取出它的属性值,比如取出某一个学生的姓名

EL表达式的11个内置对象(即隐式对象):

${对象名.成员 }//可以直接用对象名.

注:只有一个内置对象与jsp的内置对象同名,就是pageContext!

JSP:

—1. pageContext—PageContext实例

作用域相关对象:

—2. pageScope—Map类(可以用.)

—3. requestScope—Map类

—4. sessionScope—Map类

—5. applicationScope—Map类

头信息相关对象:

—6. header—Map类

—7. headerValues—Map类

参数信息相关对象:

—8. param—Map类

—9. paramValues—Map类

Cookie:

—10. cookie—Map类

全局初始化参数:

—11. initParam—Map类

一个例子:

<%
request.getParameter("address");
%>
使用EL表达式获取这个参数:
${param.address }

JSTL

引入

全程:jsp Standard Tag Library(jsp标准标签库)

作用:简化jsp的代码编写,替换<% %>写法,一般与EL表达式配合使用;(EL表达式一般都只能用来取值,却不能遍历;所以需要JSTL来执行遍历操作)
怎么用?

  1. 导入JSTL支持jar文件(jstl.jar)和standard.jar,到工程的webContent/web-inf/lib

  2. 在jsp页面上,使用taglib指令,来引入标签库

    <%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core”%>

  3. 注意:如果想支持 EL表达式,那么引入的标签库必须是1.1的版本,1.0的版本不支持;(prefix=“c,也可以是别的比如itheima,c的原因是后面是core” uri=“使用1.1的核心版本,选择http://java.sun.com/jsp/jstl/core”)

基本用法

常用的标签

  • <c:set></c:set>
<!--声明一个对象name,对象的值为zhangsan,默认存储到page,也可以通过scope指定存储域-->
<c:set var="name" value="zhangsan" scope="session"></c:set>
//取值
${sessionScope.name }
  • <c:if test=“EL表达式”></c:if>

判断test里面的表达式是否满足,如果满足,就执行c:if标签中的代码;注意:c:if是没有else;

<c:set var="age" value="18"></c:set>
//定义一个变量名 flag 去接收前面表达式的值,然后存放在session域中
//var相当于:boolean flag = age>16
<c:if test="${age>16}" var="flag" scope="session">
	年龄大于16岁了
</c:if>
//如果要写else,就再写如下的代码:
<c:if test="${age<=16}">
	年龄大于16岁了
</c:if>
//输出判断的结果
${flag }
  • <c:forEach></c:forEach>(比较常用)

用于做遍历

//从1开始遍历到10,得到的结果。复制给i,并且默认存储到page域中;step表示增幅为2
<c:forEach begin="1" end="10" var="i" step="2">
	${i }
</c:forEach>
//items:表示遍历哪一个对象,注意:这里必须是EL表达式
//var:遍历出来的每一个元素用user去接收
<%
	List list=new ArrayList();
	list.add(new User("zhangsan",13));
	list.add(new User("lisi",15));
	list.add(new User("wangwu",17));
	list.add(new User("zhaoliu",19));
	pageContext.setAttribute("li",list);
%>
<c:forEach var="user" items="${li }">
	${user.name },${user.age }
</c:forEach>

例子:学生信息管理系统

分析(见图)

学生管理系统分析

  1. 先写login.jsp,并且搭配一个loginServlet 去获取登录信息;

  2. 创建用户表,里面只要有id, username, password;

  3. 创建 userDao接口,定义登录方法;

  4. 创建 userDaoImp1 实现类, 实现刚才的登录方法;

  5. 在loginServlet里面访问userDao, 判断登录结果,以区分对待;

  6. 创建stu_list.jsp页面, 让登录成功的时候,跳转过去;

  7. 创建学生表,里面字段随意;

  8. 创建 stuDao接口,定义查找所有学生信息的方法;

  9. 创建 stuDaoImp1 实现类,实现刚才的方法;

  10. 在登录成功的时候,完成三件事:

    1)查询所有的学生信息;(访问stuDao)
    2)把这个查询到的学生集合存储到作用域中;
    3)跳转到stu_list.jsp页面。

  11. 在stu_list.jsp中,取出域中的集合,然后用c标签去遍历集合。

登录准备

  1. login.jsp:
<%@ 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=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>欢迎登陆学生信息管理系统</h2><br>
	<form action="loginServlet" method="post">
		账户:<input type="text" name="username"/><br>
		密码:<input type="password" name="password"/><br>
		<input type="submit" name="提交"/>
	</form>
</body>
</html>
  1. login_servlet.java:
package com.itheima.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 下面是登录页面的servlet
 */
 public class loginServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		System.out.println(username+":"+password);
	}  	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	doGet(request, response);
	}   	  	    
}

创建数据库 & Dao

  1. 首先在SQLyog里面 创建数据库stus–>创建用户表user
CREATE DATABASE `stusmanager`;
USE `stusmanager`;
CREATE TABLE `stusmanager`.`user`(     `id` INT(20) AUTO_INCREMENT UNIQUE ,     `username` VARCHAR(30) ,     `password` VARCHAR(30)   );
INSERT INTO `stusmanager`.`user`(`id`,`username`,`password`) VALUES ( '1','adrimn','adrimn');
INSERT INTO `stusmanager`.`user`(`id`,`username`,`password`) VALUES ( '2','张三','123');
INSERT INTO `stusmanager`.`user`(`id`,`username`,`password`) VALUES ( '3','李四','456');
INSERT INTO `stusmanager`.`user`(`id`,`username`,`password`) VALUES ( '4','赵柳','789');
  1. 然后新建一个userDao接口和一个userDaoImp1实现类

Dao接口

package com.itheima.dao;
/**
 * 该dao定义了对用户表的访问规则
 */
public interface userDao {
	/**
	 * 这里简单就返回一个Boolean类型,成功或者失败即可;
	 * 
 	* 但是开发的时候,登陆的方法,一旦成功,这里应该返回用户的个人信息
 	* @param username
 	* @param password
	 * @return true:登录成功; false:登录失败
	 */
	boolean login(String username, String password);
	boolean login();
}

Dao实现类

package com.itheima.dao.imp1;
import com.itheima.dao.userDao;
public class userDaoImp1 implements userDao {
    public boolean login(String username, String password) {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs = null;
		try {
			//1.和数据库建立连接
			conn = JDBCUtil.getConn();
			//2.和数据库打交道,创建sql语句
			String sql="select * from user where username=? and password=?";
			ps = conn.prepareStatement(sql);
			ps.setString(1, username);
			ps.setString(2, password);
			//3.执行查询,获得结果集
			rs = ps.executeQuery();
			return rs.next();//这行代码等价于下面的if---else--
			/*if(rs.next()){
				return true;
			}else{
				return false;
			}*/
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			JDBCUtil.release(conn,ps,rs);
		}
		return false;
	}
/* 用来验证是否连接上数据库
 */
public boolean login() {
	Connection conn=null;
	try {
		conn = JDBCUtil.getConn();
		System.out.println("数据库状态:" + conn.isClosed());
	} catch (Exception e) {
		e.printStackTrace();
	}
	return false;
}

}

集成JDBC环境

  1. 先把JDBC的jar复制到webContent/WEB-INF/lib;

    mysql-connector-java-5.1.46-bin.jar

  2. 新建一个JDBC的工具类(之前学MySQL时写过),其中需要在src下面写一个 .propertiesde 的文件;

JDBCUtil.java代码:

package com.itheima.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.Properties;
import com.mysql.jdbc.PreparedStatement;

public class JDBCUtil {
	static String driverClass=null;
	static String url=null;
	static String user=null;
	static String password=null;
	static{
		try {
			Properties properties=new Properties();
			InputStream is =JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
			properties.load(is);
			driverClass = properties.getProperty("driverClass");//注意:这里要加上引号
			url = properties.getProperty("url");
			user = properties.getProperty("user");
			password= properties.getProperty("password");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Connection getConn(){
		Connection conn=null;
		try {
			Class.forName(driverClass);
			conn = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;
	}
	public static void release(Connection conn, java.sql.PreparedStatement ps,
		ResultSet rs) {
		closeRs(rs);
		closePs(ps);
		closeConn(conn);
	
	}
	private static void closeConn(Connection conn) {
		try {
			if (conn != null) {
				conn.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			conn=null;
		}	
	}
	private static void closePs(java.sql.PreparedStatement ps) {
		try {
			if (ps != null) {
				ps.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			ps=null;
		}
	}
	private static void closeRs(ResultSet rs) {
		try {
			if (rs != null) {
				rs.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			rs=null;
		}
	}
}

jdbc.properties代码:

driverClass = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost/stusmanager
user = root
password = hxzgnpy123

登录显示

改进上面的login_servlet.java代码:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//提交的数据可能有中文,怎么处理
	request.setCharacterEncoding("UTF-8");
	response.setContentType("text/html;charset=utf-8");
	//1.获取客户端提交的信息
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	//System.out.println(username+":"+password);
	//2.去访问dao,看看是否满足登录;去数据库访问有没有这个用户
	//new userDaoImp1().login();
	userDao udao = new userDaoImp1();
	boolean isSuccess = udao.login(username, password);
	//3.针对dao的结果,做出反应
	if(isSuccess){
		response.getWriter().write("登录成功");
	}else{
		response.getWriter().write("用户名或密码错误");
	}
}  	

查询所有学生信息

  1. 在sqlyog中,新建学生信息表stus;

  2. 然后新建一个stuDao接口和一个stuDaoImp1实现类;

stuDao.java

package com.itheima.dao;
import java.util.List;
import com.itheima.domain.Student;
/**
 *该dao定义了对学生表stus的访问规则
 */
public interface stuDao {
	List<Student> findAll();
}

stuDaoImp1.java

package com.itheima.dao.imp1;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.itheima.dao.stuDao;
import com.itheima.domain.Student;
import com.itheima.util.JDBCUtil;
public class stuDaoImp1 implements stuDao {
	public List<Student> findAll() {
		List <Student> list=new ArrayList();
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs = null;
		try {
			//1.和数据库建立连接
			conn = JDBCUtil.getConn();
			String sql="select * from stus";
			ps=conn.prepareStatement(sql);
			rs=ps.executeQuery();
			//数据多了,用对象;对象多了,用集合;
			while(rs.next()){
				Student student=new Student();
				student.setId(rs.getInt("id"));
				student.setName(rs.getString("name"));
				student.setAge(rs.getInt("age"));
				student.setGender(rs.getString("gender"));
				student.setAdress(rs.getString("adress"));
				list.add(student);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			JDBCUtil.release(conn,ps,rs);
		}
		return list;
	}
}
  1. 改进上面的login_servlet.java代码:
	if(isSuccess){
		//response.getWriter().write("登录成功");
		//1. 查询所有学生信息(注意:一个Dao对应一张表,需要再新建一个接口Dao和一个实现类)
		stuDao sdao= new stuDaoImp1();
		List<Student> list = sdao.findAll();
		//2. 把查询到的消息保存在域中
		HttpSession session = request.getSession();
		session.setAttribute("li", list);
		//3. 跳转页面 (方法一:重定向;方法二:请求转发)
		response.sendRedirect("stu_list.jsp");
		//request.getRequestDispatcher("stu_list.jsp").forward(request,response);
	}else{
		response.getWriter().write("用户名或密码错误");
	}

页面显示学生信息

  1. 先把taglib的jar复制到webContent/WEB-INF/lib;

  2. 在stu_list.jsp中,取出域中的集合,然后用c标签去遍历集合。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!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=UTF-8">
<title>学生信息管理系统</title>
</head>
<body>
	学生信息表:<br>
	<table border="1" width="700">
		<tr align="center">
			<td>编号</td>
			<td>姓名</td>
			<td>年龄</td>
			<td>性别</td>
			<td>住址</td>
			<td>操作</td>
		</tr>
		<c:forEach items="${li}" var="stu">
			<tr align="center">
				<td>${stu.id }</td>
				<td>${stu.name }</td>
				<td>${stu.age }</td>
				<td>${stu.gender }</td>
				<td>${stu.adress }</td>
				<td><a href="#">删除</a>  <a href="#">更新</a></td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

总结:

  • JSP

三大指令集
page
include
taglib

三个动作标签

<jsp:include>
<jsp:forward>
<jsp:param>

九个内置对象

//作用域
pageContext
request
session
application
page
config
exception
out
response
  • EL

    ${表达式 }

  1. 取4个作用域中的值
  2. 11个隐式对象(内置对象)
pageContext
pageScope
requestScope
sessionScope
applicationScope
header
headerValues
param
paramValues
initParam
cookie
  • JSTL

使用1.1的版本,支持EL表达式;

拷贝jar包,通过taglib去引入标签库;
<c:set>
<c:if>
<c:forEach>

------------------------------------注:至此web结束

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值