注:再看本文章前,请先看我前面写的《Servlet原理解析及作用1》。
一、Servlet中的线程安全问题
*首先我们要明白web应用是并发应用
当用户多次刷新页面,就会请求一次服务端,这个时候就会出现该同一个请求service被两个对象调用现象,存在数据不安全。
解决方案:
(1)在Servlet中定义变量,除非特殊要求,尽量使用局部变量。
(2)如果有需要实例变量时,应做同步处理,且同步代码块尽量包围少的代码。
implements SingleThreadModel接口就可以保证安全这个时候servlet就可能实行两种方式进行加锁:
(1)每次进来一个的访问serlet对象就进行加锁,指导用完后,才给下一个访问加锁。
(2)给每一个访问一个servlet对象(相对繁琐了,而且违背了servlet单独一个对象在内存中的原则)但是安全还是无 法成立。
二、Servlet的配置对象:
ServletConfig:(读取下列数据咋内存中创建该对象(由容器来创建))
作用:代表了Servlet配置中的参数信息。
比如在web.xml中的参数配置如下:
<servlet>
<servlet-name>ServletDom2</servlet-name>
<servlet-class>com.dp.java.servlet.ServletDom2</servlet-class>
<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>
问题来了!!怎么调用方法呢?用 init(ServletConfig config)周期方法
public class ServletDom2 extends HttpServlet {
private ServletConfig config;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test1();//去配置固定值
test2();//取配置所有init的值
test3();//取配置servlet-name
}
private void test3() {
//得到servlet元素文本
String s_name=config.getServletName();
System.out.println("servlet-name: "+s_name);
}
private void test2() {
//得到所有init元素标签,并打印所有映射的value值
Enumeration enu=config.getInitParameterNames();
while(enu.hasMoreElements()){
String parmname=(String)enu.nextElement();
System.out.println("init-param的name值: "+parmname+" " +
" init-param的value值: "+config.getInitParameter(parmname));
}
}
private void test1() {
//得到init的AAA所映射的value值
String AAA_value=config.getInitParameter("AAA");
System.out.println(AAA_value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
@Override
//组件创建对象
public void init(ServletConfig config) throws ServletException {
this.config=config;
}
}
结果:
三、ServletContext详解
1、在应用被服务器加载时就创建ServletContext对象的实例。每一个JavaWeb应用都有唯一的一个 ServletContext对象它就代表着当前的应用。
2、如何得到ServletContext对象:ServletConfig.getServletContext();
(1)第一种,写一个ServletContext对象来调用
private ServletConfig config;
//一个web应用只有一个servletcontext对象
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
config.getServletContext();
}
public void init(ServletConfig config) throws ServletException {
this.config=config;
}
(2)第二种直接调用,因为在HttpServlet的父类关系中写得有。
ServletContext sc=getServletContext();
3、有什么用呢?
最重要的一个作用实现Servlet对象之间可以通过ServletContext(就因为它可以得到类对象名)对象来实现通讯, 所以ServletContext对象也被称为Context域对象。
3.1ServletContext对象是一个域对象(域对象就是说其内部维护了一个Map<String,Object>)
Object getAttribute(String name):根据名称获取绑定的对象
Enumeration getAttributeNames() :获取ServletContext域中的所有名称
void removeAttribute(String name):根据名称移除对象
void setAttribute(String name,Object value):添加或修改对象。
3.2实现多个Servlet之间的数据共享
这个举一个例子:
在ServletContext对象中放入数据
// ServletContext的练习
public class ServletDom3 extends HttpServlet {
private ServletConfig config;
//一个web应用只有一个servletcontext对象
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext sc=config.getServletContext();
sc.setAttribute("P", "放了一个p对象");
response.getOutputStream().write("已经放了".getBytes());
}
public void init(ServletConfig config) throws ServletException {
this.config=config;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
在ServletContext对象中放入数据
public class ServletDom4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext sc=getServletContext();
String obj=(String)sc.getAttribute("p");
response.getOutputStream().write(obj.getBytes());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3.3获取WEB应用的初始化参数(应用的全局参数)
在web.xml的根元素下配置一下信息:
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
这些参数就属于整个应用的全局参数,使用ServletContext来读取。 可以直接进行得到全局value。
3.4读取资源文件的三种方式:
利用ServletContext.getRealPath():
特点:读取应用中任何文件。只能在Web环境下用
利用ResourceBundle读取配置文件
特点:可以用在非web环境下。但是只能读取类路径中的properties文件
利用类加载器读取配置文件(专业)
特点:可以用在非web环境下。可以读取类路径下的任何文件。
举一个例子:
package com.dp.java.servlet;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
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 ServletDom6_file extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test2(response);
}
/**
*最简单的文件下载
*/
private void test2(HttpServletResponse response)
throws FileNotFoundException, IOException {
//得到要下载的文件
ServletContext sc=getServletContext();
String path=sc.getRealPath("/1.jpg");//根目录下的真实照片文件路径,必须通过它
System.out.println(path);//打印路径
//截取文件名
String filename=path.substring(path.lastIndexOf("\\")+1);
System.out.println(filename);
//构建输入流
InputStream in=new FileInputStream(path);
//通知客户端的形式打开
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(filename, "UTF-8"));
response.setHeader("Content-Type", "application/octet-stream");
//得到输出流
OutputStream out=response.getOutputStream();
int len=-1;
byte b[]=new byte[1024];
while((len=in.read(b))!=-1){
out.write(b, 0, len);
}
}
private String URLEncoder(String filename, String string) {
// TODO Auto-generated method stub
return null;
}
/**
*最简单的文件下载
*/
private void test1(HttpServletResponse response)
throws FileNotFoundException, IOException {
//得到要下载的文件
ServletContext sc=getServletContext();
String path=sc.getRealPath("/1.jpg");//根目录下的照片
System.out.println(path);//打印路径
//构建输入流
InputStream in=new FileInputStream(path);
//通知客户端的形式打开
response.setHeader("Content-Disposition", "attachment;filename=1.jpg");
response.setHeader("Content-Type", "application/octet-stream");
//得到输出流
OutputStream out=response.getOutputStream();
int len=-1;
byte b[]=new byte[1024];
while((len=in.read(b))!=-1){
out.write(b, 0, len);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}