概述
Hyper Text Transfer Protocol
(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。简而言之,就是定义了,客户端和服务器端通信时,发送数据的格式。
主要特点
URI
URI,是 uniform resource identifier
,统一资源标识符,用来唯一的标识一个资源。Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的。
URI一般由三部组成:
URL
URL 是uniform resource locator
,统一资源定位器,它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。URL是Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上,特别是著名的Mosaic。
采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。URL一般由三部组成:
URN
URN,uniform resource name
,统一资源命名,是通过名字来标识资源,比如mailto:[email protected]
。
URI是以一种抽象的,高层次概念定义统一资源标识,而 URL 和URN则是具体的资源标识的方式。URL和URN都是一种URI。笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。上面的 mailto、news 和 isbn URI 都是 URN 的示例。
在Java的 URI 中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息,因此它不能是相对的。
在Java类库中,URI类不包含任何访问资源的方法,它唯一的作用就是解析。相反的是,URL类可以打开一个到达资源的流。
客户端发送一个HTTP请求到服务器的请求消息包括以下四部分
请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1
请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值。
常见的请求头:
User-Agent
:浏览器告诉服务器,使用的浏览器版本信息(用于解决浏览器的兼容性问题)Referer:http://localhost/login.html
告诉服务器,当前请求从哪里来?
请求空行
空行,就是用于分割 POST 请求的请求头,和请求体的。
请求体(正文)
封装POST请求消息的请求参数的
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。
HTTP 响应也由四个部分组成,分别是:
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
状态码 | 响应类别 |
---|---|
1xx | 指示信息–表示请求已接收,继续处理 |
2xx | 成功–表示请求已被成功接收、理解、接受 |
3xx | 重定向–要完成请求必须进行更进一步的操作 |
4xx | 客户端错误–请求有语法错误或请求无法实现 |
5xx | 服务器端错误–服务器未能实现合法的请求 |
选取几个代表的
主要区别
长连接与短连接
客户端会话技术,将数据保存到客户端
实现原理:基于响应头 set-cookie和 请求头 cookie 实现
使用步骤:
new Cookie(String name, String value)
response.addCookie(Cookie cookie)
Cookie[] request.getCookies()
一些细节
一次可不可以发送多个cookie?
可以创建多个Cookie对象,使用 response 调用多次 addCookie 方法发送 cookie 即可。
cookie 在浏览器中保存多长时间?
setMaxAge(int seconds)
cookie 能不能存中文?
cookie共享问题?
假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
setPath(String path)
:设置cookie的获取范围。默认情况下,设置当前的虚拟目录"/"
不同的tomcat服务器间cookie共享问题?
setDomain(String path)
:如果设置一级域名相同,那么多个服务器之间cookie可以共享setDomain(".baidu.com")
那么tieba.baidu.com
和news.baidu.com
中cookie可以共享服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
原理: Session的实现是**依赖于 Cookie **的。
快速使用
HttpSession session = request.getSession();
Object getAttribute(String name)
void setAttribute(String name, Object value)
void removeAttribute(String name)
一些细节
当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
JSESSIONID
,设置最大存活时间,让 cookie 持久化保存。 Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
session什么时候被销毁?
invalidate()
。选择性配置修改
<session-config>
<session-timeout>30session-timeout>
session-config>
两者区分
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
Cookie 一般用来保存用户信息。比如:
Session 的主要作用就是通过服务端记录用户的状态。
典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
session可以存储任意类型,任意大小的数据
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
简而言之
HTTP是不保存状态的协议,如何保存用户状态?
HTTP 是一种不保存状态,即无状态(stateless)协议。
Session 机制的存在就是为了解决这个问题,Session 的主要作用就是通过服务端记录用户的状态。典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个 Session,过了时间限制,就会销毁这个Session)。
在服务端保存 Session 的方法很多,最常用的就是内存和数据库(比如是使用内存数据库redis保存)。既然 Session 存放在服务器端,那么我们如何实现 Session 跟踪呢?大部分情况下,我们都是通过在 Cookie 中附加一个 Session ID
来方式来跟踪。
Cookie 被禁用怎么办?
最常用的就是利用 URL 重写把 Session ID 直接附加在URL路径的后面。
常见的java相关的web服务器软件:
搭建
下载:http://tomcat.apache.org/
安装:解压压缩包即可。安装目录建议不要有中文和空格
卸载:删除目录就行了
启动:
bin/startup.bat
,双击运行该文件即可http://localhost:8080
回车访问自己; http://别人的ip:8080
访问别人可能遇到的问题
netstat -ano
找到占用的端口号,并且找到对应的进程,杀死该进程conf/server.xml
中修改自身的端口号关闭:
bin/shutdown.bat
配置部署
建议参考
直接将项目放到 webapps 目录下即可。
* /hello
:项目的访问路径–>虚拟目录
配置conf/server.xml
文件
标签体中配置
conf\Catalina\localhost
创建任意名称的 xml 文件。在文件中编写
目录结构
java 动态项目的目录结构:
- 项目的根目录
-- WEB-INF目录:
--- web.xml:web项目的核心配置文件
--- classes目录:放置字节码文件的目录
--- lib目录:放置依赖的jar包
概述
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。参考菜鸟教程
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
//Servlet的生命周期:从Servlet被创建到Servlet被销毁的过程
//一次创建,到处服务
//一个Servlet只会有一个对象,服务所有的请求
/*
* 1.实例化(使用构造方法创建对象)
* 2.初始化 执行init方法
* 3.服务 执行service方法
* 4.销毁 执行destroy方法
*/
public class ServletDemo1 implements Servlet {
//public ServletDemo1(){}
//生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
public void init(ServletConfig arg0) throws ServletException {
System.out.println("=======init=========");
}
//生命周期方法:对客户端响应的方法,该方法会被执行多次,每次请求该servlet都会执行该方法
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("hehe");
}
//生命周期方法:当Servlet被销毁时执行该方法
public void destroy() {
System.out.println("******destroy**********");
}
//当停止tomcat时也就销毁的servlet。
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
}
public class ServletDemo2 extends GenericServlet {
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("heihei");
}
}
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("haha");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("ee");
doGet(req,resp);
}
}
Servlet的体系结构
Servlet -- 接口
|
GenericServlet -- 抽象类
|
HttpServlet -- 抽象类
GenericServlet:将 Servlet 接口中其他的方法做了默认空实现,只将 service()
方法作为抽象
service()
方法即可HttpServlet:对 http 协议的一种封装,简化操作
在 web.xml
中配置:
<servlet>
<servlet-name>demo1servlet-name>
<servlet-class>cn.leyou.web.servlet.ServletDemo1servlet-class>
servlet>
<servlet-mapping>
<servlet-name>demo1servlet-name>
<url-pattern>/demo1url-pattern>
servlet-mapping>
执行原理
web.xml
文件,是否有对应的
标签体内容。
全类名Servlet 的生命周期: 从 Servlet 被创建到 Servlet 被销毁的过程:
实例化:使用构造方法创建对象
初始化:执行 init 方法
服务:执行 service 方法
销毁:执行 destroy 方法
被创建:执行 init 方法,只执行一次
Servlet什么时候被创建?
标签下配置
的值为负数
的值为0或正整数Servlet的 init 方法,只执行一次,说明一个Servlet 在内存中只存在一个对象,Servlet 是单例的
提供服务:执行service方法,执行多次
每次访问 Servlet 时,Service 方法都会被调用一次。
被销毁:执行destroy方法,只执行一次
Servlet3.0 支持注解配置。可以不需要 web.xml 了。
@WebServlet("资源路径")
@WebServlet({"/d4","/dd4","/ddd4"})
路径定义规则:
注解配置的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";//相当于
String[] value() default {};//代表urlPatterns()属性配置
String[] urlPatterns() default {};//相当于
int loadOnStartup() default -1;//相当于
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig 对象中,并在调用 servlet 的 init() 方法时,将 ServletConfig 对象传递给 servlet 。进而,通过ServletConfig 对象就可以得到当前 servlet 的初始化参数。 参考链接
相关方法
String getServletName()
– 获取当前Servlet在web.xml中配置的名字String getInitParameter(String name)
– 获取当前Servlet指定名称的初始化参数的值Enumeration getInitParameterNames()
– 获取当前Servlet所有初始化参数的名字组成的枚举ervletContext getServletContext()
– 获取代表当前web应用的ServletContext对象1)初始化参数配置在
标签中
<servlet>
<servlet-name>springServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:/conf/app-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
2)服务器会自动封装在ServletConfig中,我们利用init()或者init(ServletConfig config)方法,可以只获取一次ServletConfig对象,然后通过其getInitParameter()方法,获取配置好的参数。
ServletConfig 对象也可以直接通过父类 GenericServlet 封装好的 getServletConfig() 方法获取或直接通过父类封装的 getInitParameter() 直接获取。
@Override
public void init(ServletConfig config) throws ServletException {
// 如果重写父类带参数的init()方法,这句一定写。
// 不然拿不到ServletConfig 对象,空指针。
// 原因在于,父类中代参的init()实现的时候,先this.config = config;然后调用无参this.init();
super.init(config);
String userName = config.getInitParameter("userName");
String userName2 = this.getInitParameter("userName");
String userName3 = this.getServletConfig().getInitParameter("userName");
logger.debug("初始化参数1: "+userName+" | "+"初始化参数2: "+userName2+" | 初始化参数3: "+userName3);
}
@Override
public void init() throws ServletException {
String userName2 = this.getInitParameter("userName");
String userName3 = this.getServletConfig().getInitParameter("userName");
logger.debug("初始化参数4: "+userName2+" | 初始化参数5: "+userName3);
}
3)注意点:如果重写父类带参数的 init() 方法,这句一定写,super.init(config),不然拿不到 ServletConfig 对象,空指针。原因在于,父类中代参的init()实现的时候,先this.config = config;然后调用无参this.init()。其实你复写两个 init() 方法后,输出的顺序也解释了这一点。推荐使用不带参数的 init() 方法。父类代码以及上例输出如下:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
4)通过 getInitParameterNames() ,可以获取枚举类型的所有初始化参数。
request对象继承体系结构:
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)
接下来我们将介绍他的功能
获取请求行数据 :
GET /day14/demo1?name=zhangsan HTTP/1.1
String getMethod()
/day14
String getContextPath()
/demo1
String getServletPath()
name=zhangsan
String getQueryString()
/day14/demo1
String getRequestURI()
: `/day14/demo1``:
http://localhost/day14/demo1`http://localhost/day14/demo1
xxxxx医院/day14/demo1
医院String getRemoteAddr()
获取请求头数据
String getHeader(String name)
:通过请求头的名称获取请求头的值Enumeration getHeaderNames()
:获取所有的请求头名称获取请求体数据
请求体:只有 POST 请求方式,才有请求体,在请求体中封装了POST请求的请求参数
获取流对象,再从流对象中拿数据
BufferedReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有类型数据不论 get 还是 post 请求方式都可以使用下列方法来获取请求参数
String getParameter(String name)
:根据参数名称获取参数值 username=zs&password=123
String[] getParameterValues(String name)
:根据参数名称获取参数值的数组 hobby=xx&hobby=game
Enumeration getParameterNames()
:获取所有请求的参数名称Map getParameterMap()
:获取所有参数的 map集合中文乱码
request.setCharacterEncoding("utf-8")
;一种在服务器内部的资源跳转方式
RequestDispatcher getRequestDispatcher(String path)
forward(ServletRequest request, ServletResponse response)
特点
共享数据
域对象:一个有作用范围的对象,可以在范围内共享数据
request 域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
相关方法
void setAttribute(String name,Object obj)
:存储数据Object getAttitude(String name)
:通过键获取值void removeAttribute(String name)
:通过键移除键值对ServletContext getServletContext()
:获取ServletContext设置响应行
setStatus(int sc)
设置响应头
setHeader(String name, String value)
设置响应体
获取输出流,使用输出流,将数据输出到客户端浏览器
PrintWriter getWriter()
ServletOutputStream getOutputStream()
重定向:资源跳转的方式
//1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/day15/responseDemo2");
/简单的重定向方法
response.sendRedirect("/day15/responseDemo2");
过程分析
重定向过程:
客户浏览器发送http请求——>web服务器接受后发送302状态码响应及对应新的location给客户浏览器——>客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址——>服务器根据此请求寻找资源并发送给客户。
在这里location可以重定向到任意URL, 既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向 的路径,客户可以观察到地址的变化的。 重定向行为是浏览器做了至少两次的访问请求的。
转发过程:
客户浏览器发送http请求——>web服务器接受此请求——>调用内部的一个方法在容器内部完成请求处理和转发动作——>将目标资源发送给客户;
在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。 在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向的特点:redirect
转发的特点:forward
相对路径:通过相对路径不可以确定唯一资源
不以/
开头,以.
开头路径,如:./index.html
规则:找到当前资源和目标资源之间的相对位置关系
./
:当前目录../
:后退一级目录绝对路径:通过绝对路径可以确定唯一资源
以/
开头的路径如:
http://localhost/day15/responseDemo2
/day15/responseDemo2
转发还是重定向?
规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
request.getContextPath()
,
重定向…乱码问题
设置编码,是在获取流之前设置:response.setContentType("text/html;charset=utf-8")
;
概述
更多详细,参考原文链接
ServletContext 的获取
SevletConfig.getServletContext()
方法getServletContext ()
方法即可获取。
this.getServletContext();
request.getServletContext();
接下来我们介绍几个它的功能
获取MIME类型
MIME 类型:在互联网通信过程中定义的一种文件数据类型
String getMimeType(String file)
域对象:共享数据
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
ServletContext 对象范围:所有用户所有请求的数据
获取文件的真实(服务器)路径
String getRealPath(String path)
web 中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。如:登录验证、统一编码处理、敏感字符过滤…
快速入门
web.xml
@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
//实现 Filter 类
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// Filter 的 init 方法中提供了一个 FilterConfig 对象。
//获取初始化参数
String site = config.getInitParameter("Site");
// 输出初始化参数
System.out.println("网站名称: " + site);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 输出站点名称
System.out.println("站点网址:http://www.runoob.com");
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
一些细节
web.xml
配置
<web-app>
<filter>
<filter-name>LogFilterfilter-name>
<filter-class>com.runoob.test.LogFilterfilter-class>
<init-param>
<param-name>Siteparam-name>
<param-value>菜鸟教程param-value>
init-param>
filter>
<filter-mapping>
<filter-name>>LogFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<servlet>
<servlet-name>DisplayHeaderservlet-name>
<servlet-class>com.runoob.test.DisplayHeaderservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DisplayHeaderservlet-name>
<url-pattern>/TomcatTest/DisplayHeaderurl-pattern>
servlet-mapping>
web-app>
过滤器生命周期方法
init
:在服务器启动后,会创建Filter对象,然后调用 init 方法。只执行一次。用于加载资源doFilter
:每一次请求被拦截资源时,会执行。执行多次destroy
:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行 destroy 方法。只执行一次。用于释放资源过滤器配置详解
拦截路径配置:
/index.jsp
只有访问index.jsp
资源时,过滤器才会被执行/user/*
访问/user
下的所有资源时,过滤器都会被执行*.jsp
访问所有后缀名为 jsp 资源时,过滤器都会被执行/*
访问所有资源时,过滤器都会被执行拦截方式配置:资源被访问的方式
dispatcherTypes
属性
web.xml
配置:设置
标签即可,可以设置多个过滤器执行流程
过滤器链(配置多个过滤器)
执行顺序:如果有两个过滤器:过滤器1和过滤器2
过滤器先后顺序问题
谁定义在上边,谁先执行案例:过滤敏感词汇
web 的三大组件(Servlet、Filter、Listener)之一。
事件监听机制
* 事件 :一件事情
ServletContextListener: 监听 ServletContext 对象的创建和销毁
void contextDestroyed(ServletContextEvent sce)
:ServletContext 对象被销毁之前会调用该方法void contextInitialized(ServletContextEvent sce)
:ServletContext 对象创建后会调用该方法配置
<listener> <listener-class>cn.leyou.web.listener.ContextLoaderListenerlistener-class>
listener>
参考链接
Servlet 不是线程安全的。
每一个 Servlet 对象再Tomcat容器中只有一个实例对象,即是单例模式,共享一个对象
ServletRequest 对象是线程安全的
对于每一个请求由一个工作线程来执行,都会创建一个
ServletContext 是线程不安全
ServletContext:它是线程不安全的,多线程下可以同时进行读写,因此我们要对其读写操作进行同步或者深度的clone。
HttpSession:同样是线程不安全的,和 ServletContext 的操作一样。
Application对象也是线程不安全的
Application 对象用于存储和访问来自任何页面的变量,类似于 session 对象。不同之处在于,所有的用户分享一个 Application 对象,而 session 对象和用户的关系是一一对应的。
Spring 中 bean对象的作用域
默认 spring 容器中的 bean 是单例的。当单例中存在竞态条件,即有线程安全问题。spring 并不能保证 bean 的线程安全。
作用域 | 说明 |
---|---|
singleton | 默认的作用域,这种情况下的bean都会被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但出于Spring懒加载机制,只有在第一次被使用时才会创建) |
prototype | bean被定义为在每次注入时都会创建一个新的对象 |
request | bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象 |
session | bean被定义为在一个session的生命周期内创建一个单例对象 |
application | bean被定义为在ServletContext的生命周期中复用一个单例对象 |
websocket | bean被定义为在websocket的生命周期中复用一个单例对象 |
此处参考
第一步:导入log4j-1.2.15.jar(版本自定)包,在src目录下创建
src/com/company/resource/log4J.properties
文件。
log4j.appender.INFO.File
指定日志文件存储目录;log4j.appender.INFO.layout.ConversionPattern
形成的日志不仅包括打印的日志信息,还会存储日志产生的时间和产生日志的方法(具体参数可百度log4j.rootLogger=DEBUG,CONSOLE,A,INFO,ERROR,WARN
log4j.addivity.org.apache=false
#console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.CONSOLE.Target=System.log
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#info
#log4j.logger.INFO=INFO
log4j.appender.INFO=org.apache.log4j.DailyRollingFileAppender
log4j.appender.INFO.File= ${catalina.home}/logs/CcbClient/info/info.log
log4j.appender.INFO.Append=true
log4j.appender.INFO.Threshold=INFO
log4j.appender.INFO.layout=org.apache.log4j.PatternLayout
log4j.appender.INFO.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.INFO.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.INFO.filter.F1.LevelMin=INFO
log4j.appender.INFO.filter.F1.LevelMax=INFO
#debug
#log4j.logger.DEBUG=DEBUG
log4j.appender.A=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A.File= ${catalina.home}/logs/CcbClient/debug/debug.log
log4j.appender.A.Append=true
log4j.appender.A.Threshold=DEBUG
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.A.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.A.filter.F1.LevelMin=DEBUG
log4j.appender.A.filter.F1.LevelMax=DEBUG
#error
#log4j.logger.ERROR=ERROR
log4j.appender.ERROR=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ERROR.File=${catalina.home}/logs/CcbClient/error/error.log
log4j.appender.ERROR.Append=true
log4j.appender.ERROR.Threshold=ERROR
log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout
log4j.appender.ERROR.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.ERROR.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.ERROR.filter.F1.LevelMin=ERROR
log4j.appender.ERROR.filter.F1.LevelMax=ERROR
#WANR
log4j.appender.WARN=org.apache.log4j.DailyRollingFileAppender
log4j.appender.WARN.File=${catalina.home}/logs/CcbClient/warn/warn.log
log4j.appender.WARN.Append=true
log4j.appender.WARN.Threshold=WARN
log4j.appender.WARN.layout=org.apache.log4j.PatternLayout
log4j.appender.WARN.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.WARN.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.WARN.filter.F1.LevelMin=WARN
log4j.appender.WARN.filter.F1.LevelMax=WARN
该配置可以在控制台或者后台文件中输出日志记录。info,error,debug都可以输出。导出文件路径自己可以修改,格式内容自己也可以修改。
第二步:在 src/util 下创建 log4j 加载文件(servlet)
public class Log4jInit extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j-init-file");
if (file != null) {
System.out.println("read log4j.properties:"+prefix + file);
PropertyConfigurator.configure(prefix + file);
}
}
}
第三步:在 web.xml 中配置 log4j 加载 servlet 路径,项目启动时进行加载
<servlet>
<servlet-name>log4j-initservlet-name>
<servlet-class>util.Log4jInitservlet-class>
<init-param>
<param-name>log4j-init-fileparam-name>
<param-value>\WEB-INF\classes\com\company\resource\log4J.propertiesparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
第四步:在需要输入日志信息的类中,创建 log 对象进行引用
public Logger log=Logger.getLogger(GetAccountInfo.class);
log.info("something");
log.debug("something");
log.warn("something");
log.error("something");
用户登录案例需求:
创建项目,导入html页面,配置文件,jar包
创建数据库环境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
创建包 cn.leyou.domain ,创建类 User
/**
* 用户的实体类
*/
public class User {
private int id;
private String username;
private String password;
//省略 getter,setter及tostring
}
创建包 cn.leyou.util ,编写工具类 JDBCUtils
/**
* JDBC工具类 使用Durid连接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接Connection对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
创建包 cn.leyou.dao ,创建类UserDao,提供login方法
/**
* 操作数据库中User表的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登录方法
* @param loginUser 只有用户名和密码
* @return user包含用户全部数据,没有查询到,返回null
*/
public User login(User loginUser){
try {
//1.编写sql
String sql = "select * from user where username = ? and password = ?";
//2.调用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//记录日志
return null;
}
}
}
使用 queryForObject 需注意:
queryForObject
会抛出 EmptyResultDataAccessException
或 IncorrectResultSetColumnCountException
的异常,而如果干脆使用方法 query ,或者 queryForList 则可以在编码中处理掉这种问题,而无需 try-catch编写 cn.leyou.web.servlet.LoginServlet类
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
//2.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封装user对象
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if(user == null){
//登录失败
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登录成功
//存储数据
req.setAttribute("user",user);
//转发
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
编写FailServlet和SuccessServlet类
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取request域中共享的user对象
User user = (User) request.getAttribute("user");
if(user != null){
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录成功!"+user.getUsername()+",欢迎您");
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录失败,用户名或密码错误");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}