第一章 Tomcat 基础
一、 web 概念
1. 软件架构
- C/S: 客户端/服务器端
- B/S: 浏览器/服务器端
2.资源分类
- 静态资源: 所有用户访问后,得到的==结果都是一样==的,称为静态资源。静态资源可以==直接被浏览器解析==。如图片、视频。
- 动态资源: 每个用户访问==相同资源==后,得到的==结果可能不一样== , 称为动态资源。动态资源被访问后,需要==先转换为静态资源==再==返回给浏览器==通过==浏览器进行解析==。如:servlet,jsp,php,asp....
二、常见的web服务器
1、 概念
- 服务器:安装了服务器软件的计算机
- 服务器软件:接收用户的请求,处理请求,做出响应
- 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:
- 第一种是编程式,即继承类javax.websocket.Endpoint并实现其方法。
- 第二种是注解式,即定义一个POJO对象,为其添加Endpoint相关的注解。
Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。Endpoint接口明确定义了与其生命周期相关的方法,规范实现者确保在生命周期的各个阶段调用实例的相关方法。
3.2 Endpoint:
3.2.1 生命周期:
- onOpen:当开启一个新的会话时调用。这是客户端与服务器握手成功后调用的方法。等同于注解@OnOpen。
- onClose:当会话关闭时调用。等同于注解@OnClose。
- onError:当链接过程中异常时调用。等同于注解@OnError。
当客户端链接到一个Endpoint时,服务器端会为其创建一个唯一的会话(javax.websocket.Session)。会话在WebSocket握手之后创建,并在链接关闭时结束。当生命周期中触发各个事件时,都会将当前会话传给Endpoint。
3.2.2 接受、发送消息:
- 接受消息:
我们通过为Session添加==MessageHandler消息处理器来接收消息==。当采用注解方式定义Endpoint时,我们还可以通过 ==@OnMessage== 指定接收消息的方法。
- 发送消息:
发送消息则由==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通常需要两步走。
- 写一个java程序定义一个servlet
- 配置一下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、匹配顺序
- 精确匹配。
- 路径匹配,先最长路径匹配,再最短路径匹配。
- 扩展名匹配。
注意:使用扩展名匹配,前面就不能有任何的路径。
- 缺省匹配,以上都找不到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就是门房==,进去时需要从==门房识别一个身份==。
粗略的描述一下过程:
- 当浏览器向客户端发送请求时,服务器会为他创建一个session,同时相应会加一个头(SetCookie: jsessionid=ewrwerwer123)
- 浏览器得到相应就会在在自己这保存下这个字符串,以后访问这个网站的时候就会一直带着。
- 当下一个请求发起时,会带着这个cookie的信息,服务器通过查询id找的session,通过session内保存的信息,就能获得这个客户端的状态。
第三章 jsp入门
一、JSP基础语法
1、JSP模板元素
JSP页面中的HTML内容称之为JSP模版元素。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
2、JSP脚本片段
JSP脚本片断用于在JSP页面中编写多行Java代码(在<%%>不能定义方法)。
语法:<%多行java代码%>
注意:
- JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
- JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
- 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其
他JSP元素。
- 多个脚本片断中的代码可以相互访问
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在服务器的执行流程
第一次执行:
- 客户端通过电脑连接服务器,因为是请求是动态的,所以所有的请求交给WEB容器来处理
- 在容器中找到需要执行的*.jsp文件
- 之后.jsp文件通过转换变为.java文件
- .java文件经过编译后,形成.class文件
- 最终服务器要执行形成的*.class文件
第二次执行:
- 因为已经存在了*.class文件,所以不在需要转换和编译的过程
修改后执行:
- 源文件已经被修改过了,所以需要重新转换,重新编译。
三、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应用。
注意:
- 被引入的文件必须==遵循JSP语法==。
- 被引入的文件可以使用==任意的扩展名==,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式
处理它里面的内容,为了见明知意,==JSP规范建议使用.jspf==(JSP fragments(片段))作为静态引入文件的扩展名。
- 由于使用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属性作用域
- 当前页(pageContext):一个属性只能在==一个页面==中取得,跳转到其他页面无法取得
- 一次服务器请求(request):一个页面中设置的属性,只要经过了请求重定向之后的页面可以继续取得。
- 一次会话(session):一个用户设置的内容,只要是与此用户相关的页面都可以访问(一个会话表示一个人,这个人设置的东西只要这个人不走,就依然有效),关了浏览器就不见了。
- 上下文中(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服务器关闭或重新结束。
- 装入:启动服务器时加载Servlet的实例;
- 初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工
作有init()方法负责执行完成;
- 调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法;
- 销毁:停止服务器时调用destroy()方法,销毁实例。
2、filter:
实现javax.servlet包的Filter接口的三个方法==init()、doFilter()、destroy()==,空实现也行
- 启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
- 每一次请求时都只调用方法doFilter()进行处理;
- 停止服务器时调用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();
}
}
}