一、线程安全问题:
当多个客户端并发访问一个Servlet时,Web服务器会为每一个客户端请求创建一个线程,并在线程上调用servlet的service方法,而当service方法访问共享数据时,就存在线程安全问题了。
在早期的处理此线程安全问题的一种思路是让Servlet实现SingleThreadModel接口,此接口是一个标识接口,各web服务器对此的解释不同,一般分为每个请求创建一个实例对象与调用service方法同步。Tomcat是采取了方式一为每个请求都创建一个实例。但这两种方式都不合适,因为servlet2.4规范中已将SingleThreadModel接口过时了。而真正的处理此线程安全的合理方式时,应该尽量避免使用共享数据,尽量定义局部变量。
二、Servlet可配置参数及读取参数信息
1.在web.xml中,需要配置<servlet>及<servlet-mapping>标签,还可以在<servlet>标签下面定义子元素<init-param>,在此子元素下面配置<param-name>与<param-value>子元素。而读取<servlet>子元素<init-param>中配置的信息,可以用ServletConfig对象的getInitParameter方法。因为ServletConfig对象是,Web容器在创建Servlet后,调用servlet的init方法时传进去的,所以各种servlet的ServletConfig对象各不一样。同一servlet中可以配置多个init-param元素,同一param-name为被后面的覆盖。
举例如下:
<servlet>
<servlet-name>ServletConfigD1</servlet-name>
<servlet-class>com.itheima.servlet.ServletConfigD1</servlet-class>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql</param-value>
</init-param>
<init-param>
<param-name>url</param-name><!-- 允许有重复的,有重复的时最终读取的最后一个url -->
<param-value>jdbc:oracle</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>scott</param-value>
</init-param>
<init-param>
</servlet>
而具体读取代码示例:getServletConfig().getInitParameter(“url”)。
2.在servlet中另一种方式读取配置信息,就是将param配置在整个应用级中,为所有servlet共享,则定义在servlet元素的外面,与servlet元素同一层级。而读取应用级的读取信息,要用到应用级的对象ServletContext对象,ServletContext对象一个应用只有一个对象,在应用被web服务器加载时被加载初始化创建对象,而在应用被web服务器停用或卸载时消亡。是随着整个应用的加载而加载,随着整个应用的消亡而消亡的。具体举例如下:
<context-param>
<param-name>encoding</param-name><!--注意context-param中不允许有重复的param -->
<param-value>utf-8</param-value>
</context-param>
读取配置信息encoding,只需要在servlet中使用getServletContext().getInitParameter(“encoding”)即可。
三、三种读取配置文件的方式,另详细。见三种读取文件方式。
四、HttpServletResponse对象详解
1.输出中文内容
在response对象中,输出流可以有response.getOutputStream的字节流输出与response.getWriter字符流输出。而对中文的输出,若没有进行输出的编码说明时,直接输出中文,OutputStream没有乱码问题,而Writer会有乱码问题。
原因:response.getOutputStream采用的是默认的编码GBK,而response.getWriter采用的是ISO-8859-1编码的。而客户端浏览器也采用默认的编码GBK,所以字节流没有问题,而字符流出现了乱码。
解决向:客户端输出中文乱码文件,
对于字节流OutputStream来说:可以用合适的str.getBytes(“utf-8”)编码后输出。但输出前要告诉浏览器用了什么编码了,这个信息一般放在响应头中,如response.setHeader(“content-type”,”text/htm;charset=utf-8”),当然也可以用response.setContentType(”text/htm;charset=utf-8”),或者write("<meta http-equiv='Content-Type'content='text/html; charset=utf-8' />".getBytes());
对于字符流Writer来说,可以直接用response.setContentType(”text/htm;charset=utf-8”)来搞定即可,它相当于
response.setCharacterEncoding(“UTF-8”);response.setHeader(“content-type”,”text/htm;charset=utf-8”);两句。
2.中文文件名下载乱码问题:
在下载前的response.setHeader(“content-disposition”,”attachment;filename=strNam”);需要注意,不能直接将strName输出,要用URLEncoder进行encode(strNam,”utf-8”)进行编码后,再输入。因为这种中文名下载会像在百度输入关键字,采用get请求输入中文参数一样,会经过http的URL编码的。
3.控制浏览器缓存一页面时间为1小时(不常变的页面,为了减轻服务器的压力,而设置缓存时间长点)
response.setDateHeader(“Expires”, System.currentTimeMillis()+1000*60*60);
4.生成随机图片思路:
就是用BuffedImage,得到Graphics画笔后,先设置color后,画个边框drawRet..,再设置另一clor接着在框中填充前景fillRet…,接着画些干扰线如drawLine,接着生成随便几个数字用drawString画出来,最后将画在内容中的数据,输出在response.getOutputStream中,用ImageIO.write即可了。
5.请求重定向
前遍http入门中讲到在响应状态码中包括302与307状态码即是请求重定向了。它是http请求一组件时,此组件告诉浏览器不要向它请求,告诉了另一个地址,让浏览器重写向新的资源地址请求。所以这个过程发生了两个http请求。则表现在url地址会变化,且是两次http请求增加了服务器的负担。
代码示例:
Response.setState(302) 与response.setHeader(“Location”,”/demo5/servlet/SD1.do”)也可以用一句搞定response.sendRedirect(”/demo5/servlet/SD1.do”)
6.Response输出流的一些细节:
6.1在servlet中不能同时使用getOutputStream与getWriter方法,两者是互斥着。
6.2在servlet中输出的内容只是缓存进了response的输出流中,而Web容器会根据输出流缓存内容进行组织下加些响应头输出来。
6.3Web容器会在调用servlet的service方法后,检查response的输出流是否已经关闭了,若没有关闭则web容器会关闭输出流。
五、HttpServletRequest详细:
1.常用的一些获取客户端信息方法:
a)getRequestURL方法返回客户端发出请求时的完整URL。http://.../ServletReLine
b)getRequestURI方法返回请求行中的资源名部分。如/servlet/ServletRequestLine
c)getQueryString方法返回请求行中的参数部分。
d)getRemoteAddr方法返回发出请求的客户机的IP地址
e)getRemoteHost方法返回发出请求的客户机的完整主机名
f)getRemotePort方法返回客户机所使用的网络端口号
g)getLocalAddr方法返回WEB服务器的IP地址。
h)getLocalName方法返回WEB服务器的主机名
i)getMethod得到客户机请求方式 GET或者POST
j)getContextPath方法返回应用上下文地址:如 /demo5
2.获取请求头信息
a)getHead(name)方法返回指定name的值的头信息
b)getHeaders(String name)方法返回同一name多个值的头信息,指Warning头信息。
c)getHeaderNames方法所以头的name信息
3.获取客户端请求参数(客户端提交的值):
a)getParameter(name)方法用在一个name一个值
b)getParameterValues(String name)方法用在一个name多个值
c)getParameterNames方法将所有请求参数名返回
d)getParameterMap方法 //做框架用,非常实用,包含请求参数名与值
注意这里有个BeanUtils.populate(Obj,request. getParameterMap)可设值到obj.
e)getInputStream
4.解决请求参数乱码问题:
GET方式的乱码:
如<a href=”/demo5/servlet/RD2?name=中国”>CN</a>,直接用request.getParameter得到的字符串strCN将会乱码,这也是因为GET方式是用http的url传过来的默认用iso-8859-1编码的,所以首先得到的strCn要再用iso-8859-1编码得到原文后,再进行用utf-8(看具体页面的charset是什么utf-8或gbk)进行解码即可。new String(strCn.getBytes(“ISO-8859-1”),“UTF-8”);
POST方式的乱码:只需要request.setCharacterEncoding("UTF-8"):即可。
5.表单数据的获取:
Checkbox、input、select等等,因checkbox一般是复选代表一组通常name属性是一值的,有多个值。所以要用request.getParameterValues(name)
6.request域对象与请求转发
request也是一域对象,可以用request.setAttribute(“name”,”value”)。在用在请求转发时,在另一servlet中,可以用request.getAttribute(“name”)得到值。当然不能用在response.sendRedirect中(这个是请求重定向两个http请求,两个不同的request)。而请求转发是指请求一组件时,此组件将请求request对象与去掉响应体的response对象原样传到另一servlet组件中,由另一servlet组件来处理请求。所以此时表现在:是一个http请求,URL地址没有变化。同一request对象,源response对象去掉响应体传到目标servlet中。
具体代码如下:
RequestDispatcher rd = request.getRequestDispatcher(path);
rd.forward(request,response);//在forward前,窗口将把response中的响应体去掉,因为在源组件servlet中out.write的任何内容将丢失了。注意在源组件servlet中不能flush输出流或关闭输出流。
7.得到RequestDispatcher的方式,有以下两种:
7.1 Request.getRequestDispatcher(path)可用绝对路径/开关也可用相对路径,建议用绝对路径来搞定。
7.2 getServletContext().getRequestDispatcher(path),path只能是以/开头
8.包含
可以在一组件servlet中用请求分派器RequestDispatcher的include方法,将目标组件servlet中的响应结果包含进源组件中,两种响应体输出结果一起输出出来。注意:目标组件中的servlet响应头将会被丢失:
具体代码如下:
RequestDispatcher rd = request.getRequestDispatcher(path);
rd.include(request,response);//目标中的响应头将会丢失。
9.各种相对路径与绝对路径的写法
若路径地址是给服务器用的,则不需要加应用名。直接以/开头代表应用路径/servlet…
若路径地址是给客户端用的,则需要加应用名。要用/demo5/servlet…
具体应用场景:
9.1 请求转发与包含是服务器行为可以用Request.getRequestDispatcher(“/servlet/SD2.do”)
9.2 请求重定向,用可以reqeust.sendRedirect(“/demo5/servlet/SD2.do”)
<ahref/> 客户端用的/demo5
<scriptsrc=""/> 客户端用的/demo5
<imgsrc=""/> 客户端用的/demo5
<linktype="text/css" href=""/>客户端用的/demo5
<formaction=""/>客户端用的/demo5