JavaWeb全归纳总结

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: JavaWeb全归纳

第一章 Tomcat 基础

一、 web 概念

1. 软件架构

  1. C/S: 客户端/服务器端
  2. B/S: 浏览器/服务器端

2.资源分类

  1. 静态资源: 所有用户访问后,得到的==结果都是一样==的,称为静态资源。静态资源可以==直接被浏览器解析==。如图片、视频。
  2. 动态资源: 每个用户访问==相同资源==后,得到的==结果可能不一样== , 称为动态资源。动态资源被访问后,需要==先转换为静态资源==再==返回给浏览器==通过==浏览器进行解析==。如:servlet,jsp,php,asp....

二、常见的web服务器

1、 概念

  1. 服务器:安装了服务器软件的计算机
  2. 服务器软件:接收用户的请求,处理请求,做出响应
  3. web服务器软件:接收用户的请求,处理请求,做出响应。

在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目

2、常见服务器软件

动态服务器:

  • webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • ==Tomcat==:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。(300左右的并发)

静态的服务器:

  • nginx(代理,反向代理等)极高的并发
  • apache

3、Tomcat 历史

1) Tomcat 最初由Sun公司的软件架构师 James Duncan Davidson 开发,名称为 “JavaWebServer”。
2) 1999年 ,在 Davidson 的帮助下,该项目于1999年于apache 软件基金会旗下的 JServ 项目合并,并发布第一个版本(3.x), 即是现在的Tomcat,该版本实现了Servlet2.2 和 JSP 1.1 规范 。
3) 2001年,Tomcat 发布了4.0版本, 作为里程碑式的版本,Tomcat 完全重新设计了其架构,并实现了 Servlet 2.3 和 JSP1.2规范。

目前企业中的Tomcat服务器, 主流版本还是 7.x 和 8.x 。

4、 Tomcat 安装

下载地址

在这里插入图片描述

5、Tomcat 目录结构

目录 目录下文件 说明
bin / 存放Tomcat的启动、停止等批处理脚本文件
startup.bat ,startup.sh 用于在windows和linux下的启动脚本
shutdown.bat ,shutdown.sh 用于在windows和linux下的停止脚本
conf / 用于存放Tomcat的相关配置文件
Catalina 用于存储针对每个虚拟机的Context配置
context.xml 用于定义所有web应用均需加载的Context配置,如果web应用指定了自己的context.xml ,该文件将被覆盖
catalina.properties Tomcat 的环境变量配置
catalina.policy Tomcat 运行的安全策略配置
logging.properties Tomcat 的日志配置文件, 可以通过该文件修改Tomcat 的日志级别及日志路径等
server.xml Tomcat 服务器的核心配置文件
tomcat-users.xml 定义Tomcat默认的用户及角色映射信息配置
web.xml Tomcat 中所有应用默认的部署描述文件, 主要定义了基础Servlet和MIME映射。
lib / Tomcat 服务器的依赖包
logs / Tomcat 默认的日志存放目录
webapps / Tomcat 默认的Web应用部署目录
work / Web 应用JSP代码生成和编译的临时目录

6、Tomcat 启动停止

双击 bin/startup.bat 文件 启动
双击 bin/shutdown.bat 文件 停止
访问:http://localhost:8080

三、Tomcat 架构

1、HTTP工作原理

HTTP协议是==浏览器与服务器==之间的==数据传送协议==。作为应用层协议,HTTP是基于TCP/IP协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包(Packet)传输,主要规定了客户端和服务器之间的==通信格式==。
在这里插入图片描述

2、HTTP服务器请求处理

==浏览器发给服务端==的是一个==HTTP格式的请求==,HTTP服务器收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同的==Java类来处理==。
在这里插入图片描述
图1 , 表示HTTP服务器直接调用具体业务类,它们是紧耦合的

图2,HTTP服务器不直接调用业务类,而是把==请求交给容器来处理==,==容器通过Servlet接口调用业务类==。因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类==解耦==的目的。而Servlet接口和Servlet容器这一整套规范叫作Servlet规范。Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功能。作为Java程序员,如果我们要实现新的业务功能,只需要==实现一个Servlet,并把它注册到Tomcat(Servlet容器)中==,剩下的事情就由Tomcat帮我们处理了。

3、Servlet容器工作流程

当客户请求某个资源时,==HTTP服务器==会用一个==ServletRequest对象==把客户的请求信息==封装==起来,然后==调用Servlet容器的service方法==,Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成初始化,接着调用Servlet的service方法来处理请求,把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
在这里插入图片描述

4、Tomcat整体架构

我们现在知道了Tomcat要实现两个核心功能:

1) 处理Socket连接,负责网络字节流与Request和Response对象的转化。
2) 加载和管理Servlet,以及具体处理Request请求。

因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。==连接器负责对外==交流,==容器负责内部==处理。

大致图解为:
在这里插入图片描述

四、Tomcat 服务器配置

Tomcat 服务器的配置主要集中于 tomcat/conf 下的 catalina.policy、catalina.properties、context.xml、server.xml、tomcat-users.xml、web.xml 文件。

### server.xml
server.xml 是tomcat 服务器的核心配置文件,包含了Tomcat的 Servlet 容器(Catalina)的所有配置。


Server:
Server是server.xml的根元素,用于创建一个Server实例,默认使用的实现类是org.apache.catalina.core.StandardServer

<Server port="8005" shutdown="SHUTDOWN">
...
</Server>

属性说明:
port : Tomcat 监听的关闭服务器的端口。
shutdown: 关闭服务器的指令字符串。


Connector:
Connector 用于创建链接器实例。默认情况下,==server.xml 配置了两个链接器==,一个支持HTTP协议,一个支持AJP协议。因此大多数情况下,我们并不需要新增链接器配置,只是根据需要对已有链接器进
行优化。

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" />

