三、Servlet-登陆、跳转、统计

一、Http协议

HTTP协议:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。

HTTP协议规定 浏览器(客户端)向服务器发送 何种格式的数据. 服务器 会处理数据. 向浏览器(客户端)作出响应.(向客户端发送何种格式的数据)

HTTP协议的特点:

  • HTTP协议遵守一个请求响应模型.
    • 请求和响应必须成对出现.
    • 必须先有请求后有响应.
  • HTTP协议默认的端口:80

1. HTTP协议的请求部分

客户端向服务器发送的数据的格式:

GET请求方式的抓包:
GET /WEB09/demo1/subSucc.html?username=aaa&password=123 HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 63397-10023
Referer: http://localhost:8080/WEB09/demo1/demo1.html
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

POST方式的抓包:
POST /WEB09/demo1/subSucc.html HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 63397-10049
Referer: http://localhost:8080/WEB09/demo1/demo1.html
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 25
Connection: Keep-Alive
Cache-Control: no-cache
username=aaa&password=123

请求行
请求方式 请求路径 协议版本

  • 请求方式:请求方式有很多种 常用的是GET和POST.
  • GET和POST区别?
    • GET:请求参数会显示到地址栏.GET方式有大小的限制.GET方式没有请求体
    • POST:请求参数不会显示到地址栏.在请求体中.POST没有大小限制.POST方式有请求体.
  • 只有表单设置为method=”post”才是post请求.其他的都是get请求

请求头
请求头通常都是key:value的键值对的形式.一般情况下一个key对应一个value但也有一个key对应多个value的情况.

  • Referer :网站的来源.防盗链.
  • User-Agent :获得客户端浏览器的信息.(文件下载:IE:URL编码 火狐Base64)
  • If-Modified-Since :和响应中一个头一起使用 完成本地缓存的查找.

请求体
POST方式 提交的请求参数

常用请求头信息:

请求头
Accept: text/html,image/*       --支持数据类型
Accept-Charset: ISO-8859-1  --字符集
Accept-Encoding: gzip       --支持压缩
Accept-Language:zh-cn       --语言环境
Host: www.itcast.cn:80      --访问主机
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT      --缓存文件的最后修改时间
Referer: http://www.itcast.com/index.jsp     --来自哪个页面、防盗链
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Cookie
Connection: close/Keep-Alive    --链接状态
Date: Tue, 11 Jul 2000 18:23:51 GMT --时间

2. HTTP协议的响应部分

服务器向客户端发送的数据的格式:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"147-1455670867735"
Last-Modified: Wed, 17 Feb 2016 01:01:07 GMT
Content-Type: text/html
Content-Length: 147
Date: Wed, 17 Feb 2016 01:17:06 GMT
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>鎻愪氦鎴愬姛</h1>
</body>
</html>

响应行
协议版本 状态码 状态码描述
200:响应成功
302:重定向
304:查找本地缓存
404:浏览资源不存在.
500:服务器内部错误.

响应头
一个key对应一个value,也有一个key对应多个value的头.
Last-Modified :最后的修改文件的事件.与If-Modified-Since一起使用.
Refresh :定时刷新.
Location :重定向的路径.
Content-Disposition:文件下载的时候使用的头信息.
禁用浏览器缓存:
Pragma
Expires
Cache-Control

响应体
页面要显示的内容.

常用响应头信息:

响应头
Location: http://www.it315.org/index.jsp    --跳转方向
Server:apache tomcat            --服务器型号
Content-Encoding: gzip          --数据压缩
Content-Length: 80          --数据长度
Content-Language: zh-cn         --语言环境
Content-Type: text/html; charset=GB2312         --数据类型
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT    --最后修改时间
Refresh: 1;url=http://www.it315.org     --定时刷新
Content-Disposition: attachment; filename=aaa.zip   --下载
Set-Cookie:SS=Q0=5Lb_nQ; path=/search
Expires: -1                 --缓存
Cache-Control: no-cache             --缓存
Pragma: no-cache                --缓存
Connection: close/Keep-Alive            --连接
Date: Tue, 11 Jul 2000 18:23:51 GMT

二、Servlet

1.概述

运行在服务器端的一小的Java程序,接收和响应从客户端发送请求.
Servlet的作用:
处理客户端的请求,并且对请求作出响应.


servlet.png

a. 客户端发送请求至服务器端;
b. 服务器将请求信息发送至 Servlet;
c. Servlet 生成响应内容并将其传给服务器。响应内容动态生成,通常取决于客户端的请求;
d. 服务器将响应返回给客户端。

一个 Servlet 就是 Java语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然 Servlet 可以对任何类型的请求产生响应,但通常只用来扩展 Web 服务器的应用程序。

我们通过tomcat上提供的案例来看下servlet的写法:
如图:


servlets.png

启动tomcat服务器,访问examples工程,查看其下的servlets。


servlets2.png

点击Servlets examples,我们可以看到下图


Servlets examples.png

查看一个示例,例如Hello World这个示例。


Hello World.png

这是Hello World这个示例的源代码,我们可以很清楚的知道,servlet其实就是一个java类,这个类继承了HttpServlet,注意,HttpServlet是java servlet api下的一个类,它不在我们的jdk中,所以使用时我们需要单独导入这个jar包,我们可以在tomcat中的lib下找到这个包.
路径: ***\apache-tomcat-7.0.42\lib\servlet-api.jar

那么我们在浏览器上输入一个路径后,怎样就可以访问到这个servlet呢?
我们来查看examples这个工程的配置文件web.xml,在这个文件中有下面这段内容:

    <servlet>
        <servlet-name>HelloWorldExample</servlet-name>
        <servlet-class>HelloWorldExample</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloWorldExample</servlet-name>
    <url-pattern>/servlets/servlet/HelloWorldExample</url-pattern>
 </servlet-mapping>

其实这段内容就是对我们访问的HelloWorld这个servlet在tomcat服务器上的一个配置路径,简单说,当我们访问 http://localhost:8080/examples/servlets/servlet/HelloWorldExample时,就会通过<url-pattern>映射的路径查找到对应的servlet 类。

对于一个servlet来说,我们要创建它的步骤如下:
1.创建一个类,继承HttpServlet
2.重写doGet(和doPost)方法
3.在web.xml文件中配置servlet。

2. Servlet体系结构与api详解

Javax.servlet.http.HttpServlet类,是一个抽象类,它的作用是提供将要被子类以创建适用于web 站点的Http servlet的抽象类。而对于HttpServlet的子类,一般需要重写以下方法。
•doGet,如果 servlet 支持 HTTP GET 请求
•doPost,用于 HTTP POST 请求
•init 和 destroy,用于管理 servlet 的生命周期内保存的资源
•getServletInfo,servlet 使用它提供有关其自身的信息
我们在操作中一般重写doPost或doGet方法就可以。

  • GenericServletd类
    这个类是HttpServlet的父类,它也是一个抽象类,它主要是处理一般的,与协议无关的servlet,如果,要编写关于http协议请使用HttpServlet。
    对于我们创建一个servlet,也可以直接继承GenericServlet,虽然这种方式不常见,但也是创建servlet的一种方式。
    对于GenericServlet,它实现了一个Servlet接口,这个接口定义了所有的servlet都必须实现的方法。

  • Servlet接口
    定义所有 servlet 都必须实现的方法。
    servlet 是运行在 Web 服务器中的小型 Java 程序。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。
    要实现此接口,可以编写一个扩展 javax.servlet.GenericServlet 的一般 servlet,或者编写一个扩展 javax.servlet.http.HttpServlet 的 HTTP servlet。
    此接口定义了初始化 servlet 的方法、为请求提供服务的方法和从服务器移除 servlet 的方法。这些方法称为生命周期方法,它们是按以下顺序调用的:
    1.构造 servlet,然后使用 init 方法将其初始化。
    2.处理来自客户端的对 service 方法的所有调用。
    3.从服务中取出 servlet,然后使用 destroy 方法销毁它,最后进行垃圾回收并终止它。

除了生命周期方法之外,此接口还提供了 getServletConfig 方法和 getServletInfo 方法,servlet 可使用前一种方法获得任何启动信息,而后一种方法允许 servlet 返回有关其自身的基本信息,比如作者、版本和版权。
接下来,我们通过一个图来将上述内容总结一下:


servlet体系结构.png

3. Servlet的生命周期

加载和实例化 Servlet。这项操作一般是动态执行的。然而,Server 通常会提供一个管理的选项,用于在 服务器启动时强制装载和初始化特定的 Servlet。

  1. Server 创建一个 Servlet的实例
  2. 第一个客户端的请求到达服务器
  3. 服务器调用 Servlet 的 init() 方法(可配置为 服务器 创建 Servlet 实例时调用,在 web.xml 中 <servlet> 标签下配置 <load-on-startup> 标签,配置的值为整型,值越小 Servlet 的启动优先级越高),一个客户端的请求到达 服务器, 服务器实例化一个 Servlet的实例
  4. 服务器创建一个请求对象,处理客户端请求
  5. 服务器创建一个响应对象,响应客户端请求
  6. 服务器激活 Servlet 的 service() 方法,传递请求和响应对象作为参数
    service() 方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息
  7. service() 方法使用响应对象的方法,将响应传回Server,最终到达客户端。service()方法可能激活其它方法以处理请求,如 doGet() 或 doPost() 或程序员自己开发的新的方法。

对于更多的客户端请求,服务器创建新的请求和响应对象,仍然激活此 Servlet 的 service() 方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用 init() 方法。一般 Servlet 只初始化一次(只有一个对象),当 服务器不再需要 Servlet 时(一般当 服务器关闭时),服务器 调用 Servlet 的 destroy() 方法。
简单描述如下:
1.客户端请求该 Servlet;
2.加载 Servlet 类到内存;
3.实例化、初始化该 Servlet;
4.init() 初始化参数;
5.service()(doGet() 或者 doPost());
6.destroy()。

示例代码如下:
servlet生命周期1.png

web.xml中配置:

<servlet>
        <servlet-name>LifeServlet</servlet-name>
        <servlet-class>com.yzy.loginservlet.servlet.LifeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LifeServlet</servlet-name>
        <url-pattern>/life</url-pattern>
    </servlet-mapping>

注意:我们在重写init方法是,不需要重写带参数的,只需要重写无参数的init方法就可以。
原因: 在GenericServlet中已经将有参数的init方法重写,并调用了一个无参数的init,所以我们在重写时,不需要在重写有参数init方法


servlet生命周期3.png

客户端第一次访问该Servlet的时候才会创建一个Servlet的对象,那么Servlet中的init方法就会执行.任何一次从客户端发送的请求,那么服务器创建一个新的线程执行Servlet中service方法为这次请求服务.
service方法的内部根据请求的方式的不同调用不同doXXX的方法.当Servlet从服务器中移除或者关闭服务器的时候Servlet对象就会被销毁.destroy的方法就会执行.

Servlet的接口的实现:

Servlet         接口
    |
GenericServlet  通用的Servlet
    |
HttpServlet     HttpServlet

配置Servlet的启动时加载
在web.xml中<servlet>标签中配置
<load-on-startup>2</load-on-startup>

4. 配置url-pattern

我们在创建servlet后,如果想要这个servlet可以被我们访问到,必须在web.xml文件中对其进行配置。
在其中有一个<url-pattern>这个标签是用于确定我们访问一个servlet的路径,接下来,我们详细介绍一下关于这个标签的配置
<url-pattern>它是用于确定我们访问一个servlet的路径.
一个servlet可以被不同的路径映射,换句话说多个不同配置可以映射同一个servlet.我们可以通过下面的示例来说明上面的问题

 <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.yzy.loginservlet.servlet.LoginServlet</servlet-class>
        <!--<load-on-startup>2</load-on-startup>-->
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login2</url-pattern>
    </servlet-mapping>

上面是关于LifeServlet的配置,大家发现我们对于LoginServlet它有两个<servlet-mapping>与其映射,那么这时当我们访问
http://localhost:8080/login
http://localhost:8080/login2
时都可以访问到LgoinServlet.

那么对于<url-pattern>我们在值的写法到底应该怎样处理哪?
对于<url-pattern>我们在开发中它的写法有以下几种:

【完全路径匹配】
* 以/开头  如:/aaa /aaa/bbb
【目录匹配】
* 以/开头 以*结尾  如:/* /aaa/* /aaa/bbb/*
【扩展名匹配】
* 不能以/开始的 需要以*开始   如:*.jsp  *.do  *.action

优先级:完全路径匹配 > 目录匹配 > 扩展名匹配

我们现在查看几个例子,我们找到tomcat/conf/web.xml ,在这个文件中配置的所有内容,其实是被我们自己的工程中的web.xml文件继承了,在这个配置文件中有以下几段内容:


tomcat-web.xml.png

对于这段配置,只要访问时后缀名是jsp或jspx就会执行名称叫jsp的servlet内容,这是一个很典型的扩展名匹配效果
在tomcat/conf/web.xml中还有这样一段配置


tomcat-web.xml2.png

注意:这时<url-pattern>它的值就是”/”那么这时它就是一个默认(缺省)的servlet.。
默认的servlet其作用是用于处理其它的servlet处理不了的请求。

5. load-on-startup

上面我们提到过<load-on-startup>,它可以让我们在服务器实例化servlet时就调用这个servlet,简单说就是可以让一个servlet可以在服务器启动时,就加载这个servlet。
我们以LoginServlet类为例来说明一下:

 <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.yzy.loginservlet.servlet.LoginServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

我们在<servlet>标签中添加了一个<load-on-startup>,它的值为2,那么这时LifeServlet就会在服务器启动时,跟随启动。
注意:<load-on-startup>的值越小代表的是优先级越高。

6. 客户端访问servlet路径问题

我们在开发中,经常在页面上通过表单或超连接向服务器发送请求,如果我们访问的是一个servlet,那么这时访问servlet的路径应该如何书写?接下来我们就介绍一下,关于在客户端访问servlet的路径问题.
在介绍之前,我们先看一下,现阶段我们有多少种方式可以访问服务器端的一个资源

  1. 在地址栏上直接输入url
  2. 超连接的方式
  3. 通过表单方式
  4. 通过js的location.href方式
  5. 通过js的window.open()方法

对于以上方式,只有表单提交的方式才可能有POST请求,其它的都是GET请求。

客户端访问servlet的路径问题:

  • 相对路径:不是以 / 开始的路径.
    localhost:8080/WEB/servletDemo5
    localhost:8080/WEB/demo2/demo1.html

  • 绝对路径:
    通常都是以 / 开始的路径.
    带工程名的路径(客户端的路径)
    不带工程名的路径(服务器端路径)

我们使用绝对路径会比较多一些,而在使用绝对路径时,我们主要使用的是不带协议的绝对路径,而带协议的绝对路径只要在访问站外资源时才会使用。对于相对路径,我们需要分析它们的相对关系,所以不建议大家使用。

7. ServletConfig

ServletConfig是javax.servlet包下的一个接口,它是由servlet容器(tomcat)创建,并封装了servlet相关配置信息的对象,并在servlet容器初始化期间传递给了servlet. 通过init(ServletConfig config)方法传递。

关于有参数init(ServletConfig config)与无参数init()方法关系:
有参数的init方法,是servlet接口中定义的。
而无参数的init方法是GenericServlet中定义的。
在GenericServlet中重写了有参数的init方法,并在其中调用了无参数的init方法。
那么,我们在创建一个servlet时,如果是继承了HttpServlet,在重写init方法时,就可以只重写无参数init就可以。

在web.xml文件中我们可以对servlet进行配置,在<servlet>配置标签内可以有以下这样的子标签

        <init-param>
            <param-name>参数名称</param-name>
            <param-value>参数值</param-value>
         </init-param>

这就是用于声明servlet的初始化参数

这个对象可以获得Servlet的配置信息:

  1. 获取当前servlet的名称 getServletName()
  2. 获取当前servlet的初始化参数
    getInitParameter()
    getInitParameterNames()
  3. 获取全局管理者 getServletContext()

方法:
String getServletName():获取当前servlet的名称(web.xml配置的servlet-name)
String getInitParameter(String key):通过名称获取指定的参数值,如果不存在,返回null
Enumeration getInitParameterNames() :获取所有初始化参数的名称,以 String 对象的 Enumeration 的形式返回
初始化参数是放在 web.xml文件
servlet标签下子标签 init-param
getServletContext():获取全局管理者
servletconfig是由服务器创建的,在创建servlet的同时也创建了它,通过servlet的init(ServletConfig config)将config对象传递给servlet,由servlet的getServletConfig方法获取

  • 获取servletConfig
    对于ServletConfig对象,我们在自己的servlet中如果想要获取到,可以通过getServletConfig()对象来获取。这个方法是在javax.servlet.Servlet接口中定义的,在GenericServlet中对getServletConfig()方法进行了实现。在servlet中以过下面代码就可以获取ServletConfig对象。
    ServletConfig config=this.getServletConfig();

8. ServletContext

ServletContext它是javax.servlet.包下的一个接口。

WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。

ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

它代表的是web应用上下文(全局管理者)
一个项目的引用.代表了当前项目.
当项目启动的时候,服务器为每一个web项目创建一个servletcontext对象.
当项目被移除的时候或者服务器关闭的时候servletcontext销毁
作用:
1.获取全局的初始化参数
2.共享资源(xxxAttribute)
3.获取文件资源
4.其他操作

获取servletcontext:
方式1:
getServletConfig().getServletContext()
方式2:
getServletContext()

常用方法:
String getInitParameter(String key):通过名称获取指定的参数值
Enumeration getInitParameterNames() :获取所有的初始化参数名称
在根标签下有一个 context-param子标签 用来存放初始化参数

                <context-param>
                    <param-name>encoding</param-name>
                    <param-value>utf-8</param-value>
                </context-param>

xxxAttribute :存取域对象
String getRealPath(String path):获取文件部署到tomcat上的真实路径(带tomcat路径)
getRealPath("/"):获取文件绝对路径
D:\javaTools\apache-tomcat-7.0.52\webapps\day09
InputStream getResourceAsStream(String path):以流的形式返回一个文件
String getMimeType(String file) 可以获取一个文件的mimeType类型.
URL getResource(String path)它返回的是一个资源的URL
还提供 log(String msg),getRequestDispatcher(String path) 等这样的方法,可以做日志与转向操作

  • servletContext实现servlet共享
    ServletContext对于一个web应用只有一个,所有的servlet使用的就是同一个ServletContext。
    ServletContext提供以下方法用于在域中进行数据操作
    Object getAttribute(String name)
    返回具有给定名称的 servlet 容器属性,如果不具有该名称的属性,则返回 null。
    void setAttribute(String name,Object object)
    将对象绑定到此 servlet 上下文中的给定属性名称。如果已将指定名称用于某个属性,则此方法将使用新属性替换具有该名称的属性。
    void removeAttribute(String name)
    从 servlet 上下文中移除具有给定名称的属性。

应用场景:统计访问站点的人数

三、Classpath

java project----所有class都在bin目录下
web project-----所有的class都在classes目录下

  1. Class获取
    Class.getResource("/").getPath();获取classes目录的绝对磁盘路径
    Class.getResource("").getPath();获取的是当前Class对象代表的类所在的包的路径。

  2. ClassLoader获取
    Class.getClassLoader().getResource("/").getPath();
    获取的是classes目录的绝对磁盘路径
    Class.getClassLoader().getResource("").getPath();
    获取的是classes目录的绝对磁盘路径

这两个getResource()是使用当前ClassLoader加载资源(即资源在 Class path中),这样资源和class直接打在jar包中,避免文件路径问题.
两者不同是Class的getResource()方法是从当前.class 文件路径查找资源,ClassLoader则是从jar包根目录查找.。

简单归纳:
通过类加载器获取文件的路径(处于classes目录下的文件)
类.class.getClassLoader().getReource("文件路径").getPath()
类.class.getClassLoader().getReourceAsStream("文件路径")

四、实例:完成系统的登录功能,登录后页面定时跳转,并记录系统被访问多少次

技术要点:

  • 页面跳转所用技术
    【Refresh的响应头】
    HttpServletResponse的操作响应头的方法:
* addHeader(String name,String value); --针对一个key对应多个value头的设置
    * addDateHeader(String name,long value);
    * addIntHeader(String name,int value);
* setHeader(String name,String value); --针对一个key对应一个value
    * setDateHeader(String name,long value);
    * setIntHeader(String name,int value);

简单实例:

response.setContentType("text/html;charset=UTF-8");
response.getWriter().println("<h1>登录成功!页面将在5秒后跳转</h1>");
response.setHeader("Refresh", "5;url=/WEB09/loginSucc.html");

另一种方法,在jsp页面设置定时跳转:

可以通过html页面中的一个标签设置头信息<meta>标签.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Refresh" content="5;url=/WEB09/succ.html">
<script type="text/javascript" src="/WEB09/js/jquery-1.11.3.min.js"></script>
<title>Insert title here</title>
<script type="text/javascript">
    $(function(){
        setInterval("changeTime()", 1000);
    });
    var i = 5;
    function changeTime(){
        // 获得id为s1的元素:
        i--;
        $("#s1").html(i);
    }
</script>
</head>
<body>
<h1>登录成功!!!页面将在<span id="s1">5</span>秒后跳转!</h1>
</body>
</html>
  • 统计访问次数用到的技术:
    【ServletContext*****】
    ServletContext :Servlet中全部的内容ServletContext都了解.一个WEB应用只有一个ServletContext对象.服务器启动的时候,服务器为每个WEB工程创建一个属于自己项目的ServletContext对象.服务器关闭的时候或者项目从服务器中移除ServletContext才会被销毁.如果将值保存在ServletContext中.值就有一个作用的范围.所以这个对象称为”域对象”.

  • 保存全局性信息和数据:

    • 网站的访问次数:
    • 聊天室:
  • 在Servlet中获得ServletContext:

    • ServletContext getServletContext();
  • 操作这个对象的方法:

    • void setAttribute(String name,Object value);
    • Object getAttribute(String name);
    • void removeAttribute(String name);

简单实现:

  1. 编写一个Servlet中的init方法.在init方法中初始化一个被登录次数0.将这个值存入到ServletContext域中.配置Servlet的load-on-startup.
  2. 在登录成功代码中获得原来的次数+1.存回到ServletContext域中.
  3. 在5秒后跳转的Servlet中,从ServletContext域中获得次数,并且显示到页面上.
配置SErvlet为启动时加载:
  <servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.itheima.servlet.demo2.UserServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/userServlet</url-pattern>
  </servlet-mapping>

在Servlet的init方法中初始化次数为0
    @Override
    public void init() throws ServletException {
        // super.init();
        // 获得ServletContext对象.初始化一个值为0.
        ServletContext servletContext = this.getServletContext();
        servletContext.setAttribute("count", 0);
    }

在登录成功的代码中获得原来的次数并且+1,存回到SErvletContext域中。
// 登录成功的时候 获得原来的次数 + 1
                Integer count = (Integer) this.getServletContext().getAttribute("count");
                // 存回到ServletContext域中
                this.getServletContext().setAttribute("count", ++count);

在CountServlet中获得次数并且显示:
response.setContentType("text/html;charset=UTF-8");
        // 获得次数:
        Integer count = (Integer) this.getServletContext().getAttribute("count");
        response.getWriter().println("<h1>您是第"+count+"位登录成功的用户!</h1>");

案例代码实现:
步骤一:设计一个登录页面.

登陆页面

image.png

注意:如果html页面改成jsp页面需要在顶部加上
<%@ page pageEncoding="utf-8"%>
然后保存,否则会有乱码错误。
注意修改登录页面对应的name属性值
image.png

如果想使用el表达式,首先需要导入jstl的jar包:

 <!-- https://mvnrepository.com/artifact/jstl/jstl -->
      <dependency>
          <groupId>jstl</groupId>
          <artifactId>jstl</artifactId>
          <version>1.2</version>
      </dependency>

然后在相应的页面导入jstl标签:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
使用el表达式显示次数:

image.png

步骤二:在登录页面上点击登录按钮.提交到Servlet.
修改form表单的action和method属性值:
action地址和web.xml里面的<servlet-mapping>下的地址对应。

image.png

另需要与数据库进行交互,注意导包:

</dependency>
      <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.30</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
      <dependency>
          <groupId>com.mchange</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.5.2</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
      <dependency>
          <groupId>com.mchange</groupId>
          <artifactId>mchange-commons-java</artifactId>
          <version>0.2.11</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
      <dependency>
          <groupId>commons-dbutils</groupId>
          <artifactId>commons-dbutils</artifactId>
          <version>1.6</version>
      </dependency>

步骤三:后端核心代码

Servlet类

package com.yzy.loginservlet.servlet;


import com.yzy.loginservlet.domain.User;
import com.yzy.loginservlet.service.UserService;

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

public class LoginServlet extends HttpServlet {
    @Override
    //初始化登录次数
    public void init() throws ServletException {
        //获取全局管理者
        ServletContext context = getServletContext();

        //初始化次数
        context.setAttribute("count", 0);

        System.out.println("初始化次数成功");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //0.设置编码
        resp.setContentType("text/html;charset=utf-8");
        req.setCharacterEncoding("utf-8");

        //1.接受用户名和密码
        String username=req.getParameter("name");
        String password=req.getParameter("password");
        System.out.println(username);

        //2.调用userservice 里的login(username,password) 返回值:User user
        User user = null;
        try {
            user = new UserService().login(username,password);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("网络异常,请稍后再试!");
        }

        //3.判断user是否为空
        if(user==null){
            //3.1若为空 写"用户名和密码不匹配"
            resp.getWriter().print("用户名和密码不匹配,3秒之后跳转");
            //案例2-定时跳转
            resp.setHeader("refresh", "3;url=/login.jsp");

        }else{
            //3.2若不为空 写"xxx:欢迎回来"
            resp.getWriter().print(user.getUname()+":欢迎回来");
            resp.setHeader("refresh", "1;url=/index.jsp");
            //4.获取全局管理者
            ServletContext context = this.getServletContext();

            //5.获取总次数
            Integer cishu = (Integer) context.getAttribute("count");

            //6.将次数+1
            cishu++;

            //7.将次数再次放入域对象中
            context.setAttribute("count", cishu);
            System.out.println(cishu);

        }
    }
}

DAO层代码:

package com.yzy.loginservlet.dao;

import java.sql.SQLException;

import com.yzy.loginservlet.domain.User;
import com.yzy.loginservlet.utils.DataSourceUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;


public class UserDao {

    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     * @return 用户
     * @throws SQLException 
     */
    public User getUserByUsernameAndPwd(String username, String password) throws SQLException {
        //创建queryrunner
        QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource());
        
        //编写sql
        String sql="select * from userinfo where uname = ? and upsw = ?";
        
        //执行sql
        User user = qr.query(sql, new BeanHandler<>(User.class), username,password);
        System.out.println(user);
        return user;
    }

}

web.xml代码:

<?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">
    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.yzy.loginservlet.servlet.LoginServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
</web-app>

效果:


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容