一 web引言
1.1 生活中的上网方式
两种方式:可以通过浏览器(browser)进行上网,也可以通过客户端(client)进行上网
两种结构
BS结构 browser server 浏览器服务器
CS结构 client server 客户端服务器
BS结构 browser server 浏览器服务器
- 不需要安装客户端,只要能连上网,就能随时随地使用
- 开发人员只需要对服务器端程序进行开发、维护,降低开发维护难度和开发维护成本
- 浏览器主要负责用户界面的动态展示,只处理一些简单的逻辑功能
- 所有具体业务逻辑的处理都由服务器端程序完成,所以程序负载几乎都转移给服务器
端。
- 但是随着服务器负载的增加,可以平滑地增加服务器的个数并建立集群服务器系统,然
后在各个服务器之间做负载均衡。
CS结构 client server 客户端服务器
- 将应用程序分为客户端和服务器端两层,客户端程序用于展示功能,为用户提供操作界
面,同时也可以进行业务逻辑的处理;而服务器端程序负责操作数据库完成数据处理等
核心业务
- 由此可见通过C/S开发模型开发的应用程序,客户端程序可以承担一部分业务逻辑处
理,特别是数据的预处理工作,减轻了服务器端程序的压力
BS优缺点:
优点:实时地更新数据(新功能的增加只需要在服务端完成, 浏览器刷新就好了),
缺点:将负载给了服务器
CS优缺点:
优点:客户端也分担了一部分负载,
缺点:如果有新的功能要增加必须要重新下载客户端
------------------------------
1.2 Web服务器
作用:能够让本地电脑中的资源可以被其他的电脑访问
常见的javaweb服务器
weblogic
它是oracle公司的,已经被oracle收购,它是全面支持javaee规范,收费的大型的web
服务器,它是企业中主流的服务器,在网络上学习资料比较多。
websphere
它是ibm公司的一个大型的收费的全面支持javaee规范的javaee容器。
tomcat
它是开源的免费的servlet规范的服务器。 它是apache一个web服务器。
jboss
hibernate公司产品,不是开源免费的,是javaee规范的容器。
ngix
Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理服务器
------------------------------
1.3 Web相关概念
什么是web?
Web指的就是网页,我们所说的web指的是internet主机(服务器)上的供外界访问的资源
web资源可以分为两种:静态web资源、动态web资源
静态web资源
指web页面上供人们浏览的数据,它们始终不变。例如html
优点:
静态网站开发简易,只需要掌握HTML、CSS和JS就可以开发
静态访问速度快,因为静态网页不需要和任何程序进行交互,更不需要对数据进行处理
缺点:
静态网站内容无法实时更新,因为网站由一个个的静态HTML网页构成,新增内容只能
通过开发人员修改代码
网站内容过多时,每个页面都需要单独制作,需要不断编写和维护HTML页面,增加了
网站开发人员的工作量,提高了运营费用。
动态web资源
指web页面中内容是由程序产生的,供人们浏览,并且在不同的时间点,数据不一样,并且
还可以实现人与人之间的交互。用到Servlet和JS等技术.
优点
维护方便、可以根据用户需求实现各种功能
查询信息方便,能存储大量数据,需要时能立即查询
网站内容可以实时动态更新
与用户交互性强,提高用户粘性
缺点
技术要求高
二 Tomcat
2.1 Tomcat安装与配置
-
测试是否安装成功:
在tomcat的安装目录下有一个bin目录 ,在目录 中有一个startup.bat文件执行它。打开浏览器,在浏览器的地址栏上输入 http://localhost:8080 -
配置
- JAVA_HOME配置
在tomcat的安装目录bin文件夹下的catalina.bat中使用了JAVA_HOME,所以,安装
tomcat必须要求系统配置中有JAVA_HOME,如果没有配置,执行startup.bat文件时会出现闪退效果 - 端口号配置
tomcat默认使用的8080端口,可以进行端口号的修改,修改tomcat的端口号,在
tomcat/conf/server.xml文件, 可以添加80端口,80是http协议默认的端口。
- JAVA_HOME配置
三 Web项目
3.1Web项目分类
- Web静态项目
包含都是静态资源:html、js、css、图片、音频等等
- Web动态项目: 不是必须包含动态资源
包含都是静态资源:html、js、css、图片、音频等等
那么和静态项目的区别在哪里?可以有动态资源及WEB-INF文件夹
通过http://localhost:8080/ 访问的资源是来自于tomcat服务器的动态web项目(内置),
而在tomcat的一个安装目录中,是由一个webapps的文件夹专门用来部署web项目
------------------------------
3.2 tomcat部署项目
-
方式一:直接将web应用程序放置到webapps目录
直接将一个web应用程序放置在tomcat/webapps目录下。这时web应用程序目录名称就是我们访问tomcat下的这个应用程序的名称 -
方式二:虚拟目录初级版
将一个不在tomcat下的web应用程序部署加载。可以在tomcat/conf/server.xml文件中配置,在server.xml文件中的标签中添加一段配置
<Context docBase="磁盘路径" path="/hello" />
经过以上配置后,在浏览器上可以输入http://localhost/hello来访问
但是,在tomcat6以后已经不建议使用了。
- 方式三:虚拟目录优化版
在tomcat/conf/Catalina/localhost下创建任意名称的一个xml文件,例如创建一个good.xml文件,在good.xml中书写
<Context docBase="磁盘路径" />
这种方案配置,xml文件的名称就是访问路径,在浏览器中访问http://localhost/good
------------------------------
3.3 IDEA配置Tomcat
- 1 选择Edit Configurations
- 2 点击加号 ->tomcat server -> local
- 3 点击Configure -> 点击加号 -> 选择tomcat
------------------------------
3.4 idea中的web项目的内容部署到tomcat中
只有资源来到了day50_war_exploded文件夹中,才意味着部署成功
src文件夹:
可以部署上去!部署到了项目中的\WEB-INF\classes文件夹中
web文件夹:
可以部署上去!部署到了项目目录中!
day50项目下:
不可以部署上去
四 HTTP协议
协议:两个设备进行数据交换的约定!
Http协议:超文本传输协议(hypertext transfer protocl)
超文本:字符、音频、视频、图片等等
基于tcp协议。tomcat服务器底层实现本质上就是TCP(Socket)
4.1 使用抓包来查看http协议信息
经过演示发现,浏览器和服务器,它们之间进行交互,是一个请求-响应模型!!!
请求:
请求行
请求头
请求正文
响应:
响应行
响应头
响应正文
通过抓包可以看到如下信息
General:请求行,响应行
Request Headers:请求头
Response Headers:响应头
响应正文:将显示内容携带到浏览器
请求正文:用来接收请求的参数.
------------------------------
4.2 请求的执行流程
- 发起请求
- 域名解析
本地域名解析器(C:\Windows\System32\drivers\etc\host),
如果本地解析器无法解析,那么就交给互联网上的DNS解析器
得到IP - 根据ip和端口,可以得到一个Socket对象,执行请求
携带请求行、请求头、请求正文 - 服务器响应浏览器
携带响应行、响应头、响应正文
------------------------------
4.3 http请求
请求组成:请求行、请求头、请求正文
- 请求行
Request URL : 请求路径,告诉服务器要请求的资源路径
Request Method : 请求方式 , GET/POST
protocol : http协议版本
---------------
- GET请求和POST请求
get请求只能携带小数据、get请求下的请求参数会直接拼接到Request URL(请求网址)后面,QueryStringParameters
post请求可以携带大数据、post请求下的请求参数会存放到请求正文
请求参数:比如,表单中的输入框中的值.
如果我们要做文件上传,需要用到post请求,文件比较大
---------------
- 请求头
Content-Type:浏览器告诉服务器,请求正文的数据类型
User-Agent:浏览器告诉服务器,我是个什么样的浏览器
---------------
- 请求正文
请求正文,只有当请求方式为post,且有请求参数时才会有请求正文
------------------------------
4.4 Http响应
Http响应组成:响应行、响应头、响应正文
- 响应行
Status Code : 响应状态码
常见的有:
200:服务器响应成功
302: 告诉浏览器,进行重定向
304: 页面上的内容没有发生改变,不需要重新请求服务器
404: 没有对应的服务器资源
500:服务器内部错误!
---------------
- 响应头
Location:告诉浏览器重定向的资源路径,需要结合响应状态码302使用
Content-Type:服务器告诉浏览器,响应正文的数据类型
Content-Type:text/html;charset=utf-8; 服务器告诉浏览器,响应正文是文本和html标签;告诉浏览器,应该以utf-8的形式进行解码!浏览器就会以html标签及utf-8的形式对响应正文进行渲染显示!!!
refresh:定时跳转
Content-Disposition:文件下载
---------------
- 响应正文
浏览器显示的内容
五 Servlet
5.1 Servlet介绍
Servlet是运算在服务器上的一个java程序,简单说,它就是一个java类。我们要使用servlet,需要导入servlet的api.
------------------------------
5.2 服务器编译环境设置
------------------------------
5.3 Servlet的入门
5.3.1 入门案例
package servlet;
//1,自定义类继承HttpServlet
public class Demo01Servlet extends HttpServlet {
//2,重写doGet和doPost方法
//doGet:处理get请求
//doPost:处理post请求
//在doGet方法中调用doPost方法,不管是 get请求 or post请求,都交给doGet方法处理
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理get请求
System.out.println("Demo01Servlet doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理post请求
doGet(req, resp);
}
}
web/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--3,配置Servlet-->
<!--3.1,声明Servlet-->
<servlet>
<!--servlet的名称-->
<servlet-name>Demo01Servlet</servlet-name>
<!--servlet的全类名-->
<servlet-class>com.qfedu.servlet.Demo01Servlet</servlet-class>
</servlet>
<!--
3.2,给Servlet设置访问名称
Servlet should have a mapping
-->
<servlet-mapping>
<!--servlet的名称-->
<servlet-name>Demo01Servlet</servlet-name>
<!--servlet的访问名称-->
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
</web-app>
5.3.2 执行流程
浏览器发起请求: http://localhost:8080/day50/demo01
就会在服务器中找访问名称为demo01的Servlet -> Demo01Servlet
请求的处理就交给了Demo01Servlet的实例,根据请求方式get/post,决定是给doGet还是doPost方法处理
5.3.3 注意事项
不管是get请求还是post请求,对于服务器来说,没差别
get请求将请求参数放到请求网址
post请求将请求参数放到请求正文
服务器最终无非就要获取请求参数。getParameter()方法
------------------------------
5.4 Servlet详解
HttpServlet继承于GenericServlet、GenericServlet实现于Servlet,也就是说Servlet是顶层接口
5.4.1 Servlet处理请求
- Servlet接口下有一个实现类叫GenericServlet,GenericServlet有一个子类HttpServlet.
- 在Servlet接口中定义了一个方法service,它的主要作用是处理来自浏览器的请求操作。在service方法的重载的方法中,对请求方式进行判断,如果是GET就会调用doGet方法,如果是POST就会调用doPost方法。
5.4.2 创建Servlet的三种方式
- 方式一:实现Servlet接口
在servlet接口中,没有doGet和doPost方法,处理请求是service方法(抽象的)
- 方式二:继承GenericServlet类
在GenericServlet类中,没有doGet和doPost方法,处理请求是service方法(抽象的)
- 方式三:继承HttpServlet类
HttpServlet类中重写service方法。
根据源码,发现重写service方法中,
有将ServletRequest强转为HttpServletRequest,
将ServletResponse强转为HttpServletResponse
以上强转是因为,ServletRequest和ServletResponse并没有针对Http协议做优化!!!无法专门针对http协议调用方法!!
HttpServletRequest和HttpServletResponse有针对http协议做优化!!!
在开发中,一般应用比较多的是使用extends HttpServlet,优点是它是与http协议相关
的,封装了http协议相关的操作。
------------------------------
5.5 Servlet生命周期
在javax.servlet.Servlet接口中定义了三个方法init service destroy它们就是servlet的生命周期
方法
public interface Servlet {
//监听Servlet的初始化
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
//处理请求
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
//监听Servlet的销毁
void destroy();
}
- 第一次访问servlet,servlet会被创建,并将servlet对象常驻内存,调用init方法进行初始化操作,init方法中执行一次
- 调用service方法,用于处理来自浏览器端的请求,以后都是开启一个线程来处理浏览器端请求
- 当tomcat服务器正常关闭时,会调用destroy方法将servlet销毁
------------------------------
5.6 load-on-startup配置
根据Servlet生命周期,可知,servlet默认不会随着服务器的启动而初始化
load on startup可以让servlet随着服务器的启动而初始化
对于load-on-startup它的可以配置的值有10个,1代表优先级最高,数值越大,优先级越低
------------------------------
5.7 Servlet配置
5.7.1 自定义Servlet
对于servlet,我们需要在web.xml文件中对其进行配置
- 在web.xml中声明Servlet
<servlet>
<servlet-name>Demo01Servlet</servlet-name>
<servlet-class>servlet.Demo01Servlet</servlet-class>
</servlet>
---------------
- 在web.xml中给Servlet映射访问路径
<servlet-mapping>
<servlet-name>Demo01Servlet</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
一个<servlet>可有多个<servlet-mapping>与其对应
5.7.2 < url-parttern >书写规则
- 完全匹配
要求网址上的访问名称完全和<url-parttern>一致
必须以"/"开头,否则会报错:IllegalArgumentException : Invalid <url-pattern>
---------------
- 目录匹配
要求网址上的访问名称中的目录和<url-parttern>一致
必须以"/"开头,以"*"结尾
比如:/a/b/* , 目录必须是/a/b,后面随便写
---------------
- 后缀名匹配
要求网址上的访问名称中的后缀和<url-parttern>一致
不能以"/"开头,以"*"开头,后缀名根据业务写
比如:*.xyz。后缀名必须是xyz,其他随意写!!!
------------------------------
5.8 缺省Servlet
创建一个servlet时,如果它的url-pattern的配置值为”/”这时这个servlet就是一个缺省的
servlet,tomcat服务器中默认就有缺省Servlet
- 缺省Servlet的作用
凡是在web.xml文件总找不到匹配的<servlet-mapping>元素的URL,它们的请求都将交给
缺省Servlet处理。也就是说,缺省的servlet用于处理其他Servlet处理不了的请求
当访问tomcat服务中的静态资源(html、图片等等)时,实际上是在访问这个缺省的servlet
- 自定义缺省Servlet
在当前工程中自定义Servlet,将url-parttern设置为"/",就覆盖了tomcat容器中的缺省Servlet
- 应用
SpringMVC框架中,用于放行静态资源
------------------------------
5.9 服务器中路径
- 带协议的绝对路径
http://localhost:8080/day51/img/girl.jpg
- 不带协议的绝对路径
/day51/img/girl.jpg
- 相对路径
当前目录:./ ,可以省略
上一级目录:../
------------------------------
5.10 ServletConfig对象
ServletConfig是javax.servlet.包下的一个接口。ServletConfig它是Servlet的一个配置对象
ServletConfig对象是由服务器创建的,它是通过Servlet的init方法传递到Servlet中
- 作用
获取Servlet名称 getServletName
获取Servlet初始化参数 getInitParameter getInitParameterNames
获取ServletContext对象
getInitParameter(String parameterName):根据参数名称获取指定的参数值
getInitParameterNames():获取所有的参数名称
获取域对象:ServletContext
public class Demo11Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
String servletName = servletConfig.getServletName();
System.out.println(servletName+"正在运行...");
System.out.println("--------------------");
String username = servletConfig.getInitParameter("username");
System.out.println(username);
String password = servletConfig.getInitParameter("password");
System.out.println("--------------------");
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = servletConfig.getInitParameter(parameterName);
System.out.println("name"+parameterName+"password"+parameterValue);
}
ServletContext servletContext = getServletContext();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
<servlet>
<servlet-name>Demo11Servlet</servlet-name>
<servlet-class>servlet.Demo11Servlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Demo11Servlet</servlet-name>
<url-pattern>/demo11</url-pattern>
</servlet-mapping>
------------------------------
5.11 ServletContext对象
相当于是整个应用程序对象
5.11.1 介绍
- ServletContext它是javax.servlet包下的一个接口
- 当服务器启动时,会为服务器中的每一个web应用程序创建一个ServletContext对象,一个ServletContext对象对应的就是一个web应用程序
- 对于ServletContext,我们叫它上下文对象,ServletConfig对象中维护了ServletContext对象,也就是说,我们可以通过ServletConfig对象来获取ServletContext对象
- 在web应用中的servlet要想实现资源的共享,可以通过ServletContext来完成,
ServletContext也叫做域对象
5.11.2 ServletContext对象作用
- 实现Servlet资源共享
ServletContext是一个域对象,可以用来存储数据.可以将它想像成一个Map<String,Object>,可以通过它实现Servlet资源共享
在应用程序中的任何位置都能够访问
* getAttribute(String parameterName) : 获取ServletContext域中指定名称的参数值
* setAttribute(String paramterName,Object parameterValue):存储参数到ServletContext域中
* removeAttribute(String parameterNam):将ServletContext域中指定名称的参数移除
---------------
- 获取全局初始化参数
在web.xml中配置的全局初始化参数,可以通过ServletContext对象获取
public class Demo15Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Enumeration<String> parameterNames = servletContext.getInitParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = servletContext.getInitParameter(parameterName);
System.out.println("name:"+parameterName+"value:"+parameterValue);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<servlet>
<servlet-name>Demo15Servlet</servlet-name>
<servlet-class>servlet.Demo15Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo15Servlet</servlet-name>
<url-pattern>/demo15</url-pattern>
</servlet-mapping>
---------------
- 获取资源在服务器上的真实磁盘路径
getRealPath:依据当前项目去生成真实磁盘路径
servletContext.getRealPath("upload"):当前项目的服务器磁盘路径/upload
servletContext.getRealPath("upload/img"):当前项目的服务器磁盘路径/upload/img
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
String upload = servletContext.getRealPath("upload");
System.out.println(upload);
}
------------------------------
5.12 ServletContext综合案例
- 需求:统计站点访问次数
public class Demo17Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取ServletContext对象
ServletContext servletContext = getServletContext();
//2 判断是否第一次
Integer count = (Integer) servletContext.getAttribute("count");
if (count == null) {
//2.1 第一次访问
count = 1;
System.out.println(count);
servletContext.setAttribute("count", count);
} else {
//2.2 非第一次访问
count++;
System.out.println(count);
servletContext.setAttribute("count", count);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
------------------------------
5.13 Servlet3.0
支持注解开发,由注解配置来代替web.xml配置
- 在Servlet类上直接使用@WebServlet注解
@WebServlet(name = "Demo01Servlet", urlPatterns = {"/demo01", "/mydemo01"},
initParams = {@WebInitParam(name = "username", value = "root"),
@WebInitParam(name = "password", value = "123456")})
---------------
- 常用属性
name:String:设置Servlet名称
urlPatterns:String[]:设置Servlet的访问路径
loadOnStartup:int:设置Servlet的load-on-startup属性
initParams:WebInitParam[]:设置Servlet的初始化参数
六 request与response对象
6.1 介绍
当浏览器发起请求后,服务器会创建一个请求对象、一个响应对象,通过service方法传入给Serlvet
request与response的作用
* request对象就可以操作http请求信息
* response对象就可以操作http响应信息
ServletRequest与HttpServletRequest及ServletResponse与HttpServletResponse关系
* ServletRequest与ServletResponse它们是HttpServletRequest与HttpServletResponse的
父接口
------------------------------
6.2 response对象操作响应行、响应头
操作响应行
* setStatus(int status):操作正常响应状态码,比如:200、302
* ssendError(int status):操作错误响应状态码,比如: 404
操作响应头
* public void addHeader(String name, String value):直接覆盖响应头原有值
* public void setHeader(String name, String value):在响应头原有值的后面追加
------------------------------
6.3 response操作重定向
通过各种方法将各种网络请求重新定个方向转到其它位置
通过操作状态码302及响应头location来实现
重定向的流程
* 当浏览器访问一个资源Demo03Servlet,访问名称为“/demo03”
* Demo03Servlet进行重定向
* 操作响应状态码302
* 操作响应头Location,服务器告诉浏览器,重定向的资源的访问路径
* 浏览器进行一个新的请求,完成重定向
- 方式一
response.setStatus(302);
response.setHeader("Location","demo04");
---------------
- 方式二
response.sendRedirect("demo04");
------------------------------
6.4 response操作定时跳转
一个资源定时一段时间之后,跳转到另外一个资源
操作响应头refresh
resp.setHeader("refresh","5;url=demo07");
------------------------------
6.5 response操作响应正文
响应正文:就是浏览器显示的主体内容
response.getWriter().write():操作响应正文
响应正文中文乱码
* 本质原因,服务器的编码和浏览器的解码,格式不同!
* setCharacterEncoding("utf-8"):
* 告诉服务器,应该以utf-8格式编码响应正文
* setHeader("Content-Type","text/html;charset=utf-8"):
* 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
* setContentType("text/html;charset=utf-8")
* 告诉服务器,应该以utf-8格式编码响应正文
* 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//告诉服务器,编码格式为utf-8
// response.setCharacterEncoding("utf-8");
//单单只告诉服务器的编码格式,不合适的!浏览器的编码格式不确定!
//告诉浏览器,编码格式也应该是utf-8
// response.setHeader("Content-Type","text/html;charset=utf-8");
response.setContentType("text/html;charset=utf-8");
String msg1 = "hello world 老邱";
PrintWriter writer = response.getWriter();
//操作响应正文
writer.write(msg1);
//操作响应正文中文乱码
//不仅要告诉服务器编码格式,还要告诉浏览器编码格式! 它们应该是统一的!
//有没有一个简单,就是既告诉服务器的编码,同时也告诉浏览器的编码
}
------------------------------
6.6 request操作请求行
getRequestURI 获取请求路径
getMethod 获取请求方式
getRemoteAddr 获取请求ip
getLocalPort 获取请求端口
getQueryString 请求网址"?"后面的路径
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo02Servlet");
//获取请求路径
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//获取请求方式
String method = request.getMethod();
System.out.println(method);
//获取请求ip
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
//获取请求端口
int port = request.getLocalPort();
System.out.println(port);
//获取请求网址后的请求参数
String queryString = request.getQueryString();
//在网址后的请求参数,如果要使用,需要做字符串的拆分!!
System.out.println(queryString);
//SpringMVC:前后端交互使用的是json字符串,json解析 --> java对象!!
}
------------------------------
6.7 request操作请求头
getHeader() 获取指定请求头的值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo03Servlet");
//获取User-Agent请求头:判定请求是由哪种浏览器发起
String userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);
}
------------------------------
6.8 request操作请求参数
请求正文:post+请求参数
请求参数:不管是get请求 还是 post请求
getParameter 获取指定请求参数值
getParameterNames 获取所有请求参数名称
getParameterValues(String parameterName) 获取指定请求参数所有值
getParameterMap 键,获取所有请求参数名称 , 相当于getParameterNames方法
值,获取指定请求参数所有值,相当于getParameterValues方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取指定参数值
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username : " + username + " , password : " + password);
System.out.println("---------------");
//获取所有请求参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = request.getParameter(parameterName);
System.out.println("name : " + parameterName + " , value : " + parameterValue);
}
System.out.println("---------------");
//获取指定请求参数所有值
String[] usernames = request.getParameterValues("username");
System.out.println(usernames[0]);
String[] hobbys = request.getParameterValues("hobbys");
for (String hobby : hobbys) {
System.out.println(hobby);
}
System.out.println("---------------");
//获取请求参数对应的map :
//getParameterMap() -> Map(String,String[])
//键:请求参数名称 相当于 getParameterNames
//值:一组请求参数值 相当于 getParameterValues
Map<String, String[]> parameterMap = request.getParameterMap();
//双列集合:获取到所有的实体对象(键值对象)
Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
//键 - 请求参数名称
String parameterName = entry.getKey();
//值 - 一组请求参数值
String[] values = entry.getValue();
StringBuffer valueStr = new StringBuffer();
for (String value : values) {
valueStr.append(value + " ");
}
System.out.println("参数名称 : "+ parameterName + " , 参数值 : " + valueStr);
}
}
------------------------------
6.9 请求参数中文乱码
6.9.1 post
本质:请求正文中的中文参数乱码
解决方案
* request.setCharacterEncoding("utf-8")
* 告诉服务器应该以utf-8解码请求正文
* 逆向,先编码在解码
* 先将乱码字符串以iso8859-1编码成字节
* 将字节以utf-8解码成字符串
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//不管是get请求 还是 post请求,都存在请求参数中文乱码问题
//post请求 告诉服务器,应该以utf-8来解码请求正文
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"),"utf-8");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
将tomcat容器的URIEncoding=“utf-8”,对Query String中的请求参数有效,对请求正文中无效,对post请求下的中文乱码无效
---------------
6.9.2 get
本质:Query String中的中文参数乱码
解决方案
* 逆向,先编码在解码
* 先将乱码字符串以iso8859-1编码成字节
* 将字节以utf-8解码成字符串
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//不管是get请求 还是 post请求,都存在请求参数中文乱码问题
//告诉服务器,应该以utf-8对请求正文进行解码
//get请求中,有请求正文么? 没有!
// request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
//将乱码的字符串编码(iso8859-1)成字节
// byte[] bytes = username.getBytes("iso8859-1");
//将字节解码(utf-8)成字符串
// String newUsername = new String(bytes,"utf-8");
username = new String(username.getBytes("iso8859-1"),"utf-8");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
}
修改tomcat容器的URIEncoding=“utf-8”
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="utf-8"
redirectPort="8443" />
request.setCharacterEncoding(“utf-8”)对get请求无效,告诉服务器应该以utf-8来解码请求正文,跟Query String 没有关系
---------------
6.9.3 终极解决方案
方案
* tomcat8.5
* 相当于是tomcat7.0修改了URIEncoding="utf-8"
* 就解决了get请求参数中文乱码问题
* request.setCharacterEncoding("utf-8")
* 就解决了post请求参数中文乱码问题
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
}
------------------------------
6.10 request操作请求转发
- .request的生命周期
- Request对象是在浏览器向服务器发送请求时,服务器会创建request对象,当服务器产生了响应时,request对象就销毁。简单说就是请求时创建request,响应时销毁request。
- Request对象是在浏览器向服务器发送请求时,服务器会创建request对象,当服务器产生了响应时,request对象就销毁。简单说就是请求时创建request,响应时销毁request。
- 请求转发
- 请求转发指的就是服务器内的资源的跳转,请求转发时,就会形成一个请求链,它们共享同一个request与response对象。
- 请求转发指的就是服务器内的资源的跳转,请求转发时,就会形成一个请求链,它们共享同一个request与response对象。
- 请求转发的实现
- request.getRequestDispathcher(“路径”).forward(request,response);
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo08Servlet");
//转发到Demo09Servlet , 重定向 : 操作响应状态码302,操作响应头location 都是通过response
//获取一个转发器对象,传入一个转发地址
RequestDispatcher dispatcher = request.getRequestDispatcher("demo09");
//开始转发
dispatcher.forward(request,response);
}
------------------------------
6.11 请求转发和重定向
请求次数
* 重定向有2次请求
* 转发有1次请求
跳转区别
* 重定向既可以站内资源进行跳转,站外资源也可以进行跳转
* 转发只能站内资源进行跳转
路径区别
* 要转发的资源的相对路径无区别
* 要转发的资源的绝对路径有区别
* 重定向,是从先从项目开始找,再找资源
* 转发,是直接从项目中找资源
/**
* 重定向和转发的区别之站内资源、站外资源
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//重定向到站外资源
response.sendRedirect("http://www.baidu.com/");
//转发到站外资源
request.getRequestDispatcher("http://www.baidu.com/").forward(request,response);
}
/**
* 重定向和转发的区别之路径
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//重定向和转发,使用相对路径是没有区别的!
//使用重定向跳转到index.html(相对路径)
response.sendRedirect("index.html");
//使用转发跳转到index.html(相对路径)
request.getRequestDispatcher("index.html").forward(request,response);
//使用重定向跳转到index.html(绝对路径)
response.sendRedirect("/day55/index.html");
//使用转发跳转到index.html(绝对路径)
request.getRequestDispatcher("/index.html").forward(request,response);
}
------------------------------
6.12 request作为域对象
域对象:可以存储数据的对象
ServletContext域对象的作用范围:不同设备、当前项目中的任意一个资源都可以访问ServletContext域中的数据
request域对象的作用范围
* request对象的生命周期
* 发起请求时,request初始化
* 响应时,request销毁
* request域对象的作用范围在一次请求中!
request在重定向和转发中使用!
* 重定向中不能使用request域对象
* 转发中可以使用request域对象
/**
* request域对象在重定向和转发中的使用
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request域操作数据的方法和ServletContext域是一样!
//setAttribute、getAttribute、removeAttribute
//往request域中存储一个变量
String msg = "hello servlet";
request.setAttribute("msg",msg);
//重定向到Demo13Servlet request.getContextPath() 获取项目名称
response.sendRedirect(request.getContextPath()+"/demo13");
request.getRequestDispatcher("/demo13").forward(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.removeAttribute("msg");
System.out.println("Demo13Servlet");
String msg = (String) request.getAttribute("msg");
System.out.println(msg);
}
七 会话技术
打开浏览器,访问服务器中资源,关闭浏览器;这个过程就是会话
- 分类
* Cookie会话技术;浏览器会话技术
* Session会话技术;服务器会话技术
- 作用
解决ServletContext域对象、Request域对象存储数据所存在的问题
八 Cookie
8.1 Cookie介绍
网景公司发明。是浏览器的会话技术
- Cookie的流程
* 浏览器请求服务器,请求Demo01Servlet,创建一个Cookie对象,名称为cookie1
* 可以通过响应头Set-Cookie,携带cookie给浏览器进行保存
* 浏览器再次请求服务器,请求Demo02Servlet,获取cookie1对象
------------------------------
8.2 Cookie的基本使用
- 设置Cookie
//方式一(不推荐)
response.addHeader("set-cookie","msg=hellocoolie");
//方式二
Cookie cookie = new Cookie("msg","hellocookie");
response.addCookie(cookie);
- 获取Cookie
开发步骤
* 通过request对象获取所有的Cookie对象,存储到一个数组中
* 遍历该数组,匹配Cookie名称
* 如果匹配上,就知道了指定的Cookie对象
* 如果匹配不上,就没有指定的Cookie对象
Cookie[] cookies = request.getCookies();
Cookie cookie = null;
for(Cookie sonCookie : cookies){
if("msg".equals(sonCookie.getName())){
cookie = sonCookie;
}
}
if(null != cookie){
System.out.println("name : "+msgCookie.getName() + " , value : "+ msgCookie.getValue());
}
------------------------------
8.3 Cookie的相关设置
- 持久化设置
cookie的生命周期
* 默认是随着浏览器的关闭而销毁
setMaxAge
* 设置cookie的存活时长,cookie就可以不随着会话的关闭而销毁
- 路径设置
默认情况下,Cookie对象会随着任何一个请求携带到服务器
setPath 设置Cookie的访问路径
Cookie cookie = new Cookie("msg","helloworld");
cookie.setPath("/day56/demo04");
response.addCookie(cookie);
cookie对象只有访问路径包含"/day56/demo04",才会跟随请求携带到服务器
------------------------------
8.4 Cookie案例
8.4.1 记录上一次访问时间
需求:
第一次访问,就直接打印当前时间
不是第一次访问,就打印上一次的访问时间
开发步骤:
获取对应的Cookie对象
判断是否是第一次访问
如果是第一次访问
打印当前时间
将当前时间存储到Cookie中
如果不是第一次访问
打印上一次访问时间
将当前时间存储到Cookie中
//判断是否是一次请求
Cookie[] cookies = request.getCookies();
Cookie cookie = null;//记录上一次的访问时间
if (cookies != null && cookies.length != 0 ) {
for (Cookie sonCookie : cookies) {
if ("lastTime".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
if (null == cookie) {
//第一次访问 ,打印当前时间,并将创建Cookie对象,存储当前时间
Date currentDate = new Date();
System.out.println("第一次访问,时间为" + format.format(currentDate));
cookie = new Cookie("lastTime",currentDate.getTime()+"");
} else {
//不是第一次访问,从cookie取出上一次的访问时间,并打印。获取当前时间,并存储cookie对象中
long lastDateMills = Long.parseLong(cookie.getValue());
Date lastDate = new Date(lastDateMills);
//获取到了上一次的访问时间
String lastTimeStr = format.format(lastDate);
System.out.println("上一次访问,时间为" + lastTimeStr);
//获取当前时间,并存储cookie对象中
Date currentDate = new Date();
// cookie.setValue(currentDate.getTime()+"");
cookie = new Cookie("lastTime",currentDate.getTime()+"");
}
response.addCookie(cookie);
8.4.2 商品浏览记录
需求:
浏览商品,将商品的浏览的记录起来,并显示!
<!--页面代码 books.html-->
<a href="/day56/history?id=0">西游记</a><br>
<a href="/day56/history?id=1">红楼梦</a><br>
<a href="/day56/history?id=2">水浒传</a><br>
<a href="/day56/history?id=3">三国志</a><br>
/* * 获取history的Cookie对象
* * 判断商品浏览记录是否为空
* * 如果浏览记录没有
* * * 创建Cookie,并将当前的商品记录到Cookie中
* * 如果浏览记录有
* * * 有当前的商品,不做任何处理
* * * 没有当前商品,就需要将当前的商品拼接到已有记录中
*/
@WebServlet(name = "HistoryServlet", urlPatterns = "/history")
public class HistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
Cookie cookie = null;
Cookie[] cookies = request.getCookies();
if (null != cookies && 0 != cookies.length){
for (Cookie sonCookie : cookies) {
if ("history".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
if (null == cookie) {
//之前没有任何浏览记录 ,创建Cookie对象 ,并存储浏览记录(id)
cookie = new Cookie("history",id);
} else {
//之前有一些浏览记录
String historyStr = cookie.getValue();
if (!historyStr.contains(id)) {
//有一些记录,但是不包含当前浏览的商品;
//将浏览商品拼接到已有浏览记录中
//120
//1-2-0
historyStr += "-"+id;
cookie.setValue(historyStr);
} else {
//有一些记录,包含当前浏览的商品 ,不做任何处理
}
}
response.addCookie(cookie);
//上述代码,已经完成了商品浏览记录功能,剩下就是要显示商品浏览记录
response.sendRedirect(request.getContextPath()+ File.separator+"showHistory");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
/*
* 显示商品浏览记录
* * 获取history对应的Cookie对象
* * 获取对应的商品浏览记录
* * 判断是否有浏览记录
* * 如果没有,就显示“没有浏览记录”
* * 如果有,就显示处理浏览记录字符串
*/
@WebServlet(name = "ShowHistoryServlet", urlPatterns = "/show")
public class ShowHistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = null;
Cookie[] cookies = request.getCookies();
if (null != cookies && 0 != cookies.length) {
for (Cookie sonCookie : cookies) {
if ("history".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
StringBuffer responseContent = new StringBuffer();//记录响应正文
if (null == cookie) {
//没有浏览记录
responseContent.append("<font color='red'>没有浏览记录</font>,");
responseContent.append("<a href='books.html'>浏览商品</a>");
} else {
//有浏览记录
//获取浏览记录 0-1-2-3
String[] bookNames = {"西游记","红楼梦","水浒传","三国志"};
String historyStr = cookie.getValue();
String[] historys = historyStr.split("-");
responseContent.append("您的浏览记录如下:<br>");
for (String history : historys) {
//history : 0 / 1 / 2 /3
String bookName = bookNames[Integer.parseInt(history)];
responseContent.append(bookName+"<br>");
}
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseContent.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
------------------------------
8.5 CookieUtils工具类
获取指定名称的Cookie对象
public class CookieUtils {
public static Cookie getCookie(Cookie[] cookies,String cookisName){
if(cookies!=null&&cookies.length!=0){
for(Cookie sonCookie:cookies){
if(cookisName.equals(sonCookie.getName())){
return sonCookie;
}
}
}
return null;
}
}
九 Session
9.1 Session基本使用
Cookie之所以叫做浏览器会话,原因是Cookie的数据存储到浏览器
Session之所以叫做服务器会话,原因是Session的数据存储到服务器
- 执行流程
第一次请求Demo01Servlet时,根据request.getSession方法, 新建一个session对象
当第一次响应时,会将该session对象的id作为cookie头响应给浏览器保存
set-cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0
第二次请求Demo01Servlet时,根据request.getSession方法,请求中会有cookie头
Cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0
会根据该JSESSIONID去服务器中找有没有对应的session对象,如果有就直接用,没有就新建
------------------------------
9.2 Session相关配置
生命周期:session默认是有30分钟的存活时间,参考tomcat中的web.xml
<session-config>
<session-timeout>30</session-timeout>
</session-config>
session和cookie是相关联的,cookie中存储了jsessionid,request.getSession方法会根据
jsessionid去选择,到底是新建session对象,还是引用原来的session对象;
如果,将浏览器关闭了,就意味着cookie中存储的jsessionid就会销毁,
对应request.getSession就会新建一个session对象,但是原来的session对象还存在
session只有两种情况会销毁
调用了invalidate方法
过了30分钟
------------------------------
9.3 session的基本使用
setAttribute 往session域对象中存储数据
getAttribute 从session域对象中获取数据
removeAttribute 把session域对象中的数据移除
------------------------------
9.4 session案例
9.4.1 登录
- UserDao
public class UserDaoImpl implements UserDao {
@Override
public User login(User inputUser) throws SQLException {
// ComboPooledDataSource就是连接池,连接池包含很多连接对象
// 连接池作用就是减少连接的创建次数!
// 第一个用户,登录,创建一个连接池,创建3个连接
// 第二个用户,登录,创建一个连接池,创建3个连接
// 应该只让连接池创建一次!!后面复用就OK了!!
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
User existUser = queryRunner.query(
"select * from tb_user where username = ? and password = ?",
new BeanHandler<User>(User.class),
inputUser.getUsername(),
inputUser.getPassword());`在这里插入代码片`
return existUser;
}
}
- LoginServlet
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
private UserDao userDao = new UserDaoImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
User inputUser = new User();
inputUser.setUsername(username);
inputUser.setPassword(password);
try {
User existUser = userDao.login(inputUser);
System.out.println(existUser);
//判断登录成功
if (null == existUser) {
//登录失败,请求转发,跳转到登录页面
request.getRequestDispatcher("/login.html").forward(request,response);
} else {
//登录成功,重定向,跳转到显示用户信息
//存储existUser
//request : 跳转到首页,使用了重定向,会有一个新的请求
//servletContext : 如果存储到ServletContext,就意味着所有人都可以拿到你的用户信息!
//cookie : 如果存储到cookie中,就是存储到浏览器 , 不安全! cookie中是无法存储中文及一些特殊符号!!
//session : 数据存储到服务器!!
request.getSession().setAttribute("existUser",existUser);
response.sendRedirect("/day57/showIndex");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
- ShowIndexServlet
@WebServlet(name = "ShowIndexServlet" ,urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
User existUser = (User) request.getSession().getAttribute("existUser");
if (null != existUser) {
//在登录状态
response.getWriter().write("欢迎回来,"+existUser.getUsername());
} else {
//不在登录状态
//方式一:提示下,未登录
// response.getWriter().write("您还没有登录,<a href='/day57/login.html'>请登录</a>");
//方式二:跳转到登录页面
response.sendRedirect("/day57/login.html");
//看需求,选择方式一还是方式二
//登录状态权限管理!!
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
第三方jar包,必须放到WEB-INF文件夹中
登录失败使用请求转发、登录成功使用重定向
---------------
9.4.2 随机验证码
- 显示验证码
创建图片对象
画背景
画边框
画干扰线
产生四位随机数,存储到session
画四位随机数
将图片响应到浏览器
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 60;//定义图片宽度
int height = 32;//定义图片高度
//创建图片对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//创建画笔对象
Graphics g = image.getGraphics();
//设置背景颜色
g.setColor(new Color(0xDCDCDC));
g.fillRect(0, 0, width, height);//实心矩形
//设置边框
g.setColor(Color.black);
g.drawRect(0, 0, width - 1, height - 1);//空心矩形
Random rdm = new Random();
//画干扰椭圆
for (int i = 0; i < 50; i++) {
int x = rdm.nextInt(width);
int y = rdm.nextInt(height);
g.drawOval(x, y, 0, 0);
}
//产生随机字符串
String hash1 = Integer.toHexString(rdm.nextInt());
//生成四位随机验证码
String capstr = hash1.substring(0, 4);
//将产生的验证码存储到session域中,方便以后进行验证码校验!
request.getSession().setAttribute("existCode", capstr);
System.out.println(capstr);
g.setColor(new Color(0, 100, 0));
g.setFont(new Font("Candara", Font.BOLD, 24));
g.drawString(capstr, 8, 24);
g.dispose();
//将图片响应到浏览器
response.setContentType("image/jpeg");
OutputStream strm = response.getOutputStream();
ImageIO.write(image, "jpeg", strm);
strm.close();
}
- 校验验证码
//获取输入的验证码
String validateCode = request.getParameter("validateCode");
//将输入的验证码和产生的随机验证码进行校验
String existCode = (String) request.getSession().getAttribute("existCode");
if (validateCode.equals(existCode)) {
//校验通过,完成登录功能
} else {
//校验不通过,跳转到登录页面
}
十 过滤器
10.1 过滤器的概念
滤器就是一个用于在请求之前处理资源的组件
生命周期
随着服务器启动而初始化
随着请求的发出而过滤
随着服务器关闭而销毁
执行流程
浏览器发起请求
服务器会根据这个请求,创建request对象及response对象
过滤器会持有request对象及response对象
只有当过滤器放行之后,request对象及response对象才会传给Serlvet
过滤器链
根据配置顺序,遵从"先过滤,后放行"的原则
------------------------------
10.2 过滤器的基本使用
开发步骤
自定义类实现Filter接口
重写init、doFilter、destroy方法
在web.xml中配置过滤器
声明过滤器
过滤器配置过滤路径
- 过滤器
public class Demo01Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//Demo01Filter过滤器的初始化
System.out.println("Demo01Filter初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//Demo01Filter过滤器处理请求
System.out.println("Demo01Filter放行之前");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Demo01Filter放行之后");
}
@Override
public void destroy() {
//Demo01Filter过滤器的销毁
System.out.println("Demo01Filter销毁");
}
}
- web.xml
<!--声明Demo01Filter过滤器-->
<filter>
<filter-name>Demo01Filter</filter-name>
<filter-class>com.qfedu.filter.Demo01Filter</filter-class>
</filter>
<!--配置Demo01Filter的过滤路径-->
<filter-mapping>
<filter-name>Demo01Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
------------------------------
10.3 过滤器的相关配置
- 初始化参数
- Filter配置初始化参数
<filter>
<filter-name>Demo03Filter</filter-name>
<filter-class>com.qfedu.filter.Demo03Filter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</init-param>
</filter>
- 初始化参数
- Filter获取初始化参数
public class Demo03Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
//获取初始化参数名称
String parameterName = parameterNames.nextElement();
//获取初始化参数值
String parameterValue = config.getInitParameter(parameterName);
System.out.println("name : " + parameterName + " , value : " + parameterValue);
}
}
......
}
- Filter的过滤路径
- 针对< servlet-name > ,以为Filter仅针对Demo01Servlet进行过滤
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<servlet-name>Demo01Servlet</servlet-name>
</filter-mapping>
---------------
- Filter的过滤路径
- 针对< url-pattern >
完全匹配:必须以"/"开头
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>/aa</url-pattern>
</filter-mapping>
目录匹配:必须以"/"开头,以"*"结尾
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>/aa/bb/*</url-pattern>
</filter-mapping>
后缀名匹配:必须以"*"开头,以后缀名结尾
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
------------------------------
10.4 过滤器的注解开发
@WebFilter注解
WebInitParam[] initParams() default {}; 配置初始化参数
String filterName() default ""; 配置过滤器名称
String[] servletNames() default {}; 配置过滤的Servlet
String[] urlPatterns() default {}; 配置过滤路径
- 基本使用
@WebFilter(filterName = "Demo04Filter" ,
urlPatterns = "/demo01",
servletNames = "Demo01Servlet" ,
initParams = {
@WebInitParam(name = "username",value = "root"),
@WebInitParam(name = "password",value = "root123")
})
public class Demo04Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = config.getInitParameter(parameterName);
System.out.println(parameterName + " , " + parameterValue);
}
}
......
}
执行顺序
按照过滤器的类名的字典顺序决定谁先过滤,谁先放行
比如AFilter、BFilter,那么AFilter就会先过滤,BFilter会先放行
------------------------------
10.5 过滤器案例
10.5.1 中文乱码
public class EncodingFilter implements Filter {
private String encoding = null;
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding");
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//处理响应中文乱码
resp.setContentType("text/html;charset="+encoding);
//处理请求中文乱码
req.setCharacterEncoding(encoding);
chain.doFilter(req, resp);//放行
}
}
- web.xml
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.qfedu.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
10.5.2 自动登录
实现步骤
登录账户后,根据是否勾选了自动登录选项框,
判断是否访问和登录相关资源
如果是,直接放行
如果不是,判断是否已经在登录状态
如果是,直接放行
如果不是,需要从cookie中取出存储的用户信息,进行登录操作
如果登录成功,直接放行
如果登录失败,就跳转到登录页面
- 登录功能
@WebServlet(name = "LoginServlet" ,urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
String autoLogin = request.getParameter("autoLogin");
System.out.println(autoLogin);
if ("autoLogin".equals(autoLogin)) {
//进行自动登录,无非就是将用户信息(账户和密码(加密))保存起来!!!
//request、servletContext、cookie、session
Cookie cookie = new Cookie("autoLogin",username+"-"+password);
cookie.setMaxAge(7 * 24 * 60 * 60);
response.addCookie(cookie);
}
//登录成功,转发到一个页面,用来显示用户信息
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
request.getRequestDispatcher("/showIndex").forward(request,response);
} else {
//登录失败,转到登录页面
request.getRequestDispatcher("/index.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- LoginFilter自动登录
public class AutoLoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
//获取请求路径
String requestURI = request.getRequestURI();
//1,判断访问的资源是否和登录相关
if (requestURI.contains("login")) {
//和登录相关的资源,直接放行
chain.doFilter(request, resp);
} else {
//不是登录相关的资源
//2,判断是否在登录状态
User existUser = (User) request.getSession().getAttribute("existUser");
if (null == existUser) {
//不在登录状态 , 进行自动登录
//获取Cookie
Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
//判断cookie是否为空 , 存在浏览器清理缓存
if (null == cookie) {
//浏览器清理缓存 , 相当于自动登录失败!! 跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
} else {
//还有缓存,进行自动登录
//获取用户信息 root-root
String infoStr = cookie.getValue();
String[] infos = infoStr.split("-");
String username = infos[0];
String password = infos[1];
if ("root".equals(username) && "root".equals(password)) {
//自动登录成功 ,修改登录状态, 直接放行 ,意味着,还不在登录状态!!!
existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
chain.doFilter(req,resp);
} else {
//自动登录失败 (修改了密码) ,跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
} else {
//在登录状态 , 直接放行
chain.doFilter(request,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
10.5.3 敏感词屏蔽
对request对象进行增强。增强获取参数相关方法
放行。传递增强的请求方法
@WebFilter(
filterName = "SensitiveWordsFilter" ,
urlPatterns = "/*",
initParams = {
@WebInitParam(name = "word1",value = "笨蛋"),
@WebInitParam(name = "word2" ,value = "傻瓜"),
@WebInitParam(name = "word3" ,value = "尼玛"),
@WebInitParam(name = "word4",value = "靠")
})
public class SensitiveWordsFilter implements Filter {
//敏感词
List<String> sensitiveWords = new ArrayList<>();
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
sensitiveWords.add(sensitiveWord);
}
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("SensitiveWordsFilter doFilter");
HttpServletRequest request = (HttpServletRequest) req;
//增强request下的getParameter方法
HttpServletRequest requestPrxoy = (HttpServletRequest) Proxy.newProxyInstance(
request.getClass().getClassLoader(),
request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter方法
Object returnValue = null;
String methodName = method.getName();
if ("getParameter".equals(methodName)) {
//returnValue就是getParameter方法的返回值,可能会存在敏感词
String returnValue1 = (String)method.invoke(request, args);
//开始处理敏感词
for (String sensitiveWord : sensitiveWords) {
if (returnValue1.contains(sensitiveWord)) {
//getParameter方法的返回值包含敏感词
returnValue1 = returnValue1.replace(sensitiveWord,"***");
}
}
return returnValue1;
} else {
returnValue = method.invoke(request, args);
}
return returnValue;
}
});
chain.doFilter(requestPrxoy, resp);
}
}
十一 监听器
11.1 监听器的介绍
监听器概念
事件源:事件发生的源头
监听器:监听事件发生
绑定:将监听器绑定到事件源
事件:能够触发监听器的事
Servlet监听器
事件源:request域对象、session域对象、ServletContext域对象
监听器:Servlet三种监听器
绑定:配置web.xml
事件:域对象发生改变
------------------------------
11.2 监听器的分类
一类监听器 监听域对象的创建、销毁
二类监听器 监听域对象中的属性变更(属性设置、属性替换、属性移除)
三类监听器 监听域对象的java对象的绑定
11.2.1 一类监听器
ServletRequestListener : 监听ServletRequest域对象的创建、销毁
HttpSessionListener :监听HttpSession域对象的创建、销毁
ServletContextListener : 监听ServletContext域对象的创建、销毁
开发步骤
自定义类实现一类监听器
重写监听器中的方法
配置web.xml
- 监听器
public class MyListener01 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//监听ServletContext域的初始化,随着服务器的启动
System.out.println("ServletContext初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//监听ServletContext域的销毁,随着服务器的关闭
System.out.println("ServletContext销毁");
}
}
- web.xml(绑定)
<listener>
<listener-class>com.qfedu.listener.MyListener01</listener-class>
</listener>
事件源: ServletContext域对象
监听器:ServletContextListener
绑定: web.xml配置
事件 : ServletContext域对象发生了创建、发生了销毁
11.2.2 二类监听器
ServletRequestAttributeListener :监听ServletRequest域对象中属性变更
HttpSessionAttributeListener : 监听HttpSession域对象中属性变更
ServletContextAttributeListener : 监听ServletContext域对象中属性变更
- 监听器
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性添加
System.out.println("ServletContext added");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值被替换
System.out.println("ServletContext replaced");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值移除
System.out.println("ServletContext removed");
}
}
- web.xml(绑定)
<listener>
<listener-class>com.qfedu.listener.MyServletContextAttributeListener</listener-class>
</listener>
11.2.3 三类监听器
HttpSessionBindingListener
监听session域中的java对象的状态(绑定和解绑)
绑定:将java对象存储到session域对象
解绑:将java对象从session域对象移除
监听器组成
事件源:java对象
监听器:HttpSessionBindingListener
绑定:java对象实现HttpSessionBindingListener接口
事件:java对象在session中状态发生改变
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("User绑定");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("User解绑");
}
......
}
HttpSessionBindingListener监听不需要在web.xml配置
------------------------------
11.3 监听器综合案例
11.3.1 记录登录人数
- login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/day59/login" method="post">
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<button type="submit">登录</button>
</form>
</body>
</html>
- bean.User
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
//有人登录成功 , 在线人数(count)加1
//判断是否是第一个登录成功的人
//获取ServletContext
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if (null == count) {
//就是第一个登录成功的人
count = 1;
} else {
//不是第一个登录成功的人
count++;
}
servletContext.setAttribute("count", count);
}
//在同一个浏览器,意味着是同一个session
//第一次登录 ,session.setAttribute("existUser","root") , valueBound +1
//第二次登录 ,session.setAttribute("existUser","root1") , valueBound +1 -> valueUnbound -1
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
//有人注销登录 ,在线人数(count)减1
System.out.println("User解绑");
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
count--;
servletContext.setAttribute("count", count);
}
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
- LoginServlet
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
//登录成功 , 修改登录状态 ,跳转到ShowIndexServlet
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser", existUser);
// request.getRequestDispatcher("/showIndex").forward(request,response);
response.sendRedirect("/day59/showIndex");
} else {
//登录失败,转发到登录页面,重新登录
request.getRequestDispatcher("/login.html").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- LogoutServlet
@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//注销登录 , 将existUser变量从session域中移除!
// request.getSession().removeAttribute("existUser");
//注销登录,将session销毁 -> 将existUser变量从session域中移除!
request.getSession().invalidate();
//注销成功
request.getRequestDispatcher("/showIndex").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- ShowIndexServlet
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null == existUser) {
//不在登录状态,提示
responseBody.append("您还没有登录;<a href='/day59/login.html'>请登录</a><br>");
} else {
//在登录状态,显示信息
responseBody.append("欢迎回来," + existUser.getUsername() + " <a href='/day59/logout'>注销</a><br>");
}
ServletContext servletContext = getServletContext();
//获取在线人数count
Integer count = (Integer) servletContext.getAttribute("count");
System.out.println("在线人数 " + count);
//count变量为null,在没有任何人登录状态下,访问了ShowIndexServlet
if (null == count) {
//没有任何人在登录状态 ,在线人数处理成0人
count = 0;
} else {
//有人在登录状态 ,直接输出在线人数count人
}
responseBody.append("在线人数 : " + count);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}