servlet 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。
1、servlet是一个接口,首先浏览器发送请求到服务器,服务器找应用的web.xml。然后寻找来找到实现了servlet这个接口的类,创建类,启动类里面的init()方法,然后调用Service方法完成请求,然后传回到服务器,服务器返回给浏览器
创建一个servlet的过程
一:创建一个类实现Servlet,实现相应的方法。如图所示,init()和构造方法只在第一次创建的时候执行,接下来就是调用Service方法,不会执行init(),只会刷新Service方法,当应用被卸载的时候调用destory()方法
public class ServletDemo1 implements Servlet {
/**
* 构造方法和init方法都只执行一次,在打开这个页面的时候
* service打开和刷新的时候都会执行
* destory方法在应用被卸载的时候调用
* */
public ServletDemo1() {
System.out.println("构造方法执行了");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
}
@Override
public ServletConfig getServletConfig() {
System.out.println("getServletConfig");
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service");
}
@Override
public String getServletInfo() {
System.out.println("getServletInfo");
return null;
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
二:修改WEB-INF当中的web.xml文件,给servlet提供一个可访问的URI地址。
- 首先服务器通过uri找到应用然后找到与uri对应的< servlet-mapping>这个节点,找打servlet的名字
- 服务器是通过 < url-pattern>这个节点来匹配来寻找的,这里给的名字是/app
- 通过< servlet-name>找到实现servlet这个类的名字,然后找到对于的servelt节点,然后找到类的路径。
- 服务器自己创建对应的类,然后调用service()方法来完成交互
- service()方法中的参数servletRequest和servletResponse是服务器传递的,一个是请求的信息,一个是返回的消息
- 访问http://localhost:8080/firstJavaWeb/app就可以得看到控制台的输出
<servlet>
<servlet-name>servletDemo1</servlet-name>
<servlet-class>com.yanglin.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo1</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>
2、Servlet的声明周期:
实例化–>初始化–>服务->销毁
上类中的已经说明参看类
3、Servlet的三种创建方式
- 实现Servlet接口,如上所示
- 继承javax.servet.GenericServlet类(适配器模式)
继承javax.servlet.http.HttpServlet类(模板方法设计模式)设计当中经常使用。
接下里讲解HttpServlet创建Servlet类,如下所示,需要重写两个方法doPost()和doGet()
public class ServletDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
HttpServlet 这个类的使用了多态的机制,我们来看一下其所有的方法。
它继承了GenericServlet这个类,所有这里有service(ServletRequest,ServletResponse)这两个方法这个方式是实现Servlet这个接口的。所有当服务器访问这个类的时候是类型是这样的。
Servlet s = new HttpServlet()
这里是父类的引用指向了子类对象,这是这个父类是父类的父类。这里重写的了这个方法,所有动态绑定调用的是HttpServlet()这个类的Service方法。方法的实现源码
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
从源码中可以看到,HttpServletRequest,HttpServletResponse 是继承了ServletRequest ,ServletResponse 这两个接口,应该是有拓展的。最有调用HttpServlet自己的service(HttpServletRequest,HttpServletResponse )来处理,方法源码:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
这里这个类处理了请求方式,主要是调用doPost()和doGet()方法。判断方法后分别调用不同的方法来处理。这里给我们封装了一些实用的细节,我们只需要实用这个模板然后重写里面的doPost()和doGet()等方法来实现我们知己的需求就行了。
小技巧:使生成的servlet更清新一些 找到:MyEclipse\Common\plugins目录
把com.genuitec.eclipse.wizards_9.0.0.me201108091322.jar复制到上面目录
3、servet映射细节:处理url的规则
- servet映射细节: 通配符* 代表任意字符串
- url-pattern: *.do 以*.字符串的请求都可以访问 注:不要加/
- url-pattern: /* 任意字符串都可以访问
url-pattern: /action/* 以/action开头的请求都可以访问
优先级:从高到低
绝对匹配(具体的url)–> /开头匹配(一/开始) –> 扩展名方式匹配(.do或者.action等)
4、线程安全
因为httpServlet的service()方法都是多线程的,所以会导致最好不要有全局变量,引用全局变量会导致变量共享。
5、Servlet获取配置信息
- ServletConfig的使用
1.获取servlet的配置信息,这个配置信息是写在web.xml当中的例如如下,有编码的配置信息,就可以通过ServletConfig获取,而且获取的方式多样,这里说两个看代码。 - 获取ServletContext()
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>ServletDemo2</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
//way 1获取ServletConfig来得到局部编码
String s = this.getServletConfig().getInitParameter("encoding");
//way 2能得到全局的配置信息,和这个servlet下面的配置信息
String encoding = this.getInitParameter("encoding");
一般而言使用的是第二个,因为httpServlet继承的GenericServlet实现了getInitParameter()这个方法。但是这个只能获取全局的配置信息,以及这个servlet下面的配置信息。不能获取其他servlet的配置信息。
5、ServletContext
ServletContext: 代表的是整个应用。一个应用只有一个ServletContext对象。是单实例。
- 在一定范围内(当前应用),使多个Servlet共享数据。
在ServletDemo2的service()中写如下
//通过调用GenerServlet的getServletContext得到ServletContext
ServletContext context = this.getServletContext();
context.setAttribute("name","zeroyoung");
在ServletDemo的service()下可以访问到如下
ServletContext context = this.getServletContext();
String name = (String) context.getAttribute("name");
//String encoding = this.getInitParameter("encoding");
System.out.println(name);
- 获取全局配置信息
就是如上一节当中的,this.getInitParameter()这个方法,全局web.xml如下。
<context-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</context-param>
<context-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</context-param>
- 获取资源路径:可以获取这个应用当中所有的资源文件
方法:getRealPath(String path)
需要注意的是在src下面的文件,在部署的时候都是放到web-inf这个目录下的classes目录下面的。所有当你在src下面创建一个a.properties文件的时候路径需要些成“/WEB-INF/classes/a.properties”
//获取资源路径,在src路径下面的东西,最后打包到服务器上的时候是在WEB-INF/classes目录下面的
//其中都得以/开始,因为这个代表对着项目目录下
String path = this.getServletContext().getRealPath("/WEB-INF/classes/a.properties");
Properties p = new Properties();
p.load(new FileInputStream(path));
System.out.println(p.get("key"));
这里也说明了properties文件读取方式
Properties p = new Properties();
p.load(new FileInputStream(path));
System.out.println(p.get(“key”));
最有给出这些servlet中类的关系图:
如果是通过参数调用的就叫依赖,而当时通过方法得到的就叫挂链。
这里里面servlet继承依赖多个接口,从而具备不同的功能,然后通过其他类的实现这个类,从而具备功能。