一、简介
接收请求、处理请求、完成响应
二、创建HelloServlet
a) 创建动态WEB项目WEB_Servlet
b) 项目下创建包com.atguigu.web.servlet
c) 包下创建一个类HelloServlet并实现javax.servlet.Servlet接口
d) 在HelloServlet的service()方法中加入一行打印语句System.out.println(“hello”);
e) 在WEB-INF目录下的web.xml文件中注册映射Servlet
f) 启动服务器,在浏览器中访问:http://localhost:8080/WEB_Servlet/HelloServlet
三、Servlet生命周期
Servlet的生命周期指的是Servlet由实例化到被销毁的过程。同样也被分为了三个阶段:实例化、处理请求、被销毁。而每个阶段我们都有对应的方法来实现响应的功能,在实例化阶段需要调用init()方法来做初始化操作,处理请求阶段调用service()方法处理请求,销毁对象之前调用destroy()做释放资源等操作。
四、Servlet相关接口
ServletRequest和ServletResponse
ServletRequest是由容器创建并传递到service()方法中,容器所创建的对象实际上是HttpServletRequest,所以开发中我们都会将ServletRequest强转成HttpServletRequest。
HttpServletRequest的方法:
String getParameter(String paramName):获取指定请求参数的值;
String getMethod():获取请求方法,例如GET或POST;
String getHeader(String name):获取指定请求头的值;
void setCharacterEncoding(String encoding):设置请求体的编码!因为GET请求没有请求体,所以这个方法只只对POST请求有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!
HttpServletResponse的方法
PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。例如response.getWriter().print(“<h1>Hello JavaWeb!</h1>”);
ServletOutputStream getOutputStream():获取字节响应流,当需要向客户端响应字节数据时,需要使用这个流,例如要向客户端响应图片;
void setCharacterEncoding(String encoding):用来设置字符响应流的编码,例如在调用setCharacterEncoding(“utf-8”);之后,再response.getWriter()获取字符响应流对象,这时的响应流的编码为utf-8,使用response.getWriter()输出的中文都会转换成utf-8编码后发送给客户端;
void setHeader(String name, String value):向客户端添加响应头信息,例如setHeader(“Refresh”, “3;url=http://www.atguigu.com”),表示3秒后自动刷新到http:// www.atguigu.com;
void setContentType(String contentType):该方法是setHeader(“content-type”, “xxx”)的简便方法,即用来添加名为content-type响应头的方法。content-type响应头用来设置响应数据的MIME类型,例如要向客户端响应jpg的图片,那么可以setContentType(“image/jepg”),如果响应数据为文本类型,那么还要台同时设置编码,例如
setContentType(“text/html;chartset=utf-8”)表示响应数据类型为文本类型中的html类型,并且该方法会调用setCharacterEncoding(“utf-8”)方法;
void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404:response(404, “您要查找的资源不存在!”)。
ServletConfig
ServletConfig对象对应着web.xml文件中的一个<servlet>元素,例如:想要获取元素中的<servlet-name>的值,那么可以使用servletConfig.getServletName()方法来获取。
ServletConfig对象同样有容器创建,然后作为参数传递给init()方法,可以在init()方法中使用。
ServletConfig的方法:
String getServletName():获取Servlet在web.xml文件中的配置名称,即<servlet-name>指定的名称;
ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;
String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;
在web.xml中为servlet配置初始化参数
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>省略</servlet-class> <init-param> <param-name>username</param-name> <param-value>root</param-value> </init-param> </servlet> |
每一个init-param表示一个初始化参数,通过getInitParameter(String name)可以根据param-name的值获取到param-value的值。
每个servlet中可以配置多个init-param,servlet只能获取自身的初始化参数,而不能获得其他servlet的初始化参数。
五、 GenericServlet
在显示开发中我们如果直接实现servlet接口功能也是可以正常实现的,但是所面临的问题是:如果我直接实现Servlet接口,那接口中的所有方法都必须要实现,但是这些方法有的我们用不到,而有的方法实现起来很麻烦而且没给servlet实现的代码都差不多,于是我们就需要一个抽象类来帮助我们实现servlet接口,实现一些通用的方法,而我们只需要继承GenericServlet就可以了,这样的好处是我们不在需要重写全部方法,而只需要重写必须的和我们需要的方法就可以。
六、HttpServlet
HttpServlet是GenericServlet的子类,他是Tomcat中专门处理HttpServlet的Servlet(实际上Tomcat只处理Http类型的请求)所以在一般情况下我们都会通过继承HttpServlet来实现servlet。
HttpServlet和GenericServlet一样都是一个抽象类,也就是不能对它们进行实例化,与GenericServlet不同HttpServlet中没有任何抽象方法,所以需要我们根据实际需求来重写它的方法。
在GenericServlet中service(ServletRequest req, ServletResponse res)方法是一个抽象方法,必须要由子类实现,而在HttpServlet中已经被实现,而在HttpServlet的实现的方法中会被强制转换成HttpServletRequest和HttpServletResponse(不用担心会出现类型转换异常,因为传过来的对象本身就是该类型)。转换类型之后会调用HttpServlet中的一个重载的service(HttpServletRequest req, HttpServletResponse resp)方法。也就是说如果我们要重写service()方法不必再去重写GenericServlet的service()方法了,而可以直接重写HttpServlet重载的service()方法就可以了。
在service(HttpServletRequest req, HttpServletResponse resp)方法中,会对它的请求类型进行判断,然后根据请求类型的不同再去调用不同的方法。如:post类型的请求回去调用doPost(),get请求回去调用doGet(),delete请求回去调用doDelete(),以此类推。但是在实际应用中我们只会使用到doPost()和doGet(),所以一般情况下我们不会去重写service()方法,而是去重写更简单的doGet()或者doPost()。
七、关于servlet提问:
1、Servlet的构造器调用了几次?
Servlet是单实例的,而调用构造器就是用来创建实例的,所以只调用一次。
2、Servlet是线程安全的吗?
由于Servlet是单实例的,所以当容器调用service方法处理请求时是以多线程的方式调用的,但是因为性能问题所以在这个方法中并没有考虑同步的问题,所以Servlet并不是线程安全的,但是这样做的好处是性能较好。由于Servlet不是线程安全的,所以尽量不要在使用Servlet处理请求时操作变量,因为有可能会出现同步的问题。(实际应用中只有非常高的并发的情况下才有可能出现这个问题,虽然如此但还是尽量不要那么做)。
3、Servlet实例只能在第一次请求时被创建吗?
一般情况下Servlet会在第一次收到请求时被创建,所以当我们第一次访问某个Servlet时会比平时慢一些,这种我们称为第一次惩罚。
如果希望在服务器启动时就创建Servlet的实例,可以在web.xml中进行配置,在servlet标签中还有一个load-on-startup标签,这个标签需要一个大于等于0的整数作为参数,当配置了这个属性后,该Servlet将会在服务器启动时就被创建,且值越小创建的时机越早。
4、url-pattern映射的规则是什么?
url-pattern配置的Servlet映射的地址,他的配置规则如下:
精确匹配:
当前项目下指定的URL必须完全
如:/path/ServletName
只有URL为:http://localhost:8080/项目名/path/ServletName
由此也可知/是代表的项目根目录,而在html中/代表的是服务器根目录
路径匹配:
当前项目下指定路径的URL地址
如:/path/*
当URL为:http://localhost:8080/项目名/path/任意值
全匹配:
当前项目下所有的URL地址
如:/*
所有URL都可以
后缀匹配:
当前项目下指定后缀的URL地址
如:*.action
当URL为:http://localhost:8080/项目名/任意值. action
优先级:
1、精确匹配
2、路径匹配
3、全匹配
4、后缀匹配
5、还有一种“/”,这种也是全部匹配,优先级最低
关于“*”:
“*”就是通配符,匹配任意字符
“*”只能出现前面和后面,不能出现在中间
如:/*.action错误
“*”只能出现一次
如:*/*错误
“*”不能单独出现
如:* 错误
5、web.xml文件仅仅是看到的那么简单吗?
web.xml文件整个项目中的配置文件,非常重要。但是纵观我们项目下的web.xml文件似乎内容不多,那如此重要的文件为什么只配置的这么少的内容呢?实际上在Tomcat中还有一个总的web.xml文件,就在%CATALINA_HOME%/conf目录下。(session过期时间啥的)
未完待续……