什么是servelt
servelt是sun公司提供一个接口,但我们日常所说的servelt是实现了servlet接口的java程序,会被放在服务器上运行
编写servlet的时候不需要main函数,因为写完后,就把编写的程序编译成字节码,放到服务器上面。当前发送就一个请求的时候,服务器就会按照一定规则调用编写的代码
servlet的运行流程
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
开发一个简单的servlet程序
- 新建一个servlet
直接new出来的servlet实现的是HttpServlet接口
public class Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 三个方法
①doGet()
当发送get请求时调用
②doPost()
当发送post请求的时候调用
③service()
每一次发送请求的时候就会调用,有该方法就不会调用① ②方法
- web.xml中配置映射
url-pattern是映射到的url地址,即对外访问路径
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>finalexam.demo.Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/demo/ServletDemo1</url-pattern>
</servlet-mapping>
映射也可以使用通配符写法
servlet的线程安全问题
为什么会有线程安全问题
在之前的servlet的工作流程中直到在一个web应用程序中,同一个servlet对象只会被创建一次就永远驻留与内存,并且以后的servlet请求调用的都是该servlet对象
在多个客户端并发访问同一个servlet的时候,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用同一个servlet对象的service方法,所以如果service方法内使用了统一资源就会有线程安全问题(如访问同一个全局变量)
public class ServletDemo extends HttpServlet {
int i=1;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
i++;
try {
Thread.sleep(1000*4);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write(i+"");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
如何解决
① 加锁,为servlet加锁,每次只能一个客户端调用
但是在实际开发中绝对不可能让用户串行等待访问服务端的servlet
public class ServletDemo3 extends HttpServlet {
int i=1;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 加了synchronized后,并发访问i时就不存在线程安全问题了,
* 为什么加了synchronized后就没有线程安全问题了呢?
* 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁
* 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁,
* 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线程只能排队等候了
*
*/
synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象
i++;
try {
Thread.sleep(1000*4);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write(i+"");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
**② 让Servlet去实现一个SingleThreadModel接口 **
针对Servlet的线程安全问题,Sun公司是提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet
API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
ServletConfig
在web.xml中,可以使用一个或多个标签为servlet配置一些初始化参数。web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,我们通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
web.xml
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>finalexam.demo.Servlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>servletname</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>servletpassword</param-value>
</init-param>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
servlet
public class Servlet extends HttpServlet {
//定义ServletConfig对象来接收配置的初始化参数
private ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//将ServletConfig对象传递给servlet
this.servletConfig = servletConfig;
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = this.servletConfig.getInitParameter("name");
System.out.println(name);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}