servlet其实就是一个java类,是用来处理客户端请求的类,servlet被装载在servlet容器中,servlet容器一般寄存在服务器端。
1.我们如何创建一个servlet类呢?
1)创建servlet类就是创建一个实现Servlet接口的类
2.servlet的生命周期:servlet类实例在第一次被请求时被创建,在服务器关闭时被销毁。其实就是所谓的单例。
servlet实例是个单例。那么既然是单例那么就会出现一个问题,那么就是线程不安全的。比如说:
package com.yd.servlet;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.omg.CORBA.SystemException;
//创建一个servlet类
public class HelloWord implements Servlet {
private int ticket=4;
public HelloWord() {
System.out.println("constructor");
}
@Override
public void destroy() {
System.out.println("我是servlet类被destroy的方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
if(ticket>0){
System.out.println("你买到"+ticket+"张票了");
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
}else{
System.out.println("对不起没有票了");
}
System.out.println("客户端请求servlet类时调用的service方法");
}
}
如上的代码,如果同一时间我有多个客户端请求我的HelloWord这个Servlet类时我的全局变量ticket不能正确的被访问。
那么这里我总结了两点避免出现以上的线程不安全问题
①如果一个变量需要被所有的客户端访问,那么在访问该变量的时候加上同步机制即可
synchronized(对象){
//在此访问共享变量
}
实现如下即可,至于synchronized可以参考我的另一个博客:http://blog.csdn.net/nihaowoshiyudong/article/details/53389543
synchronized (this) {
if(ticket>0){
System.out.println("你买到"+ticket+"张票了");
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
}else{
System.out.println("对不起没有票了");
}
}
②如果变量不需要被所有的客户端访问,那么只要在方法中声明即可。
3.servlet类中包含四大方法1)constructor构造器,在客户端第一次请求servlet类时servlet容器创建servlet类是调用,并且只有在第一次调用时被创建
接下来再次请求该servlet类时,servlet容器会先看一下有没有该servlet类的实例,如果有直接拿过来使用,如果没有就创建
总的来说servlet实例是一个单例。
2)init方法和constructor一样只有在第一次请求servlet类时被调用
3)service方法是每次请求都会执行的方法,她带有两个参数一个是HttpServletRequest(请求对象),
一个是HttpServletResponse(响应对象)。
4)最后一个就是destroy方法了,只有在服务器关闭时,实例才会调用该方法,销毁自己。用于释放当前servlet所占用的资源
package com.yd.servlet;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.omg.CORBA.SystemException;
//创建一个servlet类
public class HelloWord implements Servlet {
public HelloWord() {
System.out.println("constructor");
}
@Override
public void destroy() {
System.out.println("我是servlet类被destroy的方法");
}
@Override
public ServletConfig getServletConfig() {
System.out.println("getServletConfig()");
return null;
}
@Override
public String getServletInfo() {
System.out.println("getServletInfo()");
return null;
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
//获得servletconfig中的servlet类的初始化参数
//获得当servlet类的注册名
/*String instance=servletConfig.getServletName();
System.out.println("我是servlet类的注册名:"+instance);
//获得servlet类的初始化参数名
Enumeration<String> initnames= servletConfig.getInitParameterNames();
while(initnames.hasMoreElements()){
//获得初始化的参数名
String name=initnames.nextElement();
//通过参数名获得参数值
String value=servletConfig.getInitParameter(name);
System.out.println("name:"+name+"\tvalue:"+value);
}*/
//获得servletContext
ServletContext servletContext=servletConfig.getServletContext();
//通过servletcontext获得web应用的初始化参数
Enumeration<String> names=servletContext.getInitParameterNames();
while(names.hasMoreElements()){
String name=names.nextElement();
String value=servletContext.getInitParameter(name);
System.out.println("name:"+name+"\tvalue:"+value);
}
//获得文件在服务器中的位置
String path=servletContext.getRealPath("/business/a.txt");
System.out.println(path);
//结果如下:C:\Users\WenQu\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\J2EE_Test01\business\a.txt
//以流的形式获得当前web工程下文件
InputStream is=servletContext.getResourceAsStream("/business/a.txt");
byte[] bs=new byte[1024];
int n=0;
try {
while((n=is.read(bs))!=-1){
String str=new String(bs, 0, n, "utf-8");
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(is);
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
System.out.println("客户端请求servlet类时调用的service方法");
}
}
3.创建好servlet需要在服务器端配置该servlet类,这样客户端请求该servlet类时才能快速准确到找到处理请求的servlet类。配置servlet类有两种方法
1)通过配置文件web.xml来配置servlet
<servlet> //这是配置一个servlet
<servlet-name>response</servlet-name> //servlet的名字
<servlet-class>com.yc.servlet.TestServlet</servlet-class> //servlet的处理类
<init-param>
<param-name>unicode</param-name> //servlet的初始化参数名
<param-value>utf-8</param-value> //与servlet初始化参数名对应的参数值
</init-param>
<!--配置servlet被创建的时机 -->
<load-on-startup>数值<load-on-startup> //如果数值为负数那么当前servlet将在第一次被请求时创建
//如果是0或整数则会在当前应用被servlet容器加载时创建当前servlet类 且数值越小越先被创建
</servlet>
<servlet-mapping> //配置servlet的映射关系的
<servlet-name>response</servlet-name> //上面配置的servlet名字
<url-pattern>/yd</url-pattern> //请求地址
</servlet-mapping>
上面的配置文件的意义:当请求地址为项目名称/yd时,服务器就找到配置的servlet-mapping中的url-pattern中与之对应的地址,然后根据servlet-name找到servlet-class中的处理类来处理客户端的请求。
2).第二种配置servlet的方法很简单就是我们的注解方式配置servlet。
语法为:@WebServlet("请求地址"),意思同上当客户端地址栏的地址为请求地址时,
则服务器就会把该请求交给对应的servlet处理类来处理。
4.关于servlet-mapping
1)同一个servlet类可以被映射到多个url上,也就是多个servlet-mapping下的servlet-name可以是同一个servlet类的注册名
2)servlet-mapping下的url-pattern中可以使用*通配符,通配符的使用可以是网站开发的拓展性更好,但是只有两种格式:
一种格式是*.拓展名 另一种是以/开头..../*结尾
关于浏览器端发送的url请求,web服务器在把url映射到servlet处理类时有通配符时是按照一定原则来匹配的。
①精确路径匹配。例子:比如servletA的url-pattern为/test,servletB的url-pattern为/* ,这个时候,如果我访问的url为http://localhost/test,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
②最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。这也是精确匹配的一种。
③*.拓展名的匹配度最低。
5.关于配置Servlet类时有时加上<load-up-startup>那么这个节点是用来设置什么的呢?
<servlet> //这是配置一个servlet
<servlet-name>response</servlet-name> //servlet的名字
<servlet-class>com.yc.servlet.TestServlet</servlet-class> //servlet的处理类
<init-param>
<param-name>unicode</param-name> //servlet的初始化参数名
<param-value>utf-8</param-value> //与servlet初始化参数名对应的参数值
</init-param>
<!--配置servlet被创建的时机 -->
<load-on-startup>数值<load-on-startup> //如果数值为负数那么当前servlet将在第一次被请求时创建
//如果是0或整数则会在当前应用被servlet容器加载时创建当前servlet类 且数值越小越先被创建
</servlet>
其中的数值指的的其在web服务器启动时被web服务器创建的顺序。服务器启动时就会创建servlet类的实例并且调用其init方法。其最重要的用途就是为整个web项目做初始化准备,例如为站点准备数据或者启动线程等。
6.关于inti方法中参数ServletConfig是包装servlet类的配置信息的,并且可以获得ServletContext
1)如何配置servlet的信息呢?
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yd.servlet.HelloWord</servlet-class>
<init-param>
<param-name>user</param-name>
<param-value>master</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>a</param-value>
</init-param>
</servlet>
如下在servlet节点中添加init-param节点并且设置param-name和param-value的值即可。一个servlet类可以设置多个init-param参数节点2)那么如何来获得在web.xml中给servlet类配置的init-param呢?
就是通过我们的ServletConfig来获得,具体实现如下代码:
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
//获得servletconfig中的servlet类的初始化参数
//获得当servlet类的注册名
String instance=servletConfig.getServletName();
System.out.println("我是servlet类的注册名:"+instance);
//获得servlet类的初始化参数名
Enumeration<String> initnames= servletConfig.getInitParameterNames();
while(initnames.hasMoreElements()){
//获得初始化的参数名
String name=initnames.nextElement();
//通过参数名获得参数值
String value=servletConfig.getInitParameter(name);
System.out.println("name:"+name+"\tvalue:"+value);
}
}
7.通过servletConfig可以获得ServletContext对象,那么servletContext是什么呢?API中是这样解释的:Defines a set of methods that a servlet uses to communicate with its servlet container
也就是定义一个方法的集合用来和他的servlet容器交流的一个servlet特殊类。通俗的说ServeltContext对象就是用来管理当前的web项目的,负责当前的web工程和servlet容器做交流的特殊的servlet类。从servletcontext可以获得当前web项目下的一些资源的信息,可以被当前web项目下的所有servlet类访问
1)那么如何给ServletContext设置参数呢?
在web工程下的web.xml中配置如下:
<context-param> //通过context-param设置的参数可以被所有的servlet类使用
<param-name>参数名</param-name>
<param-value>参数值</param-value>
<context-param>
2)其重要的方法有一下几个:getInitParameter(java.lang.String name):通过参数名获得servletcontext的初始化参数
getInitParameterNames():获得初始化所有参数的名字
getRealPath(java.lang.String path) :获得一个文件在服务器中的位置
getResourceAsStream(java.lang.String path):获得一个资源以流的形式
还有一些获得上下文属性的一些方法也很重要。