一、Servlet的生命周期
生命周期:Servlet被实例化后,容器运行其init方法,当请求到达时运行其service方法,当服务器决定将实例销毁的时候调用其destroy方法。
1、Servlet实例化对象和初始化方法,默认情况下,只有第一次访问时才执行,且只执行一次。
重要:在内存中一个Servlet只有一个实例。针对不同的用户请求,容器采用多线程的机制调用service方法的。
希望在应用被Tomcat加载完毕后(此时还没有任何人访问),就实例化并完成初始化Servlet的工作?
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.jxn.servlet.FirstServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
注:<load-on-startup>n</load-on-startup>表示servlet加载的时机和顺序。
1)当值为0或者大于0时:表示容器启动的时候该servlet就被加载(实例化并调用其init()方法)了。
说明:
[1]正数的值越小,启动该servlet的优先级越高。
[2]org.apache.catalina.servlets.DefaultServlet中<load-on-startup>1</load-on-startup>,1已经被占用了,故如果想让该servlet在容器启动的时候就被加载,则从2开始取(注;实际上从1开始取也可以)
2)当值为负数或者没有指定时:表示该servlet第一次被访问时才会被容器加载。
2、Servlet接口中的常用方法:
public void init(ServletConfig config):初始化。Servlet类被实例化后就执行,且执行一次。由容器进行调用
public void destroy():销毁Servlet对象。由容器进行调用
3、如果设计与HTTP协议有关的Servlet,一般选择集成javax.servlet.http.HttpServlet.
容器最终要调用service方法为客户进行服务:
HttpServlet覆盖了其父类GenericServlet的public abstract void service(ServletRequest req, ServletResponse res)方法,
然后在覆盖的service方法中调用了自己的protected void service(HttpServletRequest req, HttpServletResponse resp)方法,
在自己的service方法中调用了自己的doXXX方法。
故自己写的Servlet不应该覆盖HttpServlet的service方法,而应该去覆盖掉它的doXXX方法。
二、Servlet的线程安全问题
在内存中一个Servlet只有一个实例。针对不同的用户请求,容器采用多线程的机制调用service方法的。
在Servlet中定义变量,除非特殊要求,尽量使用局部变量。
如果有需要实例变量时,应做同步处理,且同步代码块尽量包围少的代码。
三、Servlet的配置
Servlet的配置对象ServletConfig:(容器来创建)
作用:代表了Servlet配置中的参数信息。
比如在web.xml中的参数配置如下:
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>cn.jxn.servlet.ServletDemo2</servlet-class>
<!-- aaa=bbb -->
<init-param>
<param-name>aaa</param-name>
<param-value>bbb</param-value>
</init-param>
<init-param>
<param-name>xxx</param-name>
<param-value>yyy</param-value>
</init-param>
</servlet>
Servlet配置
1、一个Servlet可以被映射到多个URL地址上
2、URL地址映射还支持通配符*
方式一:以*开头,以扩展名结尾。比如 <url-pattern>*.do</url-pattern>
方式二:以/前缀开头,以*结尾。 比如<url-pattern>/action/*</url-pattern>
3、多个Servlet使用通配符时,有可能有多
以"/"开头(方式二)要比"*"开头(方式一)优先级高
都以"/"开头,还是有多个匹配,找最匹配的
4、如果一个Servlet的映射为一个"/",就称之为默认的Servlet,它负责处理没有映射路径的URL请求的响应。
四、ServletContext
1、在应用被服务器加载时就创建ServletContext对象的实例。每一个JavaWeb应用都有唯一的一个ServletContext对象。它就代表着当前的应用。
2、如何得到ServletContext对象:ServletConfig.getServletContext();
3、作用:
3.1ServletContext对象是一个域对象(域对象就是说其内部维护了一个Map<String,Object>)
Object getAttribute(String name):根据名称获取绑定的对象
void setAttribute(String name,Object value):添加或修改对象。
void removeAttribute(String name):根据名称移除对象
Enumeration getAttributeNames() :获取ServletContext域中所有的值对应的名称
3.2实现多个Servlet之间的数据共享
3.3获取WEB应用的初始化参数(应用的全局参数)
在web.xml下配置以下信息:
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
这些参数就属于整个应用的全局参数,使用ServletContext来读取。
3.4读取资源文件的三种方式:
利用ServletContext.getRealPath():
特点:读取应用中任何文件。只能在Web环境下用
利用ResourceBundle读取配置文件
特点:可以用在非web环境下。但是只能读取类路径中的properties文件
利用类加载器读取配置文件(专业)
特点:可以用在非web环境下。可以读取类路径下的任何文件。
五、HttpServletResponse详解
5.1输出中文数据:
字节流:
注:String.getBytes()方法:使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
String.getBytes("UTF-8")方法:使用UTF-8将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
ServletOutputStream out = response.getOutputStream();
out.write("中文".getBytes());无乱码
无乱码的原因:平台默认的字符集和浏览器默认的字符集一样(在中国一般为GBK)
ServletOutputStream out = response.getOutputStream();
out.write("中文".getBytes("UTF-8"));有乱码
解决办法:
通知浏览器,使用的码表
方式一:response.setHeader("Content-Type", "text/html;charset=UTF-8");
方式二:response.getOutputStream().write("<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>".getBytes("UTF-8"));
*方式三:response.setContentType("text/html;charset=UTF-8");//方式一、二、三都是一样的
字符流:
注:Servlet中的字符流默认查ISO-8859-1(SUN的Servlet规范要求的)
PrintWriter out = response.getWriter();
out.write(s);//字符输出流out默认查的是ISO-8859-1码表,即默认以ISO-8859-1编码的形式输出到浏览器
解决办法:
方式一:
第一步:更改默认的编码: response.setCharacterEncoding("UTF-8");
第二步:通知浏览器的编码: response.setHeader("Content-Type", "text/html;charset=UTF-8");
方式二:
response.setContentType("text/html;charset=UTF-8");
在字符流输出中文数据时:response.setContentType("text/html;charset=UTF-8");有两个作用:
(1)通知字符流以UTF-8编码输出
(2)通知客户端以UTF-8解码显示
5.2控制不要缓存
response.setHeader("Pragma","no-cache"); // HTTP1.0 Pragma: 编译指示,标示,杂注
response.setHeader("Cache-Control","no-cache"); // HTTP1.1
response.setDateHeader("Expires", 0);
5.3HttpServletResponse细节:
字节流和字符流不能同时使用,互斥的。
通过字符流或字节流输出的数据并不是直接打给浏览器的。而是把数据写到response对象的缓存中的。服务器从缓存中取出数据,按照HTTP协议的响应格式输出给浏览器。
如果你调用的response的输出流没有主动关闭,服务器会替你关的。
六、HttpServletRequest详解
HttpServletRequest代表着客户端的请求。要客户的信息只要找这个对象即可,该对象由容器创建。
ServletRequest是一个域对象(内部维护了一个Map<String,Object>)
Object getAttribute(Stirng name):
void setAttribute(String name,Object value):
void removeAttribute(String name):
请求参数的编码:
浏览器当前使用什么编码,就以什么编码提交请求参数。<meta http-equiv="content-type" content="text/html; charset=UTF-8">
request.setCharacterEncoding(编码):通知程序,客户端提交的数据使用的编码。但是只对POST请求方式有效
【重要】如果是get请求提交数据,编码就是ISO-8859-1
请求转发、包含、重定向
1、请求转发:(当前应用内转)
请求转发借助于RequestDispatcher: RequestDispatcher.forward(request,response)
如何得到RequestDispatcher对象:
方式一:ServletContext.getRequestDispatcher(目标资源的URI);
方式二:ServletRequest.getRequestDispatcher(目标资源的URI);
区别:
方式一中的目标资源的URI必须以"/"开头,否则报错,此"/"就表示的是当前应用(绝对路径表示法)
方式二中的目标资源的URI如果以"/"开头,就表示的是当前应用(绝对路径表示法)。如果不以"/"开头,就表示相对路径。
转发的细节:AServlet(源组件)--->BServlet(目标组件)
注:Response对象是有缓存的。转发前,容器会清空response的缓存
1>转发前会清空response的正文。容器会清空源组件输出的数据。因此,用户只会看到目标组件输出的页面结果。但是,源组件的响应头信息是不清空的。
2>转发页面上只会输出目标组件的输出,源组件的任何页面输出都无效。
原则:转发前,不要刷新或关闭response的输出流。
2、包含:借助于RequestDispatcher: RequestDispatcher.include(request,response)
AServlet(源组件)--->BServlet(目标组件):AServlet包含BServlet的输出内容
包含的细节:
由源组件包含到目标组件时,容器会清空目标组件的头。因此,只有源组件设置的头才有效。但是,目标组件的响应体信息是不清空的。
编码原则:不要在目标组件中设置响应头。(做无用功)
3、重定向:response.sendRedirect(String location)
各种URL地址的写法
相对路径:不是以"/"开头
绝对路径:(建议的)
原则:地址是不是给服务器用的,如果是,"/"就代表着当前应用。如果是给客户端用的绝对路径要加上应用名称。
<link href=path/> 要加/day07
<script src=path/> 要加/day07
<img src=path/> 要加/day07
<a href=path/> 要加/day07
<form action="path"/> 要加/day07
HttpServletResponse.sendRedirect(path) 要加/day07
getRequestDispatcher(String path): 不要加应用名称,"/"就代表着当前应用
ServletContext.getRealPath(path) 不要加,"/"就代表着当前应用
响应消息头:Refresh=2;URL=path 要加/day06
Servlet
最新推荐文章于 2022-07-27 00:13:38 发布