本篇文章主要是作者学习Servlet的一些小总结,希望能帮到您,如果有什么不妥当的地方希望得到指出,谢谢阅读
Java学习大纲:https://blog.csdn.net/weixin_39778570/article/details/94667501
JavaWeb:https://blog.csdn.net/weixin_39778570/article/details/97800485
请仔细阅读目录,点击可进行跳转
servlet 生命周期详解
servlet程序是由web服务器调用,web服务器实现了对servlet生命周期的管理。当你的应用加载并使用一个Servlet时,从初始化到销毁这个Servlet期间会发生一系列的事件,这些事件叫做Servlet的生命周期事件(或方法)。Servlet 生命周期可以归纳为:Servlet 加载—>实例化—>服务—>销毁
每个Servlet的运行都遵循如下生命周期
1. 创建Servlet实例
web容器负责加载Servlet,当web容器启动时或者是在第一次使用这个Servlet时,容器会负责创建Servlet实例,但是用户必须通过部署描述符(web.xml)指定Servlet的位置,也就是Servlet所在的类名称,成功加载后,web容器会通过反射的方式对Servlet进行实例化。
2. WEB容器调用Servlet的init()方法,对Servlet进行初始化
在Servlet实例化之后,Servlet容器会调用init()方法,来初始化该对象,主要是为了让Servlet对象在处理客户请求前可以完成一些初始化的工作,例如,建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只能被调用一次。init()方法有一个类型为ServletConfig的参数,Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从Web应用程序的配置信息中获取以名-值对形式提供的初始化参数。另外,在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和它的Servlet容器进行通信。无论有多少客户机访问Servlet,都不会重复执行init()。
3. Servlet初始化之后,将一直存在于容器中,service()响应客户端请求
1. 如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求
2. 如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求
3. 或者统一用service()方法处理来响应用户请求
service()是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。要注意的是,在service()方法被容器调用之前,必须确保init()方法正确完成。容器会构造一个表示客户端请求信息的请求对象(类型为ServletRequest)和一个用于对客户端进行响应的响应对象(类型为ServletResponse)作为参数传递给service()方法。在service()方法中,Servlet对象通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。
4. WEB容器决定销毁Servlet时,先调用Servlet的destroy()方法,通常在关闭web应用之前销毁Servlet
destroy()仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当容器检测到一个Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy()方法,以便让Servlet对象可以释放它所使用的资源,保存数据到持久存储设备中,例如,将内存中的数据保存到数据库中,关闭数据库的连接等。当需要释放内存或者容器关闭时,容器就会调用Servlet对象的destroy()方法。在Servlet容器调用destroy()方法前,如果还有其他的线程正在service()方法中执行,容器会等待这些线程执行完毕或等待服务器设定的超时值到达。一旦Servlet对象的destroy()方法被调用,容器不会再把其他的请求发送给该对象。如果需要该Servlet再次为客户端服务,容器将会重新产生一个Servlet对象来处理客户端的请求。在destroy()方法调用之后,容器会释放这个Servlet对象,在随后的时间内,该对象会被Java的垃圾收集器所回收。
小结
1.在正常情况下(所以并不是都是单例哈),Servlet只会初始化一次,而处理服务会调用多次,销毁也只会调用一次;但是如果一个Servlet长时间不使用的话,也会被容器自动销毁,而如果需要再次使用时会重新进行初始化的操作,即在特殊情况下初始化可能会进行多次,销毁也可能进行多次。
2.在servlet实例创建之后,在servlet能为客户请求提供服务之前,容器会在servlet实例上调用init()方法。如果你有初始化代码,就应该覆盖servlet类的init()方法,否则会调用GenericServlet的init()方法。而对应每个客户请求(无论是谁,无论是不是同一个人,只针对请求),容器都会创建一对新的请求和响应对象,创建一个新的线程/栈。任何servlet类都不会有多个实例,除非一种特殊情况(SingleThreadModel)。
3.servlet生命周期的4个周期总结如下:
a. 实例化以及加载servlet,new的过程
b. 初始化init(ServletConfig)。
c. 处理请求,调用servlet的service,doget,dopost方法将Request和Response,作为参数传递。
d. 退出服务,调用destory方法释放资源。
Servlet容器
一般用Tomcat来做Servlet容器,Tomcat的容器分为四个等级,真正管理Servlet的容器是Context容器,一个Context对应一个Web工程,Context容器是直接管理Servlet在容器中的包装类Wrapper,所以Context容器如何运行将直接影响Servlet的工作方式。
Servlet部署
“部署描述符”,控制servlet容器如何提供servlet的实例。对于不在分布式环境中承载的servlet(默认情况下),servlet容器每个servlet声明只能使用一个实例。但是,对于实现singlethreadmodel接口(已经被废弃)的servlet,servlet容器可以实例化多个实例来处理大量的请求加载,并将请求序列化到特定实例。
XML配置
<servlet>
<servlet-name>可以指定servlet被创建的时机</servlet-name>
<servlet-class>servlet全类名</servlet-class>
<!-- 可以指定servlet被创建的时机 -->
<liuload-on-startup>1</load-on-startup>
</servlet>
load-on-startup数值说明:可以指定servlet被创建的时机。
1)若为负数或不填,则在第一次请求时被创建:
a)当工程在run as server时即执行localhost:8080/HelloWorld, 什么都不触发
b)再执行localhost:8080/HelloWorld/SecondServlet时,依次执行生命周期函数:构造函数、init、service
c)不同负数数值,没有什么意义
2)若为0或正数,则在当前web应用被servlet容器加载时创建实例,且数字越小越早被创建:假设在web.xml中配置的多个servlet,而且load-on-startup填上非负数
a)当工程在run as server即执行localhost:8080/项目名 时,那么按数字大小,依次执行:firstServlet 构造函数----> firstServlet init方法---> second 构造函数 ---> second init方法
b)再执行localhost:8080/项目名/SecondServlet时,再执行SecondService生命周期的service方法。
ServletContext对象详解
ServletContext用来存放全局变量,每个Java虚拟机每个Web项目只有一个ServletContext,这个ServletContext是由Web服务器创建的,来保证它的唯一性。由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象通讯。ServletContext对象通常也被称之为Context域对象。
ServletContext接口作用域
您可以看下这篇文章:https://www.cnblogs.com/softidea/p/7245899.html
(不太理解2333…)
容器中部署的每一个web应用都有一个ServletContext接口的实例对象与之关联。如果容器被分布在多个虚拟机上,一个web应用将在每一个VM中有一个ServletContext实例。不作为web应用一部分部署的容器中的servlet默认是“默认”web应用的一部分,有一个默认的ServletContext。在分布式容器中。默认ServletContext是非分布式的,并且必须只存在于一个VM中。
方法
1. addServlet() //提供了编程式的向servlet容器中注入servlet的方式,其达到的效果和在web.xml中配置或者使用WebServlet注解配置servlet是一样的。只不过这种方式更加灵活、动态。addServlet方法返回的是ServletRegistration实例(ervletRegistration.Dynamic集成ServletRegistration),使用这个实例可以进一步配置servlet的注册信息,比如配置url-pattern。
2. createServlet() //实例化一个servlet,得到一个servlet实例。这个传入的表示servlet类的Class实例必须要有个无参构造函数,因为这个方法的底层实现就是利用发射机制调用默认的无参构造函数进行实例化。这个方法返回的servlet实例没有多大的使用意义,可能还是需要调用相应的addServlet进行注册。
3. getServletRegistration() //根据servlet名称查找其注册信息,即ServletRegistration实例。
4. getServletRegistrations() //查询当前servlet上下文中所有的servlet的注册信息。
5. getSessionCookieConfig() //返回SessionCookieConfig实例,这个实例可以用于获取和设置会话跟踪的cookie的属性。多次调用getSessionCookieConfig方法返回的SessionCookieConfig实例是同一个,说明SessionCookieConfig是单例的。
6. setSessionTrackingModes() //设置会话的跟踪模式,getDefaultSessionTrackingModes用于获取默认的会话跟踪模式,getEffectiveSessionTrackingModes用于获取有效的会话跟踪模式。默认情况下,getDefaultSessionTrackingModes返回的会话跟踪模式就是有效的。
7. getJspConfigDescriptor() //获取web.xml和web-fragment.xml中配置的<jsp-config>数据。
8. getClassLoader() //获取当前servlet上文的类加载器。
9. declareRoles() //该方法用于申明安全角色。
10. getVirtualServerName() //返回servlet上下文(即应用)部署的逻辑主机名,比如在本机跑Tomcat,那么这个方法返回"Catalina/localhost"。
例子
ok,接下来让我们写两个Servlet,"沟通"一下吧…
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>cp.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>cp.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
</web-app>
package cp;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet1 extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("=====================>运行Servlet1");
String data = "Builed in Servlet1.";
/**
* ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,
* 可以通过ServletConfig.getServletContext方法获得ServletContext对象。
*/
ServletContext context = this.getServletConfig().getServletContext();//获得ServletContext对象
context.setAttribute("data", data); //将data存储到ServletContext对象中
}
}
package cp;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("=====================>运行Servlet1");
ServletContext context = this.getServletContext();
String data = (String) context.getAttribute("data");//从ServletContext对象中取出数据
response.getWriter().print("data="+data);
}
}
启动Tomcat,在浏览器输入:http://localhost:8089/cp/servlet1
控制台输出:=====================>运行Servlet1
再输入:http://localhost:8089/cp/servlet2
控制台输出:=====================>运行Servlet2
浏览器返回:
servlet 运行工作原理详解
工作流程
1. Web Client 向Servlet容器(Tomcat)发出Http请求
2. Servlet容器接收Web Client的请求
3. Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中。
4. Servlet容器创建一个HttpResponse对象
5. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet 对象。
6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。
7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据。
8. Servlet容器把HttpServlet的响应结果传给Web Client。
处理请求流程
1. 用户点击一个链接,指向了一个servlet而不是一个静态页面。
2. 容器“看出”这个请求是一个Servlet,所以它创建了两个对象HttpServletRequest和HttpServletResponse。
3. 容器根据请求中的URL找到正确的Servlet,为这个请求创建或分配一个线程,并把请求和响应对象传递给这个Servlet线程。
4. 容器调用Servlet的service()方法。根据请求的不同类型,service()方法会调用
5. doGet()或doPost()方法。这里假设调用doGet()方法。
6. doGet()方法生成动态页面,并把这个页面“塞到”响应对象里,需要注意的是,容器还有响应对象的一个引用!
7. 线程结束,容器把响应对象转换为一个HTTP响应,并把它发回给客户,然后删除请求和响应对象。
Servlet线程安全
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用service方法,因此,service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。大家重点理解下面两句即可:
(1) Servlet是单实例多线程的(默认),如果存在可以修改的成员变量将会出现线程安全问题。
(2) 使用Servlet最好保证Servlet是无状态的,也就是没有可以修改的成员变量。
如何开发线程安全的Servlet:
1、实现 SingleThreadModel 接口 :Public class Concurrent Test extends HttpServlet implements SingleThreadModel
2、同步对共享数据的操作:Public class Concurrent Test extends HttpServlet {
Username = request.getParameter (“username”);
Synchronized (this){…}}
例子
//test.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>登录</title>
</head>
<body>
<form action="http://localhost:8080/loginServlet/LoginServlet" method="post">
用户:<input type="text" name="username" /><br/>
密码:<input type="password" name="password" /><br/>
<input type="submit" value="登录" />
</form>
</body>
</html>
//LoginServlet.jsp
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class LoginServlet extends HttpServlet {
//重写doGet方法
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,
IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
//服务器端打印信息
//System.out.println("username=" + username);
//System.out.println("password=" + password);
//设置编码格式
response.setContentType("text/html;charset=GB18030");
//返回html页面
response.getWriter().println("<html>");
response.getWriter().println("<head>");
response.getWriter().println("<title>登录信息</title>");
response.getWriter().println("</head>");
response.getWriter().println("<body>");
response.getWriter().println("欢迎【" + username + "】用户登录成功!!!");
response.getWriter().println("</body>");
response.getWriter().println("</html>");
}
//重写doPost方法
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,
IOException {
doGet(request, response);
}
}
//web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/LoginServlet</url-pattern>
</servlet-mapping>
</web-app>
Servlet源码
一般而言,我们比较关心的是,以下这么几个类或接口
Servlet和ServletConfig
其中,Servlet和ServletConfig是个接口
String getServletName() -- 获取当前Servlet在web.xml中配置的名字
String getInitParameter(String name) -- 获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames() -- 获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext() -- 获取代表当前web应用的ServletContext对象
GenericServlet
GenericServlet,他是个抽象类,这是个比较通过的Servlet类,一般定义的Servlet会继承他,如HttpServlet
他的主要方法:
获取参数的方法:
初始化方法,可以看到最重要的service方法没有是实现,init()方法的调用方式是一种比较规范的写法:为了防止一件事情,当我们需要在init方法中做一点别的事情,我们想到的方法就是继承GenericServlet并且重写了init(ServletConfig config)方法,这样依赖,就破坏了原本在GenericServlet类中init(ServletConfig config)写的代码了。所以我们在进行二次开发的时候选择的是init()这个方法进行重写。
主要来看看最重要的service方法:
首先他把类型进行转换,再调用protected修饰的service
如果你不看"GET"下面的第5到第21行,可以发现,他是选择方法然后进行doXxx,否则返回501状态
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
那么你肯定会问"GET"方法怎么那么复杂啊?
查阅资料发现,这是一种缓存的机制
什么是”Last-Modified”?和 If-Modified-Since?
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,
内容是你请求的资源, 同时有一个Last-Modified的属性标记此文件在服务期端
最后被修改的时间,格式类似这样:
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
客户端第二次请求此URL时,根据 HTTP 协议的规定,
浏览器会向服务器传送 If-Modified-Since 报头,
询问该时间之后文件是否有被修改过:
If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码
内容为空,这样就节省了传输数据量。
当服务器端代码发生改变或者重启服务器时,则重新发出资源
返回和第一次请求时类似。从而保证不向客户端重复发出资源,
也保证当服务器有变化时,客户端能够得到最新的资源。
HttpServletRequest客户请求对象详解
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
1. 获得客户机信息
getRequestURL //返回客户端发出请求时的完整URL。
getRequestURI //返回请求行中的资源名部分。
getQueryString //返回请求行中的参数部分。
getRemoteAddr //返回发出请求的客户机的IP地址
getRemoteHost //返回发出请求的客户机的完整主机名
getRemotePort //返回客户机所使用的网络端口号
getLocalAddr //返回WEB服务器的IP地址。
getLocalName //返回WEB服务器的主机名
getMethod //得到客户机请求方式
2. 获得客户机请求头
getHead(name) //获取一个指定头字段的值
getHeaders(String name) //返回一个Enumeration集合对象,该集合对象由请求消息中出现的某个指定名称的所有头字段值组成。
getHeaderNames //获取一个包含所有请求头字段的Enumeration对象
3. 获得客户机请求参数(客户端提交的数据)
getParameter(name) //获取指定名称的参数值。这是最为常用的 //之一。
getParameterValues(String name) //获取指定名称参数的所有值数组。它适用于一个参数名对应多个值的情况。如页面表单中的复选框,多选列表提交的值。
getParameterNames() //返回一个包含请求消息中的所有参数名的Enumeration对象。通过遍历这个Enumeration对象,就可以获取请求消息中所有的参数名。
getParameterMap() //返回一个保存了请求消息中的所有参数名和值的Map对象。Map对象的key是字符串类型的参数名,value是这个参数所对应的Object类型的值数组。
例子
//获取所有客户信息
public class CustInfoServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.获取客户端请求的完整URL
String url = request.getRequestURL().toString();
System.out.println(url);
//2.获取客户端请求的资源部分的名称
String uri = request.getRequestURI().toString();
System.out.println(uri);
//3.获取请求行的参数部分
String qStr = request.getQueryString().toString();
System.out.println(qStr);
//4.获取请求客户端的ip地址
String ip = request.getRemoteAddr();
System.out.println(ip);
//5.获取客户机的请求方式
String method = request.getMethod();
System.out.println(method);
//6.获取当前web应用的名称,不是工程名
String webName = request.getContextPath();
System.out.println(webName); //7.给一个请求头名称,获取其值
String host = request.getHeader("Host");
System.out.println(host);
//8.获取所有请求头名字组成的枚举
Enumeration<String> enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String values = request.getHeader(name);
System.out.println(name+":"+values);
}
//9.获取post和get两种请求参数 String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"),"utf-8");
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
获取完整的URL
1、request.getRequestURL() //返回的是完整的url,包括Http协议,端口号,servlet名字和映射路径,但它不包含请求参数。
2、request.getRequestURI() //得到的是URL的前部分值
3、request.getContextPath() //返回url主体
4、request.getServletPath() //返回调用servlet的部分url.
5、request.getQueryString() //返回url路径后面的查询字符串
public class test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String getContextPath = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+getContextPath+"/";
String getRemoteAddress=request.getRemoteAddr();
String getServletPath =request.getServletPath();
String getServletContext_getRealPath =request.getServletContext.getRealPath("/");
String getRequestURL =request.getRequestURL().toString();
String getRequestURI =request.getRequestURI();
String getQueryString =request.getQueryString();
String getRemoteUser =request.getRemoteUser();
out.println("getContextPath:"+ getContextPath +"<br>");
out.println("basePath:"+basePath+"<br>");
out.println("getRemoteAddress:"+ getRemoteAddress +"<br>");
out.println("getServletPath:"+ getServletPath +"<br>");
out.println("getServletContext_getRealPath:"+ getServletContext_getRealPath +"<br>");
out.println("getRequestURL:"+ getRequestURL +"<br>");
out.println("getRequestURI:"+ getRequestURI +"<br>");
out.println("getQueryString:"+ getQueryString +"<br>");
out.println("getRemoteUser:"+ getRemoteUser +"<br>");
}
}
执行结果:
getContextPath:/WebDemo
basePath:http://localhost:8683/WebDemo/
getRemoteAddress:127.0.0.1
getServletPath:/ welcome.jsp
getServletContext_getRealPath:D:\apache-tomcat-6.0.13\webapps\WebDemo\
getRequestURL: http://localhost:8683/WebDemo/welcome.jsp
getRequestURI:/WebDemo/welcome.jsp
getRequestQueryString: userName=Jhon
getRemoteUser:null
HttpServletRequest获取真实IP地址详解
普通获取IP地址方法
request.getRemoteAddr() //输出:192.168.0.105
如果通过了 Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了。经过代理以后,由于在客户端和服务之间增加了中间层,因此服务器无法直接拿到客户端的 IP,服务器端应用也无法直接通过转发请求的地址返回给客户端。但是在转发请求的HTTP头信息中,增加了X-FORWARDED-FOR信息。用以跟踪 原有的客户端IP地址和原来客户端请求的服务器地址。
获取真实IP地址方法
public final static String getIpAddress(HttpServletRequest request) throws IOException {
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
String ip = request.getHeader("X-Forwarded-For");
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip=" + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip=" + ip);
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip=" + ip);
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip=" + ip);
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip=" + ip);
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (logger.isInfoEnabled()) {
logger.info("getIpAddress(HttpServletRequest) - getRemoteAddr - String ip=" + ip);
}
}
} else if (ip.length() > 15) {
String[] ips = ip.split(",");
for (int index = 0; index < ips.length; index++) {
String strIp = (String) ips[index];
if (! ("unknown".equalsIgnoreCase(strIp))) {
ip = strIp;
break;
}
}
}
return ip;
}
Java中servlet http status状态码详解
代码 | 消息 | 描述 |
---|---|---|
100 | Continue | 只有请求的一部分已经被服务器接收,但只要它没有被拒绝,客户端应继续该请求。 |
101 | Switching Protocols | 服务器切换协议。 |
200 | OK | 请求成功。 |
201 | Created | 该请求是完整的,并创建一个新的资源。 |
202 | Accepted | 该请求被接受处理,但是该处理是不完整的。 |
203 | Non-authoritative Information | |
204 | No Content | |
205 | Reset Content | |
206 | Partial Content | |
300 | Multiple Choices | 链接列表。用户可以选择一个链接,进入到该位置。最多五个地址。 |
301 | Moved Permanently | 所请求的页面已经转移到一个新的 URL。 |
302 | Found | 所请求的页面已经临时转移到一个新的 URL。 |
303 | See Othe | r 所请求的页面可以在另一个不同的 URL 下被找到。 |
304 | Not Modified | |
305 | Use Proxy | |
306 | Unused | 在以前的版本中使用该代码。现在已不再使用它,但代码仍被保留。 |
307 | Temporary Redirect | 所请求的页面已经临时转移到一个新的 URL。 |
400 | Bad Request | 服务器不理解请求。 |
401 | Unauthorized | 所请求的页面需要用户名和密码。 |
402 | Payment Required | 您还不能使用该代码。 |
403 | Forbidden | 禁止访问所请求的页面。 |
404 | Not Found | 服务器无法找到所请求的页面。. |
405 | Method Not Allowed 在请求中指定的方法是不允许的。 | |
406 | Not Acceptable | 服务器只生成一个不被客户端接受的响应。 |
407 | Proxy Authentication Required | 在请求送达之前,您必须使用代理服务器的验证。 |
408 | Request Timeout | 请求需要的时间比服务器能够等待的时间长,超时。 |
409 | Conflict | 请求因为冲突无法完成。 |
410 | Gone | 所请求的页面不再可用。 |
411 | Length Required “Content-Length” | 未定义。服务器无法处理客户端发送的不带 Content-Length 的请求信息。 |
412 | Precondition Failed | 请求中给出的先决条件被服务器评估为 false。 |
413 | Request Entity Too Large | 服务器不接受该请求,因为请求实体过大。 |
414 | Request-url Too Long | 服务器不接受该请求,因为 URL 太长。当您转换一个 “post” 请求为一个带有长的查询信息的 “get” 请求时发生。 |
415 | Unsupported Media Type | 服务器不接受该请求,因为媒体类型不被支持。 |
417 | Expectation Failed | |
500 | Internal Server Error | 未完成的请求。服务器遇到了一个意外的情况。 |
501 | Not Implemented | 未完成的请求。服务器不支持所需的功能。 |
502 | Bad Gateway 未完成的请求。服务器从上游服务器收到无效响应。 | |
503 | Service Unavailable | 未完成的请求。服务器暂时超载或死机。 |
504 | Gateway Timeout | 网关超时。 |
505 | HTTP Version Not Supported | 服务器不支持"HTTP协议"版本。 |