属性说明:
1) port: 端口号,Connector 用于创建服务端Socket 并进行监听, 以等待客户端请求链接。如果该属性设置为0,Tomcat将会随机选择一个可用的端口号给当前Connector 使用。
2) protocol : 当前Connector 支持的访问协议。 ==默认为 HTTP/1.1== 。
3) connectionTimeOut : Connector 接收链接后的等待超时时间, 单位为毫秒。 ==-1 表示不超时==。
4) URIEncoding : 用于指定编码URI的字符编码, ==Tomcat8.x版本默认的编码为 UTF-8== 。

五、手写Tomcat

六、创建Javaweb项目

1、创建项目

在这里插入图片描述
在这里插入图片描述

2、项目结构

在这里插入图片描述

3、配置Tomcat

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

七、WebSocket

1、WebSocket与HTTP


HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步 JavaScript 和 XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。


WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

状态码:101


在这里插入图片描述

2、特点

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

示例:

ws://example.com:80/some/path

在这里插入图片描述

3、Tomcat的WebSocket

Tomcat自7.0.5版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356 ),而在7.0.5版本之前(7.0.2版本之后)则采用自定义API,即WebSocketServlet。

根据JSR356的规定,Java WebSocket应用由一系列的WebSocket Endpoint组成。Endpoint是一个Java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口,就像Servlet之于HTTP请求一样(不同之处在于Endpoint每个链接一个实例)。

3.1 定义Endpoint:

  1. 第一种是编程式,即继承类javax.websocket.Endpoint并实现其方法。
  2. 第二种是注解式,即定义一个POJO对象,为其添加Endpoint相关的注解。

Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。Endpoint接口明确定义了与其生命周期相关的方法,规范实现者确保在生命周期的各个阶段调用实例的相关方法。

3.2 Endpoint:

3.2.1 生命周期:
  1. onOpen:当开启一个新的会话时调用。这是客户端与服务器握手成功后调用的方法。等同于注解@OnOpen。
  2. onClose:当会话关闭时调用。等同于注解@OnClose。
  3. onError:当链接过程中异常时调用。等同于注解@OnError。

当客户端链接到一个Endpoint时,服务器端会为其创建一个唯一的会话(javax.websocket.Session)。会话在WebSocket握手之后创建,并在链接关闭时结束。当生命周期中触发各个事件时,都会将当前会话传给Endpoint。

3.2.2 接受、发送消息:
  1. 接受消息:

我们通过为Session添加==MessageHandler消息处理器来接收消息==。当采用注解方式定义Endpoint时,我们还可以通过 ==@OnMessage== 指定接收消息的方法。

  1. 发送消息:

发送消息则由==RemoteEndpoint==完成,其实例由Session维护,根据使用情况,我们可以通过Session.getBasicRemote获取同步消息发送的实例或者通过Session.getAsyncRemote获取异步消息发送的实例。

WebSocket通过javax.websocket.WebSocketContainer接口维护应用中定义的所有Endpoint。它在每个Web应用中只有一个实例,类似于传统Web应用中的ServletContext。

最后,WebSocket规范提供了一个接口javax.websocket.server.ServerApplicationConfig,通过它,我们可以为编程式的Endpoint创建配置(如指定请求地址),还可以过滤只有符合条件的Endpoint提供服务。该接口的实现同样通过SCI机制加载。

第二章 Servlet入门

servlet就是一个java程序,用来处理请求和响应。

一、Servlet架构

在这里插入图片描述

二、Servlet任务

处理请求equest,生成响应response

三、Servlet相关知识

1、Servlet加载时机

在默认情况下,当Web客户第一次请求访问某个Servlet时,Web容器会创建这个Servlet的实例。当设置了web.xml中的子元素后,Servlet容器在启动Web应用时,将按照指定顺序创建并初始化这个Servlet。设置的==数值大于0==即可。

<servlet>
  <servlet-name>HelloServlet</servlet-name>
  <servlet-class>com.langsin.servlet.HelloServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>

2、Servlet的生命周期

  • init():Servlet进行初始化
  • service():Servlet处理客户端的请求
  • destroy():Servlet结束,释放资源

在调用destroy()方法后,Servlet由JVM的垃圾回首器进行垃圾回收


init()方法:

Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化在Servlet生命周期中==init()方法只被调用一次==。
当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。


service()方法:

service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查HTTP 请求类型(==GET、POST(增加)、PUT(修改)、DELETE== 等),并在适当的时候调用doGet()、doPost()等方法。


destroy()方法:

destroy()方法只会被==调用一次==,在Servlet==生命周期结束时被调用==。destroy()方法可以让Servlet关闭数据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用destroy()方法之后,Servlet对象被标记为垃圾回收。

总结:

  • 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。
  • 再次访问时,只会执行service()方法,不再执行init()方法。
  • 在关闭Web容器时会调用destroy()方法。

3、实现一个servlet

当服务器接收到一个请求,就要有一个servlet去处理这个请求,所以完成一个servlet通常需要两步走。

  1. 写一个java程序定义一个servlet
  2. 配置一下servlet确定这个servlet要处理哪一个请求

1、创建Servlet的三种方式

(1)实现javax.servlet.Servlet接口。
(2)继承javax.servlet.GenericServlet类
(3)==继承javax.servlet.http.HttpServlet类==(常用)

2、配置Servlet的两种方式

(1)使用web.xml文件配置Servlet

<servlet>
  <servlet-name>user</servlet-name>
  <servlet-class>com.controller.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>user</servlet-name>
  <url-pattern>/user.do</url-pattern>
</servlet-mapping>

(2)使用注解进行Servlet配置

@WebServlet("/user.do")

四、servlet的匹配规则

1、四种匹配规则

(1) 精确匹配

<url-pattern>中配置的项必须与url完全精确匹配。

<servlet-mapping>
  <servlet-name>user</servlet-name>
  <url-pattern>/user.do</url-pattern>
