Servlet基础概要
1. Server + let
意为:运行在服务器端的小程序。
Servlet实际上就是一个接口。
狭义上的Servlet,指的就是Servlet接口
广义上的我们认为凡是实现Servlet接口的类,我们都称他是一个Servlet *****
2.Servlet的作用:
1.接收用户发送的请求
2.调用其他的java程序来处理请求
3.根据处理结果,返回给用户一个页面
1.web客户向Servlet容器发出HTTP请求;
2.Servlet容器解析web的HTTP请求.
3.Servlet容器创建一个HttpRequest对象,在这个对象中封装了http请求信息;
4.Servlet容器创建一个HttpResponse对象;
5.Servlet容器(如果访问的该servlet不是在服务器启动时创建的,则先创建servlet实例并调用init()方法初始化对象)调用HttpServlet的service()方法,把HttpRequest和HttpResponse对象为service方法的参数传给HttpServlet对象;
6.HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;
7.HttpServlet调用HttpResponse的有关方法,生成响应数据;
8. Servlet容器把HttpServlet的响应结果传给web客户.
Servlet API的核心就是javax.servlet.Servlet接口,所有的Servlet 类(抽象的或者自己写的)都必须实现这个接口。在Servlet接口中定义了5个方法,其中有3个方法是由Servlet 容器在Servlet的生命周期的不同阶段来调用的特定方法。
package javax.servlet;
import java.io.IOException;
publicinterfaceServlet {
//负责初始化Servlet对象。容器一旦创建好Servlet对象后,就调用此方法来初始化Servlet对象
publicvoidinit(ServletConfig config)
throwsServletException;
//方法负责响应客户的请求,提供服务。当容器接收到客户端要求访问特定的servlet请求时,就会调用Servlet的service方法
publicvoidservice(ServletRequest req, ServletResponse res)
throwsServletException, IOException;
//Destroy()方法负责释放Servlet 对象占用的资源,当servlet对象结束生命周期时,servlet容器调用此方法来销毁servlet对象.
publicvoiddestroy();
//说明:Init(),service(),destroy() 这三个方法是Servlet生命周期中的最重要的三个方法。
//返回一个字符串,在该字符串中包含servlet的创建者,版本和版权等信息
publicString
getServletInfo();
//GetServletConfig: 返回一个ServletConfig对象,该对象中包含了Servlet初始化参数信息
publicServletConfig
getServletConfig();
}
GenericServlet抽象类实现了Servlet接口,它只是通用的实现,与任何网络应用层协议无关。
同时,HttpServlet这个抽象类继承了GenericServlet抽象类,在Http协议上提供了通用的实现。
代表当前Servlet的配置信息,每一个Servlet都有其唯一对应的ServletConfig。
<servlet>
<servlet-name>AServlet</servlet-name>
<servlet-class>com.atguigu.servlet.AServlet</servlet-class>
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123123</param-value>
</init-param>
</servlet>
获取:由Tomcat服务器创建,最终作为参数传递到init()方法中,我们可以在init()方法直接使用。
当我们通过继承HttpServlet创建Servlet时,由于父类已经实现ServletConfig接口,
所以我们可以在Servlet中直接调用ServletConfig的方法。
功能:
【1】 获取Servlet的别名
【2】 获取当前Servlet的初始化参数。
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123123</param-value>
</init-param>
【3】 获取当前WEB应用的ServletContext对象。
②ServletContext
代表:当前的WEB应用,一个WEB应用对应一个唯一的ServletContext对象,
ServletContext对象在项目启动时创建,在项目卸载时销毁。
获取:通过ServletConfig的getServletContext()方法获取。
功能:
【1】 可以获取整个WEB应用的初始化参数
<context-param>
<param-name>phone</param-name>
<param-value>1388888888</param-value>
</context-param>
【2】 可以获取资源的真实路径(物理路径),主要在文件的上传和下载时使用。
【3】 可以作为一个域对象在不同的web资源之间共享数据。
package javax.servlet;
import java.io.IOException;
import java.util.Enumeration;
//抽象类GenericServlet实现了Servlet接口的同时,也实现了ServletConfig接口和Serializable这两个接口
publicabstractclassGenericServlet
implementsServlet, ServletConfig, java.io.Serializable
{
//私有变量,保存 init()传入的ServletConfig对象的引用
privatetransient ServletConfig config;
//无参的构造方法
publicGenericServlet() { }
------------------------------------
以下方法实现了servlet接口中的
5个方法
实现Servlet接口方法开始
------------------------------------
/*
实现接口Servlet中的带参数的init(ServletConfig Config)方法,将传递的ServletConfig对象的引用保存到私有成员变量中,
使得GenericServlet对象和一个ServletConfig对象关联.
同时它也调用了自身的不带参数的init()方法
**/
publicvoidinit(ServletConfig config)
throwsServletException {
this.config = config;
this.init();
//调用了无参的 init()方法
}
//无参的init()方法
publicvoidinit()
throwsServletException {
}
//空实现了destroy方法
publicvoiddestroy() { }
//实现了接口中的getServletConfig方法,返回ServletConfig对象
publicServletConfig
getServletConfig()
{
return config;
}
//该方法实现接口<Servlet>中的ServletInfo,默认返回空字符串
publicString
getServletInfo() {
return"";
}
//唯一没有实现的抽象方法service(),仅仅在此声明。交由子类去实现具体的应用
//在后来的HttpServlet抽象类中,针对当前基于Http协议的Web开发,HttpServlet抽象类具体实现了这个方法
//若有其他的协议,直接继承本类后实现相关协议即可,具有很强的扩展性
publicabstractvoidservice(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
------------------------------------
实现Servlet接口方法结束
------------------------------------
---------------------------------------------
以下四个方法实现了接口ServletConfig中的方法
实现ServletConfig接口开始
---------------------------------------------
//该方法实现了接口<ServletConfig>中的getServletContext方法,用于返回servleConfig对象中所包含的servletContext方法
publicServletContext
getServletContext() {
return getServletConfig().getServletContext();
}
//获取初始化参数
publicString
getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
//实现了接口<ServletConfig>中的方法,用于返回在web.xml文件中为servlet所配置的全部的初始化参数的值
publicEnumeration
getInitParameterNames() {
return getServletConfig().getInitParameterNames();
//获取在web.xml文件中注册的当前的这个servlet名称。没有在web.xml 中注册的servlet,该方法直接放回该servlet的类名。
//法实现了接口<ServleConfig>中的getServletName方法
publicString
getServletName() {
return config.getServletName();
}
---------------------------------------------
实现ServletConfig接口结束
---------------------------------------------
publicvoidlog(String msg) {
getServletContext().log(getServletName() +
": "+ msg);
}
publicvoidlog(String message, Throwable t) {
getServletContext().log(getServletName() +
": "+ message, t);
}
}
是继承了GenericServlet抽象类的一个抽象类,但是他的里面并没有任何抽象方法,这就是说他并不会强迫我们去做什么。我们只是按需选择,重写HttpServlet中的的部分方法就可以了。
- HttpServlet继承了GenericServlet,而GenericServlet实现Servlet接口
- 所以我们可以同构继承HttpServlet来创建一个Servlet。
- HttpServlet重写service()方法:
1.在该方法中先将ServletRequest和ServletResponse
强转为了HttpServletRequest和HttpServletResponse。
2.然调用重载的service()方法,并将刚刚强转得到对象传递到重载的方法中。
- 重载service(HttpServletRequest request , HttpServletResponse response)
1.在方法中获取请求的方式(get或post)
2.在根据不同的请求方式去调用不同的方法:
如果是GET请求,则调用doGet(HttpServletRequest request , HttpServletResponse response)
如果是post请求,则调用doPost(HttpServletRequest request , HttpServletResponse response)
- 结论:
当通过继承HttpServlet来创建一个Servlet时,我们只需要根据要处理的请求的类型,来重写不同的方法。
处理get请求,则重写doGet()
处理post请求,则重写doPost()
Java
代码
package javax.servlet.http;
publicabstractclassHttpServletextendsGenericServlet
implementsjava.io.Serializable
{
privatestaticfinalString METHOD_GET =
"GET";
privatestaticfinalString METHOD_POST =
"POST";
......
/**
* Does nothing, because this is an abstract class.
* 抽象类HttpServlet有一个构造函数,但是空的,什么都没有
*/
publicHttpServlet() { }
/*分别执行doGet,doPost,doOpitions,doHead,doPut,doTrace方法
在请求响应服务方法service()中,根据请求类型,分贝调用这些doXXXX方法
所以自己写的Servlet只需要根据请求类型覆盖响应的doXXX方法即可。
*/
//doXXXX方法开始
protectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString(
"http.method_get_not_supported");
if(protocol.endsWith(
"1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
}
else{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protectedvoiddoHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
.......
}
protectedvoiddoPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString(
"http.method_post_not_supported");
if(protocol.endsWith(
"1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
}
else{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protectedvoiddoPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protectedvoiddoOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protectedvoiddoTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protectedvoiddoDelete(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
//doXXXX方法结束
//重载的service(args0,args1)方法
protectedvoidservice(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if(lastModified == -
1) {
// servlet doesn‘t support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
}
else{
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if(ifModifiedSince < (lastModified /
1000*
1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
}
else{
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
}
elseif(method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
}
elseif(method.equals(METHOD_POST)) {
doPost(req, resp);
}
elseif(method.equals(METHOD_PUT)) {
doPut(req, resp);
}
elseif(method.equals(METHOD_DELETE)) {
doDelete(req, resp);
}
elseif(method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
}
elseif(method.equals(METHOD_TRACE)) {
doTrace(req,resp);
}
else{
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString(
"http.method_not_implemented");
Object[] errArgs =
newObject[
1];
errArgs[
0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
//实现父类的service(ServletRequest req,ServletResponse res)方法
//通过参数的向下转型,然后调用重载的service(HttpservletRequest,HttpServletResponse)方法
publicvoidservice(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
//向下转型
response = (HttpServletResponse) res;
//参数向下转型
}
catch(ClassCastException e) {
thrownewServletException(
"non-HTTP request or response");
}
service(request, response);
//调用重载的service()方法
}
......
//其他方法
}
代表:浏览器发送给服务器的请求报文。
获取:该对象由Tomcat服务器创建,最终作为参数传递到doGet或doPost方法中,我们可以在这两个方法中直接使用。
功能:
【1】获取用户发送的请求参数
request.getParameter("username");
【2】获取项目的名字(用来设置绝对路径)
request.getContextPath();
【3】作为一个域对象,在不同的WEB资源之间共享数据。
【4】请求的转发
request.getRequestDispatcher("target.html").forward(request, response);
代表:服务器发送给浏览器的响应报文。
获取:该对象由Tomcat服务器创建,最终作为参数传递到doGet或doPost方法中,我们可以在这两个方法中直接使用。
功能:
【1】响应给浏览器一个网页或者是网页片段(设置的是响应报文的响应体)
response.getWriter("");
【2】请求的重定向
response.sendRedirect("target.html");
转发和重定向:(掌握)
转发 重定向
请求的次数: 1 2
发生的位置 服务器内部 浏览器
浏览器地址栏 不改变 改变
浏览器的感知 不知道 知道
Servlet的生命周期可以分为四个阶段,即装载类及创建实例阶段、初始化阶段、服务阶段和实例销毁阶段。
在javax.servlet.Servlet接口中,定义了针对Servlet生命周期最重要的三个方法,按照顺序,依次是init(),Serveice()和destroy()这三个方法.
Servlet初始化阶段,包括执行如下四个步骤:
1. servlet容器(如tomcat)加载servlet类,读入其.class类文件到内存
2. servlet容器开始针对这个servlet,创建ServletConfig对象
3. servlet容器创建servlet对象
4. servlet容器调用servlet对象的init(ServletConfig
config)方法,在这个init方法中,建立了sevlet对象和servletConfig对象的关联,执行了如下的代码:
public void init(ServletConfig config) throws ServletException
{
this.config = config; //将容器创建的servletConfig 对象传入,并使用私有成员变量引用该servletConfig对象
this.init();
}
通过以上的初始化步骤建立了servlet对象和sevletConfig对象的关联,而servletConfig对象又和当前容器创建的ServleContext对象获得关联.。
一旦Servlet实例被创建,Web服务器会自动调用init(ServletConfig config)方法来初始化该Servlet。其中方法参数config中包含了Servlet的配置信息,比如初始化参数,该对象由服务器创建。
在默认情况下Servlet实例是在第一个请求到来的时候创建,以后复用。如果有的Servlet需要复杂的操作需要载初始化时完成,比如打开文件、初始化网络连接等,可以通知服务器在启动的时候创建该Servlet的实例。具体配置如下:
<servlet>
<servlet-name>TimeServlet</servlet-name>
<servlet-class>com.allanlxf.servlet.basic.TimeServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
ⅠWeb.xml文件:
当我们编写好servlet之后,如何让tomcat知道,这个java类是和某个请求路径对应的呢,就是通过web.xml配置文件。 servlet-name用于指定servlet名字,在同一个web程序中,每一个servlet的名字必须是唯一的,该元素的内容不能为空。servlet-class用于指定servlet类的完全限定名;servlet-mapping节点用于在servlet于url之间定义一个映射,这里面的servlet-name的值必须与上面的一致,url-pttern用于指定对应的url路径。
在web.xml中该Servlet的定义标记中,比如:
<servlet>
//指定servlet的名称
<servlet-name>TimeServlet</servlet-name>
指定servlet编译生成的.class文件的相对路径,区分大小写
<servlet-class>com.allanlxf.servlet.basic.TimeServlet</servlet-class>
<init-param>
<param-name>user</param-name>
<param-value>username</param-value>
</init-param>
<init-param>
<param-name>blog</param-name>
<param-value>http://。。。</param-value>
</init-param>
</servlet>
<servlet-mapping>
在解析到<url-pattern>中的路径请求时,由<servlet-name>指定的servlet来处理;<servlet-mapping>用于对<servlet>中指定的servlet映射路径
<servlet-name>别名</servlet-name>
<url-pattern>需要servlet处理的请求地址</url-pattern>
指定客户端在地址栏里面输入什么能访问到这个servlet(斜杠表示从项目名开始)
</servlet-mapping>
配置了两个初始化参数user和blog它们的值分别为username和http://。。。, 这样以后要修改用户名和博客的地址不需要修改Servlet代码,只需修改配置文件即可。
II.如何读取Servlet的初始化参数?
ServletConfig中定义了如下的方法用来读取初始化参数的信息:
public String getInitParameter(String name)
参数:初始化参数的名称。
返回:初始化参数的值,如果没有配置,返回null。
III.init(ServletConfig)方法执行次数
在Servlet的生命周期中,该方法执行一次。
IV.init(ServletConfig)方法与线程
该方法执行在单线程的环境下,因此开发者不用考虑线程安全的问题。
V.init(ServletConfig)方法与异常
该方法在执行过程中可以抛出ServletException来通知Web服务器Servlet实例初始化失败。一旦ServletException抛出,Web服务器不会将客户端请求交给该Servlet实例来处理,而是报告初始化失败异常信息给客户端,该Servlet实例将被从内存中销毁。如果在来新的请求,Web服务器会创建新的Servlet实例,并执行新实例的初始化操作
由service()方法根据提交的方式选择执行doGet()或者doPost()方法,再通过ServletResponse对象生成响应结果,然后发送给客户端,最后销毁创建的ServletRequest 和ServletResponse
一旦Servlet实例成功创建及初始化,该Servlet实例就可以被服务器用来服务于客户端的请求并生成响应。当容器接受到访问特定的servlet请求时,针对这个请求,在服务阶段Web服务器会调用该实例的service(ServletRequest request, ServletResponse response)方法,request对象和response对象有服务器创建并传给Servlet实例。request对象封装了客户端发往服务器端的信息,response对象封装了服务器发往客户端的信息。
I. service()方法的职责
service()方法为Servlet的核心方法,客户端的业务逻辑应该在该方法内执行,典型的服务方法的开发流程为:
解析客户端请求-〉执行业务逻辑-〉输出响应页面到客户端
II.service()方法与线程
为了提高效率,Servlet规范要求一个Servlet实例必须能够同时服务于多个客户端请求,即service()方法运行在多线程的环境下,Servlet开发者必须保证该方法的线程安全性。
III.service()方法与异常
service()方法在执行的过程中可以抛出ServletException和IOException。其中ServletException可以在处理客户端请求的过程中抛出,比如请求的资源不可用、数据库不可用等。一旦该异常抛出,容器必须回收请求对象,并报告客户端该异常信息。IOException表示输入输出的错误,编程者不必关心该异常,直接由容器报告给客户端即可。
编程注意事项说明:
1) 当Server Thread线程执行Servlet实例的init()方法时,所有的Client Service Thread线程都不能执行该实例的service()方法,更没有线程能够执行该实例的destroy()方法,因此Servlet的init()方法是工作在单线程的环境下,开发者不必考虑任何线程安全的问题。
2) 当服务器接收到来自客户端的多个请求时,服务器会在单独的Client Service Thread线程中执行Servlet实例的service()方法服务于每个客户端。此时会有多个线程同时执行同一个Servlet实例的service()方法,因此必须考虑线程安全的问题。
3) 请大家注意,虽然service()方法运行在多线程的环境下,并不一定要同步该方法。而是要看这个方法在执行过程中访问的资源类型及对资源的访问方式。分析如下:
i. 如果service()方法没有访问Servlet的成员变量也没有访问全局的资源比如静态变量、文件、数据库连接等,而是只使用了当前线程自己的资源,比如非指向全局资源的临时变量、request和response对象等。该方法本身就是线程安全的,不必进行任何的同步控制。
ii. 如果service()方法访问了Servlet的成员变量,但是对该变量的操作是只读操作,该方法本身就是线程安全的,不必进行任何的同步控制。
iii. 如果service()方法访问了Servlet的成员变量,并且对该变量的操作既有读又有写,通常需要加上同步控制语句。
iv. 如果service()方法访问了全局的静态变量,如果同一时刻系统中也可能有其它线程访问该静态变量,如果既有读也有写的操作,通常需要加上同步控制语句。
v. 如果service()方法访问了全局的资源,比如文件、数据库连接等,通常需要加上同步控制语句。
当Web服务器认为Servlet实例没有存在的必要了,比如应用重新装载,或服务器关闭,以及Servlet很长时间都没有被访问过。服务器可以从内存中销毁(也叫卸载)该实例。Web服务器必须保证在卸载Servlet实例之前调用该实例的destroy()方法,以便回收Servlet申请的资源或进行其它的重要的处理。
Web服务器必须保证调用destroy()方法之前,让所有正在运行在该实例的service()方法中的线程退出或者等待这些线程一段时间。一旦destroy()方法已经执行,Web服务器将拒绝所有的新到来的对该Servlet实例的请求,servlet容器现调用web应用中所有的Servlet对象的destroy()方法,然后再销毁这些servlet对象,此外,容器还销毁了针对各个servlet所创建的相关联的serveltConfig对象
以下程序代码演示了Servlet对象的生命周期:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
publicclassLifeServletextendsHttpServlet
{
privateintinitvar=
0;
privateintservicevar=
0;
privateintdestroyvar=
0;
private String name;
//覆盖父类的销毁方法,加入销毁的计数器
publicvoiddestroy()
{
destroyvar++;
System.out.println(name+
">destroy()被销毁了"+destroyvar+
"次");
}
//覆盖父类的init(ServletConfig config)方法 加入计数
/* public void init(ServletConfig config) throws ServletException
{
super.init(config); //调用父类的带参数的init方法
name=config.getServletName();
initvar++;
System.out.println(name+">init():Servlet 被初始化了"+initvar+"次");
}*/
/* 根据分析GenericServlet的源码,其init(ServletConfig config)方法最终还是会调用了不带参数的init()方法,
* 所以也可以在自己编写的的servlet中覆盖不带参数的init()方法来加入统计计数
*/
publicvoidinit() //
覆盖父类的不带参数的初始化方法
{
initvar++;
//name=getServleConfig.getServletNmae();
name=getServletName();
//直接返回
System.out.println(name+
"init(): servlet被初始化了了 "+initvar+
"次");
}
*/
publicvoidservice(HttpServletRequest request,HttpServletResponse response)
throwsServletException,IOException
{
servicevar++;
//请求服务计数器自增
System.out.println(name+
">service():servlet共响应了"+servicevar+
"请求");
String content1=
"初始化次数: "+initvar;
String content2=
"销毁次数: "+destroyvar;
String content3=
"请求服务次数 "+servicevar;
response.setContentType(
"text/html;charset=GBK");
PrintWriter out=response.getWriter();
out.print(
"<html><head><title>LifeServlet</title></head>");
out.print(
"<body>");
out.print(
"<h1>"+content1+
"</h1><br>");
out.print(
"<h1>"+content2+
"</h1><br>");
out.print(
"<h1>"+content3+
"</h1><br>");
out.print(
"</body>");
out.print(
"</head>");
out.close();
}
}
之前提到servlet 生命周期中的三(四)个阶段,第一个阶段中servlet容器会执行init方法来初始化一个servlet.
init方法和destroy这两个方法在servlet生命周期中之执行一次。servlet容器(或者说是servlet引擎)创建了servlet实例对象后立即调用该init方法。
Init方法是在servlet对象被创建后,再由servlet容器调用的方法,其执行位于构造方法之后,在执行init方法时,会传递一个serveltConfig对象。
所以,如果要在初始代码中用到servletConfig对象,则这些初始操作只能在init方法中编写,不能在构造方法中编写.
Java
代码
GenericServlet
中关于init方法的示意源码:
publicvoidinit(ServletConfig config)
throwsServletException
{
this.config=config;
......
}
如果在要在自己编写的servlet类中增加一些额外的初始化功能,则必须覆盖GenricServlet类的init(ServletConfig config)方法,如果覆盖后,父类的init(ServletConfig config)方法就不会被调用。GenericServlet类中有些方法依赖init方法执行的结果,例如
publicServletContext
getServeltContext()
{
return config.getServletContext;
}
这里的config对象的引用就来自Init(ServletConfig config)执行的结果.
所以如果子类需要覆盖了父类的init(ServletConfig config)方法,则首先要调用父类的init(ServletConfig config)方法,也就是先加入 super.init(config) 语句来先执行父类的方法.否则,会产生空指针异常 java.lang.NullPointerException ,因为config对象的引用为空。
为了避免这样的情况的产生,GenericServlet的设计人员 在GenericServlet中又定义了一个无参数的init()空方法. 且在init(servletConfig config)方法最后也调用了这个无参的空方法
Java
代码
publicvoidinit(ServletConfig config)
throwsServletException
{
this.config=config;
init();
}
所以,我们只需覆盖这个无参的init方法加入自己的初始代码即可,而无需覆盖带参数的父类的init方法.
如果有多个重载的init方法,对以servlet而言,servlet容器始终就之调用servlet接口中的那个方法:init(ServletConfig config) (当然也会执行已经覆盖的无参的init()方法),其他的覆盖的init方法不会执行。
1、Serlvet容器启动时自动装载某些Servlet,实现它只需要在web.xml文件中<servlet> </servlet>之间添加<load-on-startup>1</load-on-startup>,数字越小,优先级越大。
2、在Servlet容器启动时,客户首次向Servlet发送请求
3、Servlet类文件被更新后,重新装载Servlet
> 当用户通过表单向Servlet发送中文请求参数时,Servlet获取到内容会产生乱码,
当Servlet向浏览器响应中文内容时,也会产生乱码。
> 浏览器和服务器之间通信时,中文内容时不能直接发送的,需要对中文进行编码。
> 编码:
- 将字符转换为二进制码的过程叫编码。
> 解码:
- 将二进制码转换为普通字符的过程叫解码。
> 编码和解码所采用的规则我们称为字符集。
> 产生乱码问题的根本原因:
编码和解码所采用的字符集不同。
> 解决方法:
统一编码和解码的字符集为UTF-8。
> 常见字符集:
1.ASCII
2.ISO8859-1
3.GBK
4.GB2312
5.UTF-8
> 请求编码
- 请求是浏览器发送给服务器的。
- 浏览器 --> 服务器
- 浏览器 编码
> 浏览器的会自动使用网页的字符集对参数进行编码
UTF-8的张三:%E5%BC%A0%E4%B8%89
GBK的张三:%D5%C5%C8%FD
> 所以我们只需要统一网页的字符集为UTF-8即可。
- 服务器 解码
post请求
> request解码时默认字符集时iso8859-1,但是iso压根就不支持中文
> post请求在servlet中解码,所以我们只需要指定request的字符集即可。
> 我们可以通过如下方法,来设置request的字符集:
request.setCharacterEncoding("utf-8");
> 注意:
该方法要在request.getParameter()第一次调用之前调用
get请求
> get请求是通过url地址传递请求参数,url中的请求参数将会被Tomcat服务器自动解码。
> Tomcat的默认编码是iso8859-1,但是iso压根就不支持中文,所以必然乱码。
> 只需要修改Tomcat的解码的默认字符集,修改配置文件server.xml
> 在server.xml的Connector标签中(改端口号的那个标签)添加如下属性:
URIEncoding="utf-8"
> 修改完配置文件以后,get请求的编码就不用再处理的,但是post请求还是老样子。
> 响应编码
- 响应是服务器发送给浏览器
- 服务器 --> 浏览器
- 服务器 编码
> 指定服务器的编码字符集为UTF-8。
> 指定response的字符集
response.setCharacterEncoding("utf-8");
> 虽然我们已经指定了response的字符集为utf-8,但是浏览器并不是用utf-8解码。
浏览器默认使用gb2312解码的,所以依然乱码,只不过没有那么乱。
- 浏览器 解码
> 浏览器的解码字符集可以通过浏览器来设置(不靠谱)
> 我们可以通过服务器来告诉浏览器,我们的内容的编码格式为utf-8
> 我们可以通过一个响应头来告诉浏览器,内容的编码格式:
Content-Type:text/html;charset=utf-8
> 通过response的方法,来设置响应头:
response.setHeader("Content-Type", "text/html;charset=utf-8");
解决方案:
1.设置响应头
response.setHeader("Content-Type", "text/html;charset=utf-8");
2.设置response的编码格式
response.setCharacterEncoding("utf-8");
> 当我们设置Content-Type这个响应头时,服务器会自动使用响应头中的字符集为内容编码。
> 最终方案:
response.setContentType("text/html;charset=utf-8");
总结:
post请求:
- 在request.getParameter()方法第一次调用之前,调用如下代码:
request.setCharacterEncoding("utf-8");
get请求:
- 修改server.xml配置文件
- <Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
响应:
- 设置一个Content-Type响应头
response.setContentType("text/html;charset=utf-8");
> URI和URL
- URL是URI的一种实现,也是URI最常见的实现方式。
- URI有两种实现方式URL和URN,URN用的很少
- 我们说URL和URI实际上可以理解为一个意思
> URL地址的格式
http://主机名:端口号/项目名/资源路径/资源名
①相对路径和绝对路径
> 相对路径
- 之前我们使用的路径全都是相对路径:
- 所谓的相对路径指相对于当前资源所在路径:
http://主机名:端口号/项目名/资源路径/
- 由于转发的出现,相对路径会经常发生变化,容易出现错误的链接
所以在开发中我们一般不使用相对路径,而是使用绝对路径。
> 绝对路径
- 绝对路径使用/开头
- 由浏览器解析的绝对路径中的/代表的是服务器的根目录:
http://主机名:端口号/
注意:需要加上项目名
- 由服务器解析的绝对路径中的/代表的项目的根目录:
http://主机名:端口号/项目名/
注意:不要加项目名
- 转发的路径由服务器解析,设置绝对路径时不需要加项目名
- 重定向的路径由浏览器解析,设置绝对路径时需要加上项目名
②常见的路径:
url-pattern:
转发的路径:
- url-pattern和转发中的路径都是由服务器解析的,
根目录是项目的根目录:
http://主机名:端口号/项目名/
- 所以这两个路径不需要加项目名
重定向的路径:
页面中的路径:
- 重定向和页面中的路径(HTML标签中的路径),由浏览器解析的,
根目录是服务器的根目录:
http://主机名:端口号/
- 所以这个两个路径必须加上项目名
原文:https://www.cnblogs.com/learn-zy/p/10382265.html