今天刚接触Servlet,并作了如下总结,如有哪里错误请指教!(PS:后面的知识还有持续更新,如有需要的朋友,请关注······)
1. 如何创建一个你的第一个servlet?
**在tomcat中新建一个web应用,然后在web应用中新建一个web-inf/classes目录。
//**在classes目录中新建一个FirstServlet,例如:
package com.Cecilia.servlet;
import java.io.*;
import javax.servlet.*;
public class FirstServlet extends GenericServlet{
public void service(ServletRequest req,ServletResponse res)throws Exception{
OutputStream out=res.getOutStream();
out.write("Hello Servlet!".getBytes());
}
}
//**set classpath-%classpath%;//配置路径
//导入servlet.jar;//编译servlet
//**在wWEB-INF目录中新建一个web.xml文件,配置servlet的对外访问路径;
//**启动tomcat完成。
看到这里,相信大家对Servlet有个大致的轮廓了解,那么接下来让我们站起来从整体上来看看Servlet,如下图:
2. servlet生命周期
对于每一个生命而言,都会存在生老病死,当然,Servlet也一样,它在开发的过程中也存在生命周期,随着用户访问时出生,又随着Web应用关闭而死亡,那么Servlet的整个生命周期具体是如何的呢?
**servlet对象是用户访问时第一次创建servlet对象驻留在内存中,响应后续服务请求。
**servlet的使用过程中,需要调用初始化init()、request()、response()方法;
**在web服务器被关闭或者web应用被删除的情况下,servlet就会被摧毁的时候,这时需要调用destroy()方法。
另外还提供一个 Servlet的UML原理图,如下:
3.Servlet接口两个实现类
GenericServlet、HttpServlet
HttpServlet指能够处理HTTP协议请求的Servlet,在原有的Servlet接口基础上添加了一些与HTTP协议请求的处理方法,比Servlet接口更强大,通常使用HttpServlet接口,在实现Servlet接口时,覆写了service的方法,该方法体内的代码会自动判断用户的请求方式,如GET请求就会调用GET方法、POST请求就会调用POST方法,开发人员在编写Servlet时,不需要覆写service方法,只需要覆写DoGet犯法或者doPost方法。
**细节部分
***Servlet程序能够被外界访问,可以在web.xml中使用和来完成servlet程序映射到一个URL地址上,用于指定servlet的注册名称,用于指定servlet的对外访问路径;
***Servlet注册使用,包含两个主要的子元素:用于设置servlet的注册名称,用于设置Servlet的完整类名;
*同一个Servlet可以被映射到多个URL地址上,即多个元素的子元素的设置值可以是同一个Servlet的注册名;
**URL地址使用通配符的两种方式:
一种格式是:“.扩展名”,一种格式是:必须是/开头,以“/”结尾;
*当多个servlet都存在匹配问题时,需要考虑这些映射关系的优先级。
eg:下面有一些映射关系:
- Servlet1 映射到/abc/*
- Servlet2 映射到/*
- Servlet3 映射到/abc
- Servlet4 映射到 *.do
- 当请求的URL为“/abc/a.html”,/abc/和/都匹配,Servlet引擎将调用Servlet1;
- 当请求的URL为“/abc”,/abc/*和/abc都匹配,Servlet引擎将调用Servlet3;
- 当请求的URL为“/abc/a.do”,/abc/和.do都匹配,Servlet引擎将调用Servlet1;
- 当请求的URL为“/a.do”,/abc/和.do都匹配,Servlet引擎将调用Servlet2;
- 当请求的URL为“/XXX/yyy/a.do”,/和.do都匹配,Servlet引擎将调用Servlet2;
*(PS:当*.扩展名的形式优先级最低)
***Servlet是一个供其他Java程序调用的Java类,不能独立运行
*针对客户端的多次servlet请求,只会创建一个serlet实例对象,一旦创建,他就会驻留在内存中,为后续其他请求服务,直至web容器退出,servlet才会被摧毁;
*在servlet的生命周期内,servlet的init方法只会被调用一次,而对一个servlet的每次访问请求都会导致servlet引擎的service方法,对于每次请求,Servlet引擎都会创建一个HttpServletRequest请求对象和一个新HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用servlet的service()方法,service方法再根据请求方式分别调用doGet或者DoPost()方法。
4. Servlet的线程安全问题
*出现线程安全问题的原因:
当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service方法,因此在service的内部访问同一个共享资源时,就可能会引发线程安全问题。
**解决线程安全的方法
**SingThreadModel接口—实现SingThreadModel接口,那么servlet引擎将会以单线程模式来调用service方法,SingThreadModel接口中未定义任何方法(类似的接口还有Serializable序列化、Cloneable克隆接口等),只需要实现类添加继承SingThreadModel接口声明就行,这实现SingThreadModel接口的servlet类任然可以支持多线程并发访问,其采用的方法就是产生多个servlet实例对象,并发的每一个线程分别调用一个独立的servlet的对象,这样能够实现servlety引擎创建多个servlet实例对象,能够从侧面上解决线程安全问题,但是不能真正意义上解决多线程同时访问同一个Servlet实例对象的问题;(不过这种方式已经过时!!)
5. ServletConfig API
*当servlet配置初始化参数后,web容器在创建servlet实例化对象时,会自动将这些参数封装到ServletConfig对象中,并调用servlet的init方法时,将ServletConfig对象传递给servlet,进而开发者就可以通过Servlet API获得当前的servlet的初始化参数。
eg:
//得到指定的参数
String value=this.getServletConfig.getInitParameter("data");
//得到所有的参数
Enumeration e=this.getServletConfig.getInitParameterNames();
while(e.hasMoreElements()){
String name=(String) e.nextElement();
String value=this.getServletConfig.getInitParameter(name);
System.out.println(name+“”+value);
}
*ServletConfig对象的作用
**获得字符集编码;
**获得数据库连接信息;
**获得配置文件后,查看Struts案例的web.xml文件。
6. ServletContext API
*当web容器在启动时,它会为每一个web应用创建一个ServletContext对象,代表当前web应用。在一个web应用的所有Servlet共享同一个ServletContext对象,通常也称为Context域对象,也就是web应用作用范围。
*ServletContext的一些方法
**getContext()方法——返回内容
**getResourcePaths()——返回路径
eg:
/welcome.html
/catalog/index.html
/catalog/products.html
/catalog/offers/books.html
/catalog/offers/music.html
/customer/login.jsp
/WEB-INF/web.xml
/WEB-INF/classes/com.acme.OrderServlet.class
getResourcePaths("/") 返回 {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}
getResourcePaths("/catalog/") 返回{"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}.
*ServletContext的作用
**使用ServletContext对象来实现资源共享
**使用Servlet的转发(与重定向的区别,对于客户机而言,发一次请求,使用重定向就发两次请求,而转发只一次)
eg:
RequestDispatcher rd=this.ServletContext.getRequestDispatcher("/1.jsp");
rd.forward(request,response);//转发
**利用ServletContext来读取资源文件——管理Servlet文件(比较重要)
*得到文件路径
*读取资源文件的三种方式
*.xml文件和.properties文件(如果配置文件中的内容之间有关联,那么使用.xml文件配置相关信息,如果配置信息的内容之间不存在关联,比如连接数据库时需要使用的数据库名和数据库密码等信息,这些不存在联系,可以使用.properties文件)。
eg:
******db.properties文件******
url=jdbc:mysql://localhost:3306/test
username=Cecilia
password=123
***********Java代码*********
public class ServletTest extends HttpServlet{
public void doGet(HttpRequest request,HttpResponse response){
//方法一
testRead1();
//方法二
testRead2();
}
public void doPost(HttpRequest request,HttpResponse response){
doGet(request,response);
}
public void testRead1(){
InputStream in=this.getServletContext().getResourceStream("/WEB-INF/classes/db.properties");
Properties p=new Properties();
p.load(in);//加载properties文件
//读取文件路径、用户名、密码
String url=p.getProperty("url");
String username=p.getProperty("username");
String username=p.getProperty("password");
System.out.println(url);
System.out.println(username);
System.out.println(password);
}
//通过ServletContext的getRealPath得到资源的绝对路径,再通过传统流读取资源的文件(一般应用在需要下载资源的情况场景)
public void testRead2(){
String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties")
String filename=path.subString(path.lastIndexOf("\\")+1);
System.out.println("当前读取的资源名称是:"+filename);
//定义传统文件流
FileInputStream fin=new FileInputStream(path);
Properties p=new Properties();
p.load(in);//加载properties文件
//读取文件路径、用户名、密码
String url=p.getProperty("url");
String username=p.getProperty("username");
String username=p.getProperty("password");
System.out.println("当前读取的资源数据是:");
System.out.println(url);
System.out.println(username);
System.out.println(password);
}
}
*如何在WEB应用中的Java程序如何读取资源文件?(一般Servlet不直接操作数据库,只是通过一个Dao层去处理)
eg:
public class ServletTest extends HttpServlet{
public void doGet(HttpRequest request,HttpResponse response){
//实例化Dao对象
UserDao dao=new UserDao();
dao.update();
}
public void doPost(HttpRequest request,HttpResponse response){
doGet(request,response);
}
**************Dao类***********
//通过读取资源文件的程序下不是servlet只能通过类装载器读取(前提是装载入的文件不能太大,否则造成内存溢出)
public class UserDao{
//使用静态代码块封装使用类加载器来读取资源的代码,这样有利于代码的可维护性、可读性
private static Properties dbconfig=new Properties();
static{
try{
//得到类的装载器并将其返回一个输入流
InputStream input=UserDao.class.getClassLoader("db.properties");
Properties p=new Properties();
config.load(in);//加载properties文件
}catch(Exception e){
throws new ExceptionInitialiserError(e);//抛出初始化异常
}
}
//方法一:使用类装载器来读取资源
public void update(){
//读取文件路径、用户名、密码
System.out.println(dbconfig.getProperty("url"));
System.out.println(dbconfig.getProperty("username"));
System.out.println(dbconfig.getProperty("password"));
}
//方法二:先使用类装载得到文件路径,然后通过传统文件流读取资源文件,这样能够得到更新后的资源文件数据
public void update1(){
//如果想要在发布之后修改配置文件并刷新后能得到修改后的数据
InputStream path=UserDao.class.getClassLoader.getResource("db.properties").getPath();//得到资源的路径
FileInputStream fin=new FileInputStream(path);
Properties dbconfig=new Properties();
dbconfig.load(fin);
//读取文件路径、用户名、密码
System.out.println(dbconfig.getProperty("url"));
System.out.println(dbconfig.getProperty("username"));
System.out.println(dbconfig.getProperty("password"));
}
public void select(){}
public void delete(){}
}