</servlet-mapping>

(2) 路径匹配

以“/”字符开头,并以“/*”结尾的字符串用于路径匹配

<servlet-mapping>
  <servlet-name>user</servlet-name>
  <url-pattern>/user/*</url-pattern>
</servlet-mapping>
路径以/user/开始,后面的路径可以任意。

(3)扩展名匹配**

以“*.”开头的字符串被用于扩展名匹配

<servlet-mapping>
  <servlet-name>user</servlet-name>
  <url-pattern>*.jsp</url-pattern>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>
任何扩展名为jsp或action的url请求都会匹配

(4) 缺省匹配

<servlet-mapping>
  <servlet-name>user</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

2、匹配顺序

  1. 精确匹配。
  2. 路径匹配,先最长路径匹配,再最短路径匹配。
  3. 扩展名匹配。

注意:使用扩展名匹配,前面就不能有任何的路径。

  1. 缺省匹配,以上都找不到servlet,就用默认的servlet

3、注意问题

路径匹配和扩展名匹配无法同时设置

如<url-pattern>/user/*.action</url-pattern>是非法的

<url-pattern>/aa/*/bb</url-pattern>是精确匹配,合法,这里的*不是通配的含义

"/*"和"/"含义并不相同

“/*”属于路径匹配,并且可以匹配所有request
由于路径匹配的优先级仅次于精确匹配,
所以“/*”会覆盖所有的扩展名匹配,很多404错误均由此引起,
所以这是一种特别恶劣的匹配模式。

“/”是servlet中特殊的匹配模式,
切该模式有且仅有一个实例,优先级最低,
不会覆盖其他任何url-pattern,只是会替换servlet容器的内建default servlet ,
该模式同样会匹配所有request。

“/*”和“/”均会拦截静态资源的加载

五、请求和响应

1、请求-request

(1)request概述

request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。

request的功能:

  • 封装了请求头数据
  • 封装了请求正文数据,如果是GET请求,那么就没有正文
  • request是一个域对象,可以把它当成Map来添加获取数据
  • request提供了==请求转发==和请求包含功能

(2)request获取请求头数据

相关方法:

  • String getHeader(String name):获取指定名称的请求头
  • Enumeration getHeaderNames():获取所有请求头名称
  • int getIntHeader(String name):获取值为int类型的请求头

(3)request获取请求相关的其它方法

  • int getContentLength():获取请求体的字节数,GET请求没有请求体,没有请求体返回-1;
  • ==String getContentType()==:获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;
  • String getMethod():返回请求方法,例如:GET
  • Locale getLocale():返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用;
  • String getCharacterEncoding():获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码;
  • void setCharacterEncoding(String code):设置请求编码,==只对请求体有效==!注意,==对于GET而言,没有请求体!!!== 所以此方法只能对POST请求中的参数有效!
  • String getContextPath():返回上下文路径,例如:/hello
  • String getQueryString():返回请求URL中的参数,例如:name=zhangSan
  • String getRequestURI():返回请求URI路径,例如:/hello/oneServlet
  • StringBuffer getRequestURL():返回请求URL路径,例如:http://localhost/hello/oneServlet,即返回除了参数以外的路径信息;
  • String getServletPath():返回Servlet路径,例如:/oneServlet
  • String getRemoteAddr():返回当前客户端的IP地址;
  • String getRemoteHost():返回当前客户端的主机名,但这个方法的实现还是获取IP地址;
  • String getScheme():返回请求协议,例如:http;
  • String getServerName():返回主机名,例如:localhost
  • int getServerPort():返回服务器端口号,例如:8080

案例:request.getRemoteAddr():封IP

String ip = request.getRemoteAddr();   
if("127.0.0.1".equals(ip)) {   
    response. getWriter().print("您的IP已被禁止!");   
} else {   
   response.getWriter().print("Hello!");   
} 

(4)request获取请求参数

  • 浏览器地址栏直接输入:一定是GET请求;
  • 超链接:一定是GET请求;
  • 表单:可以是GET,也可以是POST,这取决与的method属性值;

*

GET请求和POST请求的区别:

GET请求:
Ø 请求参数会在浏览器的地址栏中显示,所以==不安全==;
Ø 请求参数长度==限制长度==在1K之内;
Ø GET请求==没有请求体==,无法通过request.setCharacterEncoding()来设置参数的编码;

POST请求:
Ø 请求参数不会显示浏览器的地址栏,相对安全;
Ø 请求参数长度没有限制;


使用request获取请求参数的API:

  • ==String getParameter(String name)==:通过指定名称获取参数值;
  • String[] getParameterValues(String name):当多个参数名称相同时,可以使用方法来获取;
  • Enumeration getParameterNames():获取所有参数的名字;
  • Map<String,String[]> getParameterMap():获取所有参数封装到Map中,其中key为参数名,value为参数值,因为一个参数名称可能有多个值,所以参数值是String[],而不是String。

(5)请求转发

public class AServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
    System.out.println("AServlet");
    request.getRequestDispatcher("/BServlet").forward(request, response);
    }
}

(6)request域方法

一个请求会创建一个request对象,如果在一个请求中经历了多个Servlet,那么==多个Servlet就可以使用request来共享数据.==

  • void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性
  • Object getAttribute(String name):用来获取request中的数据,当前在获取之前需要先去存储才行,例如:String value =
    (String)request.getAttribute(“xxx”);,获取名为xxx的域属性;
  • void removeAttribute(String name):用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
  • Enumeration getAttributeNames():获取所有域属性的名称;

2、响应-response

(1) response概述

response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。
在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。
response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。

response对象的功能:

  • 设置响应头信息
  • 发送状态码
  • 设置响应正文
  • ==重定向==

(2)response响应正文

response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:

  • PrintWriter out = response.getWriter():获取字符流,处理字符;
  • ServletOutputStream out = response.getOutputStream():获取字节流,处理文件;

注意,在一个请求中,==不能同时使用这两个流==!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。

字符响应流:

(1)字符编码

重要:在使用response.getWriter()时需要注意==默认字符编码为ISO-8859-1==,如果希望设置字符流的字符编码为utf-8,可以使用==response.setCharaceterEncoding(“utf-8”)== 来设置。这样可以保证输出给客户端的字符都是使用UTF-8编码的!
但客户端浏览器并不知道响应数据是什么编码的!如果希望通知客户端使用UTF-8来解读响应数据,那么还是使用==response.setContentType("text/html;charset=utf-8")== 方法比较好,
因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用content-type头来解读响应数据。

(2)缓冲区

response.getWriter()是PrintWriter类型,所以它有缓冲区,缓冲区的默认大小为8KB。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当Servlet执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。
如果希望响应数据马上发送给客户端:
Ø 向流中写入大于8KB的数据;
Ø 调用response.flushBuffer()方法来手动刷新缓冲区;

(3)设置响应头信息

使用==response对象的setHeader()方法来设置响应头==!使用该方法设置的响应头最终会发送给客户端浏览器!

  • response.setHeader(“content-type”, “text/html;charset=utf-8”)

    设置content-type响应头,该头的作用是告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“utf-8”);

  • response.setHeader("Refresh","5;URL=http://www.baidu.cn")
    5秒后自动跳转到百度主页

(4)设置状态码及其他方法

  • response.setContentType("text/html;charset=utf-8"):等同与调用
  • response.setHeader(“content-type”, “text/html;charset=utf-8”);用它就行了。
  • response.setCharacterEncoding(“utf-8”):设置字符响应流的字符编码为utf-8;
  • response.setStatus(200):设置状态码;
  • response.sendError(404,“您要查找的资源不存在”):当发送错误状态码时,Tomcat会跳转到固定的错误页面去,但可以显示错误信息。

(5)重定向

响应码为200表示响应成功,而响应码为302表示重定向。

public class AServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setStatus(302);
        response.setHeader("Location", "http://www.baidu.com");
    } 
}
public class AServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("http://www.baidu.com");
    }
}

重定向与转发的区别:

转发过程: 客户端浏览器发送http请求 → web服务器接受此请求 → 调用内部的一个方法在容器内部完成请求处理和转发动作 → 将目标资源发送给客户。

//java代码示例
request.getRequestDispatcher("xxx.jsp或者servlet").forward(request,response);

重定向过程: 客户端浏览器发送http请求 → web服务器接收后发送30X状态码响应及对应新的location给客户浏览器 → 客户浏览器发现是30X响应,则自动再发送一个新的http请求,请求url是新的location地址→ 服务器根据此请求寻找资源并发送给客户。

//java代码示例
response.sendRedirect("xxx.jsp或者servlet");

转发和重定向对比:
| | 转发 |重定向|
|--|--|--|
|跳转方式 |服务器端转发| 客户端转发|
|客户端发送请求次数| 1次| 2次|
|客户端地址栏是否改变| 不变| 变|
|是否共享request域| 共享 |不共享(request域中的数据丢失),必须使用session传递属性|
|是否共享response域| 共享 |不共享|
|范围| 网站内| 可以跨站点|
|JSP| URL不可带参数| URL可带参数|
|是否隐藏路径| 隐藏| 不隐藏|

什么时候使用重定向,什么时候使用转发?

原则上: 要==保持request域的数据时使用转发==,要==访问外站资源的时候用重定向==,其余随便;
特殊的应用: 对数据进行修改、删除、添加操作的时候,应该用response.sendRedirect()。如果是采用了request.getRequestDispatcher().forward(request,response),那么操作前后的地址栏都不会发生改变,仍然是修改的控制器,如果此时再对当前页面刷新的话,就会重新发送一次请求对数据进行修改,这也就是有的人在刷新一次页面就增加一条数据的原因。

转发与重定向的安全性:

  • 转发安全性: 在服务器内部实现跳转,客户端不知道跳转路径,相对来说比较安全。
  • 重定向安全性: 客户端参与到跳转流程,给攻击者带来了攻击入口,受威胁的可能性较大。
    比如一个HTTP参数包含URL,Web应用程序将请求重定向到这个URL,攻击者可以通过修改这个参数,引导用户到恶意站点,并且通过将恶意域名进行十六进制编码,一般用户很难识别这是什么样的URL;或者指引到该网站的管理员界面,如果访问控制没有做好将导致一般用户可以直接进入管理界面。
  • 重定向和转发检查列表:

       重定向之前,验证重定向的目标URL。 
       ==使用白名单验证重定向目标==。 如果在网站内重定向,可以使用相对路径URL。
      重定向或者转发之前,要验证用户==是否有权限==访问目标URL。

3、sesion和cookie

==http是无状态的==,他不保存状态,意思就是一个浏览器发的请求,随后就断开了,下一次发送请求就和上一次无关了。
比如一个用户购买一个商品,第一次需要登录,如果再买一个时向服务器发送请求,服务器如果不知道是谁发的,那么他就得再登录一次,这显然是不合理的,于是就提出了cookie和session的概念。
==cookie==是记录在==浏览器端==的一个字符串,==session==是保存在==服务器端==的一个对象。他们两互相配合让服务器有了能识别客户端一些状态的能力,意思就是服务就就能知道这个客户端有没有登录等。==cookie就相当于通行证==,==session就是门房==,进去时需要从==门房识别一个身份==。

粗略的描述一下过程:

  1. 当浏览器向客户端发送请求时,服务器会为他创建一个session,同时相应会加一个头(SetCookie: jsessionid=ewrwerwer123)
  2. 浏览器得到相应就会在在自己这保存下这个字符串,以后访问这个网站的时候就会一直带着。
  3. 当下一个请求发起时,会带着这个cookie的信息,服务器通过查询id找的session,通过session内保存的信息,就能获得这个客户端的状态。

第三章 jsp入门

一、JSP基础语法

1、JSP模板元素

JSP页面中的HTML内容称之为JSP模版元素。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。

2、JSP脚本片段

JSP脚本片断用于在JSP页面中编写多行Java代码(在<%%>不能定义方法)。
语法:<%多行java代码%>

注意:

  1. JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
  2. JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
  3. 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其

他JSP元素。

  1. 多个脚本片断中的代码可以相互访问

3、JSP注释

注释类型 使用
显式注释 HTML风格的注释:<!- - 注释内容- ->
隐式注释 JAVA风格的注释://、/……/
JSP自己的注释 <%- - 注释内容- -%>

注意:
HTML的注释在浏览器中查看源文件的时候是可以看得到的,而JAVA注释和JSP注释在浏览器中查看源文件时是看不到注释的内容的。

二、JSP原理

1、JSP的本质

浏览器向服务器发请求,不管访问的是什么资源,其实都是在访问Servlet,所以当==访问一个jsp页面时,其实也是在访问一个Servlet==,服务器在执行jsp的时候,首先把jsp编译成一个Servlet,所以我们访问jsp时,其实不是在访问jsp,而是在访问jsp翻译过后的那个Servlet。
jsp的本质其实就是个html模板,编译器会根据模板生成对应的servlet

2、_jspService方法

问题1:Jsp页面中的html排版标签是如何被发送到客户端的?
浏览器接收到的这些数据,都是在_jspService方法中输出给浏览器的。

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException {
    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;
    try {
        response.setContentType("text/html; charset=UTF-8");
        pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true);
        _jspx_page_context = pageContext;
        application = pageContext.getServletContext();
        config = pageContext.getServletConfig();
        session = pageContext.getSession();
        out = pageContext.getOut();
        _jspx_out = out;
        out.write("\r\n");
        out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
        out.write("<html>\r\n");
        out.write("<head>\r\n");
        out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
        out.write("<title>Insert title here</title>\r\n");
        out.write("</head>\r\n");
        out.write("<body>\r\n");
        out.write("\t");
        out.write('\r');
        out.write('\n');
        out.write(' ');
        TestFun();
        out.println("name:" + name);
        out.write("\r\n");
        out.write("</body>\r\n");
        out.write("</html>");
    } catch (java.lang.Throwable t) {
        if (!(t instanceof javax.servlet.jsp.SkipPageException)){
            out = _jspx_out;
            if (out != null && out.getBufferSize() != 0)
            try {
                if (response.isCommitted()) {
                    out.flush();
                } else {
                    out.clearBuffer();
                }
            } catch (java.io.IOException e) {}
            if (_jspx_page_context != null)
                _jspx_page_context.handlePageException(t);
            else throw new ServletException(t);
        }
    } finally {
        _jspxFactory.releasePageContext(_jspx_page_context);
    }
}

问题2:Jsp页面中的java代码服务器是如何执行的?
在jsp中编写的java代码会被翻译到jspService方法中去,当执行jspService方法处理请求时,就会执行在jsp编写的java代码了,所以Jsp页面中的java代码服务器是通过调用_jspService方法处理请求时执行的。

3、jsp在服务器的执行流程

第一次执行:

  1. 客户端通过电脑连接服务器,因为是请求是动态的,所以所有的请求交给WEB容器来处理
  2. 在容器中找到需要执行的*.jsp文件
  3. 之后.jsp文件通过转换变为.java文件
  4. .java文件经过编译后,形成.class文件
  5. 最终服务器要执行形成的*.class文件

第二次执行:

  1. 因为已经存在了*.class文件,所以不在需要转换和编译的过程

修改后执行:

  1. 源文件已经被修改过了,所以需要重新转换,重新编译。

三、JSP指令

1、JSP指令标识的语法格式

<%@ 指令名 属性1 = "属性1的值" 属性2 = "属性2的值" ....%>
  • 指令名:用于指定指令名称 在JSP中包含==page include raglib== 这3种指令
  • 属性: 用于指定指令属性名称 不同的指令包含不同的属性 在同一个指令中可以设置多个属性 ==各个属性之间用逗号或者空格隔开==
  • 属性值:用于指定属性的值

2、Page指令

定义整个JSP页面的相关属性

语法格式:

<%@ page 属性1 = "属性1的值" 属性2 = "属性2的值" ....%>

language属性
用于设置整个JSP页面的使用的语言,目前只支持JAVA语言,改属性默认值是JAVA

<%@ page language="java" %>

improt属性
设置JSP导入的类包

<%@ page improt="java.util.*" %>

pageEccoding属性
这种JSP页面的编码格式,也就是指定文件编码
设置JSP页面的MIME类型和字符编码

<%@ page contentType ="text/html;charset=UTF-8" %>

Sesssions属性
设置页面是否使用HTTP的session会话对象.Boolen类型,默认值是true
JSP模板

<%@ page session ="false" %>

注意:session是JSP的内置对象之一
autoFlush属性
设置JSP页面缓存满时,是否自动刷新缓存,默认值是:true, 如果这种为false,则当页面缓存满是就会抛出异常

<%@ page autoFlush ="false" %>

isErrorPage属性
把当前页面设置成错误处理页面来处理另外jsp页面的错误

<%@ page isErrorPage ="true" %>

errorPage属性
指定当前jsp页面异常错误的另一个JSP页面,指定的JSP页面的isErrorPage属性必须为true,属性值是一个url字符串

<%@ page errorPage ="errorPage.jsp" %>

3、include指令

include指令用于==引入其它JSP页面==,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为==静态引入==。

语法格式:

<%@ include file="relativeURL"%>

file属性用于指定被引入文件的路径。路径以"/"开头,表示代表当前web应用。

注意:

  1. 被引入的文件必须==遵循JSP语法==。
  2. 被引入的文件可以使用==任意的扩展名==,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式

处理它里面的内容,为了见明知意,==JSP规范建议使用.jspf==(JSP fragments(片段))作为静态引入文件的扩展名。

  1. 由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的==指令不能冲突==(除了pageEncoding和导包除外)。

四、JSP标签

一、 Jsp标签分类

1)内置标签(动作标签): 不需要在jsp页面导入标签
2)jstl标签: 需要在jsp页面中导入标签
3)自定义标签 : 开发者自行定义,需要在jsp页面导入标签

JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,==避免在JSP页面中直接编写java代码,造成jsp页面难以维护。==

常用内置标签:

1、标签一<<jsp:include>>
2、标签<<jsp:forward>>和<<jsp:param>>
<<jsp:forward>>标签用于把给另外一个资源(服务器跳转,地址不变)。

<%--使用jsp:forward标签进行请求转发--%>
<jsp:forward page="/index2.jsp" >
<jsp:param value="10086" name="num"/>
<jsp:param value="10010" name="num2"/>
</jsp:forward>

五、九大内置对象

对象名 描述 类型 作用域
request 请求对象 javax.servlet.ServletRequest Request
response 响应对象 javax.servlet.SrvletResponse Page
pageContext 页面上下文对象 javax.servlet.jsp.PageContext Page
session 会话对象 javax.servlet.http.HttpSession Session
application 应用程序对象 javax.servlet.ServletContext Application
out 输出对象 javax.servlet.jsp.JspWriter Page
config 配置对象 javax.servlet.ServletConfig Page
page 页面对象 javax.lang.Object Page
exception 例外对象 javax.lang.Throwable Page

六、JSP属性作用域

  1. 当前页(pageContext):一个属性只能在==一个页面==中取得,跳转到其他页面无法取得
  2. 一次服务器请求(request):一个页面中设置的属性,只要经过了请求重定向之后的页面可以继续取得。
  3. 一次会话(session):一个用户设置的内容,只要是与此用户相关的页面都可以访问(一个会话表示一个人,这个人设置的东西只要这个人不走,就依然有效),关了浏览器就不见了。
  4. 上下文中(application):在==整个服务器==上设置的属性,所有人都可以访问

七、静态资源的路径问题

模板:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
    <head>
        <base href="<%=basePath%>">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>title</title>
    </head>
    <body>
        这是模板
    </body>
</html>

八、错误页面、404页面、欢迎页

模板:

<error-page>
    <error-code>404</error-code>
    <location>/pages/404.jsp</location>
</error-page>

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/pages/err.jsp</location>
</error-page>

<welcome-file-list>
    <welcome-file>/pages/home.jsp</welcome-file>
</welcome-file-list>

第四章 EL表达式和JSTL标签库

一、EL表达式

1、特点

(1)是一个由java开发的工具包
(2)用于从特定域对象中读取并写入到响应体开发任务,不能向域对象中写入。
(3)EL工具包自动存在==Tomcat的lib中(el-api.jar)==,开发是可以直接使用,无需其他额外的包。
(4)标准格式 : ${域对象别名.。关键字} 到指定的域中获取相应关键字的内容,并将其写入到响应体。

2、域对象

jsp el 描述
application applicationScope 全局作用域对象
session sessionScope 会话作用域
request requestScope 请求作用域对象
pageContext pageScope 当前页作用域对象

默认查找顺序: pageScope -> requestScope -> sessionScope -> applicationScope

案例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>jsp</title>
</head>
<body>
    <%
        application.setAttribute("name","application");
        session.setAttribute("name","session");
        request.setAttribute("name","request");
        pageContext.setAttribute("name","pageContext");
    %>
    <br>--------------------使用java语言---------------------------<br>
        application中的值:<%= application.getAttribute("name") %> <br>
        session中的值:<%= session.getAttribute("name") %> <br>
        request中的值:<%= request.getAttribute("name") %> <br>
        pageContext中的值:<%= pageContext.getAttribute("name") %> <br>
    <br>--------------------使用EL表达式---------------------------<br>
        application中的值:${applicationScope.name} <br>
        session中的值:${sessionScope.name} <br>
        request中的值:${requestScope.name} <br>
        pageContext中的值:${pageScope.name} <br>
    <br>----------------使用EL表达式,省略域对象---------------------<br>
        application中的值:${name} <br>
</body>
</html>

3、支持的运算

(1)数学运算
(2)比较运算 > gt < lt >= ge <= le == eq != !=
(3)逻辑预算 && || !

4、其他的内置对象

(1)param 使用 ${param.请求参数名} 从请求参数中获取参数内容
(2)paramValues 使用 ${ paramValues.请求参数名 } 从请求参数中获取多个值,以数组的形式
(3)initParam 使用 ${ initParam.参数名 } 获取初始化参数

5、EL表达式的缺陷

(1)只能读取域对象中的值,不能写入
(2)不支持if判断和控制语句

二、JSTL标签工具类

1、基本介绍

(1) JSP Standrad Tag Lib jsp标准标签库
(2) sun公司提供
(3)组成

  • 核心标签 对java在jsp上基本功能进行封装,如 if while等
  • sql标签 JDBC在jsp上的使用
  • xml标签 Dom4j在jsp上的使用
  • format标签 jsp文件格式转换

2、使用方式

(1)导入依赖的jar包 ==jstl.jar standard.jar==
(2)在jsp中引入JSTL的core包依赖约束

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

3、重要标签的使用

(1) <c:set>

<c:set scope="request" var="name" value="zhangsan" />

(2)<c:if >

<c:set scope="page" var="age" value="20"/>
<c:if test="${ age ge 18 }">

ge 大于等于 le 小于等于 gt大于 lt 小于

(3)<c:choose>

<c:choose>
    <c:when test="${age eq 18}"></c:when>
    <c:otherwise></c:otherwise>
</c:choose>

(4)<c:forEach>

<c:forEach var="申明循环变量的名称" begin="初始化循环变量" end="循环变量可以接受的最大值" step="循环变量的递增或递减值">
*** step属性可以不写,默认递增1
*** 循环变量默认保存在pageContext中
</c:forEach>

第五章 Listener、Filter

一、概念

  • servlet

servlet是一种运行服务器端的java应用程序,他可以用来==处理请求和响应==。

  • filter

过滤器,不像Servlet,它不能产生一个请求或者响应,它是一个中间者,能==修改处理经过他的请求和响应==,并不能直接给客户端响应。

  • listener

监听器,他用来==监听容器内的一些变化==,如session的创建,销毁等。当变化产生时,监听器就要完成一些工作。

二、生命周期

1、servlet:

servlet的生命周期始于它被装入web服务器的内存时,并在web服务器终止或重新装入servlet时结束。servlet一旦被装入web服务器,一般不会从web服务器内存中删除,直至web服务器关闭或重新结束。

  1. 装入:启动服务器时加载Servlet的实例;
  2. 初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工

作有init()方法负责执行完成;

  1. 调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法;
  2. 销毁:停止服务器时调用destroy()方法,销毁实例。

2、filter:

实现javax.servlet包的Filter接口的三个方法==init()、doFilter()、destroy()==,空实现也行

  1. 启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
  2. 每一次请求时都只调用方法doFilter()进行处理;
  3. 停止服务器时调用destroy()方法,销毁实例。

3、listener:

servlet2.4规范中提供了8个listener接口,可以将其分为三类:

  • 第一类:与servletContext有关的listner接口。包括:ServletContextListener、ServletContextAttributeListener
  • 第二类:与HttpSession有关的Listner接口。包括:HttpSessionListner、HttpSessionAttributeListener、HttpSessionBindingListener、HttpSessionActivationListener;
  • 第三类:与ServletRequest有关的Listener接口,包括:ServletRequestListner、ServletRequestAttributeListener

==web.xml 的加载顺序是:context- param -> listener -> filter -> servlet==

三、使用方式

listener:

public class TestListener implements HttpSessionListener,ServletRequestListener,ServletRequestAttributeListener {
    private Logger logger = LoggerFactory.getLogger(TestListener.class);
    //sessionListener start!
    public void sessionCreated(HttpSessionEvent arg0) {
        logger.info(".......TestListener sessionCreated().......");
    } 
    public void sessionDestroyed(HttpSessionEvent arg0) {
        logger.info(".......TestListener sessionDestroyed().......");
    } 
    //sessionListener end!
    //requestListener start!
    public void requestInitialized(ServletRequestEvent arg0) {
        logger.info("......TestListener requestInitialized()......");
    } 
    public void requestDestroyed(ServletRequestEvent arg0) {
        logger.info("......TestListener requestDestroyed()......");
    } 
    //requestListener end!
    //attributeListener start!
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        logger.info("......TestListener attributeAdded()......");
    } 
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        logger.info("......TestListener attributeRemoved()......");
    }
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        logger.info("......TestListener attributeReplaced()......");
    } 
    //attributeListener end!
}

Filter:

public class TestFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(TestFilter.class);
    public void destroy() {
        logger.info("..............execute TestFilter destory()..............");
    } 
    public void doFilter(ServletRequest arg0, ServletResponse arg1,FilterChain arg2) throws IOException, ServletException {
        logger.info("..............execute TestFilter doFilter()..............");
        arg2.doFilter(arg0, arg1);
    } 
    public void init(FilterConfig arg0) throws ServletException {
        logger.info("..............execute TestFilter init()..............");
    }
}

Servlet:

public class TestServlet extends HttpServlet {
    private Logger logger = LoggerFactory.getLogger(TestServlet.class);
    private static final long serialVersionUID = -4263672728918819141L;
    @Override
    public void init() throws ServletException {
        logger.info("...TestServlet init() init..........");
        super.init();
    } 
    @Override
    public void destroy() {
        logger.info("...TestServlet init() destory..........");
        super.destroy();
    } 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    } 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        logger.info("...TestServlet doPost() start..........");
        //操作attribute
        request.setAttribute("a", "a");
        request.setAttribute("a", "b");
        request.getAttribute("a");
        request.removeAttribute("a");
        //操作session
        request.getSession().setAttribute("a", "a");
        request.getSession().getAttribute("a");
        request.getSession().invalidate();
        logger.info("...TestServlet doPost() end..........");
    }
}

配置XML:

<!-- 测试filter -->
<filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>com.xy.web.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>*.do</url-pattern>
</filter-mapping>

<!-- 测试servlet -->
<servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>com.xy.web.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<!-- 测试listener -->
<listener>
    <listener-class>com.xy.web.listener.TestListener</listener-class>
</listener>

第六章 常用模板

Filter-解决乱码:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 解决全站乱码问题,处理所有的请求
 */
@WebFilter("/*")
public class CharchaterFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse rep, FilterChain filterChain) throws IOException, ServletException {
        //将父接口转为子接口
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) rep;
        //获取请求方法
        String method = request.getMethod();
        //解决post请求中文数据乱码问题
        if(method.equalsIgnoreCase("post")){
            request.setCharacterEncoding("utf-8");
        }
        //处理响应乱码
        response.setContentType("text/html;charset=utf-8");
        filterChain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

数据源(druid) db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root

filters=wall,stat
maxActive=20
initialSize=3
maxWait=5000
minIdle=3
maxIdle=15
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 'x'
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
maxOpenPreparedStatements=20
removeAbandoned=true
removeAbandonedTimeout=1800
logAbandoned=true

日志logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 定义参数常量 -->
    <!-- 日志级别 TRACE<DEBUG<INFO<WARN<ERROR -->
    <!-- logger.trace("msg") logger.debug... -->
    <property name="log.level" value="debug"/>
    <property name="log.maxHistory" value="30"/>
    <property name="log.filePath" value="E:/log"/>
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <!-- 控制台输出设置 -->
    <appender name="consoleAppender"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- DEBUG级别文件记录 -->
    <appender name="debugAppender"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径 -->
        <file>${log.filePath}/debug.log</file>
        <!-- 滚动日志文件类型,就是每天都会有一个日志文件 -->
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 文件名称 -->
            <fileNamePattern>${log.filePath}/debug/debug.%d{yyyy-MMdd}.log.gz
            </fileNamePattern>
            <!-- 文件最大保存历史数量 -->
            <maxHistory>${log.maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- DEBUG级别文件记录 -->
    <appender name="errorAppender"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径 -->
        <file>${log.filePath}/debug.log</file>
        <!-- 滚动日志文件类型,就是每天都会有一个日志文件 -->
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 文件名称 -->
            <fileNamePattern>${log.filePath}/debug/debug.%d{yyyy-MMdd}.log.gz
            </fileNamePattern>
            <!-- 文件最大保存历史数量 -->
            <maxHistory>${log.maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- INFO -->
    <appender name="infoAppender"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径 -->
        <file>${log.filePath}/info.log</file>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 文件名称 -->
            <fileNamePattern>${log.filePath}/info/info.%d{yyyy-MM-dd}.log.gz
            </fileNamePattern>
            <!-- 文件最大保存历史数量 -->
            <maxHistory>${log.maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- com.xinzhi开头的日志对应形式 -->
    <logger name="cn.itnanls" level="${log.level}" additivity="true">
        <appender-ref ref="debugAppender"/>
        <appender-ref ref="infoAppender"/>
        <appender-ref ref="errorAppender"/>
    </logger>
    <!-- <root> 是必选节点,用来指定最基础的日志输出级别,只有一个level属性 -->
    <root level="info">
        <appender-ref ref="consoleAppender"/>
    </root>
    <!-- 捕捉sql开头的日志 -->
    <appender name="MyBatis"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.filePath}/sql_log/mybatis-sql.log</file>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.filePath}/sql_log/mybatissql.log.%d{yyyy-MM-dd}</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder
                class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%thread|%d{yyyy-MM-dd
                HH:mm:ss.SSS}|%level|%logger{36}|%m%n
            </pattern>
        </encoder>
    </appender>
    <logger name="sql" level="DEBUG">
        <appender-ref ref="MyBatis"/>
    </logger>
</configuration>

日志需要依赖的包

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        这个factory加载配置文件用!!!
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

Servlet-退出

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

@WebServlet("/exitServlet")
public class ExitServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.销毁session
        request.getSession().invalidate();

        //2.跳转登录页面
        response.sendRedirect(request.getContextPath()+"/login.html");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

Util-JDBCUtiles

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/*
    1. 声明静态数据源成员变量
    2. 创建连接池对象
    3. 定义公有的得到数据源的方法
    4. 定义得到连接对象的方法
    5. 定义关闭资源的方法
 */
public class JDBCUtils {
    // 1.    声明静态数据源成员变量
    private static DataSource ds;

    // 2. 创建连接池对象
    static {
        // 加载配置文件中的数据
        InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        Properties pp = new Properties();
        try {
            pp.load(is);
            // 创建连接池,使用配置文件中的参数
            ds = DruidDataSourceFactory.createDataSource(pp);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 3. 定义公有的得到数据源的方法
    public static DataSource getDataSource() {
        return ds;
    }

    // 4. 定义得到连接对象的方法
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    // 5.定义关闭资源的方法
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {}
        }

        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {}
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {}
        }
    }

    // 6.重载关闭方法
    public static void close(Connection conn, Statement stmt) {
        close(conn, stmt, null);
    }
}

Util-Jedis

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

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

/**
 * Jedis工具类
 */
public final class JedisUtil {
    private static JedisPool jedisPool;

    static {
        //读取配置文件
        InputStream is = JedisPool.class.getClassLoader().getResourceAsStream("jedis.properties");
        //创建Properties对象
        Properties pro = new Properties();
        //关联文件
        try {
            pro.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取数据,设置到JedisPoolConfig中
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
        config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));

        //初始化JedisPool
        jedisPool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));


    }


    /**
     * 获取连接方法
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 关闭Jedis
     */
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}
相关文章
|
4月前
|
JSON Java 应用服务中间件
JavaWeb项目之乱码问题及如何解决
JavaWeb项目之乱码问题及如何解决
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
3月前
|
存储 前端开发 JavaScript
基于JavaWeb实现停车场管理系统
基于JavaWeb实现停车场管理系统
|
3月前
|
前端开发 JavaScript Java
图书借阅管理平台|基于JavaWeb实现图书借阅系统
图书借阅管理平台|基于JavaWeb实现图书借阅系统
|
6天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
3月前
|
前端开发 Java 关系型数据库
JavaWeb开发简介
JavaWeb开发简介
41 0
|
7天前
|
SQL JSON JavaScript
JavaWeb基础9——VUE,Element&整合Javaweb的商品管理系统
Vue 指令、生命周期、this和$、vue脚手架进行模块化开发/ElementUI框架、综合案例,element商品列表展示增删改查
JavaWeb基础9——VUE,Element&整合Javaweb的商品管理系统
|
3月前
|
SQL Java 数据库连接
JavaWeb Mapper代理开发
JavaWeb Mapper代理开发
|
2月前
|
存储 程序员
JavaWeb之Listener监听器
JavaWeb之Listener监听器
33 0
|
3月前
|
前端开发 JavaScript 算法
javaweb(四)——过滤器与监听器
javaweb(四)——过滤器与监听器