- servlet是sun公司提供的一门用于开发动态web资源的技术
- servlet技术基于Resquest-Response编程模型
- sun公司在其API中提供了一个Servlet接口,编写一个servlet需要两步骤
- 编写一个Java类实现Servlet接口
- 把Java类部署到web服务器中
学习目标:手动编写一个Servlet
Servlet支持Java包 tomcat\lib\servlet-api.jar
在Web.xml文件中,一个<servlet>元素用于注册一个Servlet,<servlet>元素中包含有两个主要的子元素:<servlet-name>和<servlet-class>它们分别用于设置Servlet的注册名称和指定Servlet的完整类名,如下所示:
<servlet> <servlet-name>ServletDemo1</servlet-name> <servlet-class>com.malinkang.web.ServletDemo1</servlet-class> </servlet>
<servlet-mapping> <servlet-name>ServletDemo1</servlet-name> <url-pattern>/ServletDemo1</url-pattern> </servlet-mapping>
url-pattern三种匹配方式:
1.完整路径配置(不含通配符*,以/开始)
2.目录匹配(含通配符*,必须以/开始,以/*结尾)
3.扩展名匹配(含有通配符*、必须不以/开始,以*.xxx结尾):例如*.do
匹配优先级:完整路径匹配>目录匹配>扩展名
缺省Servlet:如果某个Servlet映射路径仅仅为一个正斜杠,那么这个Servlet就是当前web应用的缺省Servlet。
作用:凡是在web.xml文件中找不到匹配的资源,它们的访问请求都将交给缺省Servlet。
所有服务器端 静态web资源 都是由缺省Servlet 返回给客户端
默认缺省Servlet 在tomcat/conf/web.xml
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
将上面的listings值改为true访问某个资源目录,则以列表显示。
1.浏览器访问Servlet的过程
1.浏览器根据用户的请求生成http请求消息,并将其发给web容器
2.Web容器检查内存中是否存在负责处理当前请求Servlet程序的实例对象,如果不存在,则加载和创建该Servlet对象。
3.web服务器创建针对本次访问的请求对象和响应对象。请求对象包含了http的请求消息,从它里面可以获取http请求消息的内容:响应对象用于封装将要回送给浏览器的http响应信息,响应对象中的初始化内容为空,但是以后可以调用各种方法来生成http响应消息的各个部分。
4.web服务器调用Servlet的service方法,并将请求对象和响应对象传递给该方法。
5.Servlet对象的service方法从请求对象中读取该请求消息,并向响应对象中写入响应头和响应体消息。
6.service方法执行完毕
7.web服务器从响应对象中读取响应消息。
2.Servlet生命周期
第一次访问创建Servlet对象,调用init方法和service方法
第二次访问只调用service方法,没有创建Servlet对象也没有调用init方法。在Servlet整个生命周期中Servlet对象只创建一次,init方法也只调用一次。
当服务器正常关闭时,调用destory方法
3.Servlet自动加载
默认情况下Servlet在第一次访问时,创建对象和执行init。如果希望服务器启动时创建Servlet对象、执行init,则要配置web.xml添加<load-on-startup>
<servlet> <servlet-name>InitServlet</servlet-name> <servlet-class>cn.itcast.servlet.InitServlet</servlet-class> <!-- 配置Servlet 随服务器启动时 进行创建初始化 --> <!-- 中间值可以 0-9 代表加载优先级 0 最高 --> <load-on-startup>0</load-on-startup> </servlet>
ServletConfig:在Servlet初始化阶段,向Servlet传递数据。
当Servlet配置了初始化参数后,当web容器创建了Servlet的实例对象会自动将这些初始化参数封装到ServletConfig对象,调用init(ServletConfig config)方法将ServletConfig对象传递给Servlet。Servlet.getServletConfig()方法必须返回init(ServletConfig config)方法传递进来的这个ServletConfig对象的引用。
应用:1.在web.xml进行初始化参数的配置
<init-param> <param-name>company</param-name> <param-value>google</param-value> </init-param> <init-param> <param-name>city</param-name> <param-value>北京</param-value> </init-param>
public class ServletTest2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //根据初始化参数名字获取初始化值 String value = this.getServletConfig().getInitParameter("company"); response.getOutputStream().write(value.getBytes()); //获取所有初始化名字 Enumeration<String> e = this.getServletConfig().getInitParameterNames(); while (e.hasMoreElements()) { response.getOutputStream().write(e.nextElement().getBytes()); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
Web容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
ServletConfig对象中维护了ServletContext对象的引用,开发人员在创建servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。由于一个web应用中的所有Servlet共享同一个ServletContext对象,所以多个Servlet通过ServletContext对象实现数据共享。ServletContext对象通常被称为context域对象。
ServletContext应用
1.提供全局初始化参数
<!-- 全局初始化参数 --> <context-param> <param-name>name</param-name> <param-value>zhangsan</param-value> </context-param>
public class ServletTest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ServletContext context=this.getServletConfig().getServletContext();可以简化为下面代码 ServletContext context = this.getServletContext(); String value = context.getInitParameter("name"); response.getOutputStream().write(value.getBytes()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
ServletContext与ServletConfig区别:
ServletConfig每个Servlet对象对应一个,用来在Servlet的init方法进行初始化,传递Servlet私有初始化信息。
ServletContext 每个web应用对应一个,提供全局初始化参数,所有Servlet都可以访问。
2.案例二:通过文件扩展名 获得文件MIME类型
MIME类型:MIME协议中规定文件书写格式
tomcat/conf/web.xml 配置了文件扩展名和MIME类型对应关系
ServletContext对象 提供 API : String getMimeType(String file) 根据文件名获得MIME类型
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String file = "QQ.exe"; String mime = this.getServletContext().getMimeType(file); System.out.println(mime);//application/octet-stream }
3.案例三:获取日志信息
public void log(String msg):将特定信息写入servlet日志文件(通常是一个事件日志)。servlet日志文件的名称和类型是特定于servlet容器的。
public void log(String message,Throwable throwable):将给定异常的解释性信息和堆栈跟踪写入servlet日志文件。
注:MyEclipse情动tomcat,无法写入日志文件
public class ServletTest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = getServletContext(); context.log("记录普通信息"); try { int a = 5 / 0; } catch (Exception e) { context.log("记录异常信息", e); } }
案例四 : 统计Servlet的访问次数
作为数据域,保存整个web应用数据
void setAttribute(String name, Object object) 将一个数据保存ServletContext中
Object getAttribute(String name) 从ServletContext对象中取出数据
ServletContext因为全局唯一的,所以在多个Servlet之间可以共享数据
public class ServletTest extends HttpServlet { @Override // 初始化访问次数为0 public void init() throws ServletException { getServletContext().setAttribute("visittimes", 0); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;utf-8"); Integer times = (Integer) getServletContext() .getAttribute("visittimes"); // 加一并返回 getServletContext().setAttribute("visittimes", times + 1); response.getOutputStream().write( ("你是第" + (times + 1) + "个访问者").getBytes("utf-8")); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
案例五:统计字母出现次数
知识点:对请求分发。
常见面试题:比较转发和重定向
重定向:产生两次请求和两次响应。对于客户端来说,知道产生了重定向(URL地址会转变为B的地址);
转发:服务器内部技术,由服务器多个资源共同处理同一个请求和响应。转发的过程对于客户端是不可见的。
操作步骤:
RequestDispatcher getRequestDispatcher(String path):获得转发器,完成转发。
再使用Dispatcher的void forward(ServletRequest request,ServletRequest response):完成请求和响应转发操作。
1.写一个html页面输入字母
<form action="/day05/count" method="post" > <textarea rows="5" cols="30" name="content"></textarea> <input type="submit" value="提交"/> </form>
2.创建一个Servlet 虚拟目录为count 用于统计字母个数
public class Demo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得请求的内容 String content = request.getParameter("content"); // 索引代表字母 int[] arr = new int[26]; for (int i = 0; i < content.length(); i++) { char ch = content.charAt(i); if (Character.isLetter(ch)) { // 转换为小写 ch = Character.toLowerCase(ch); arr[ch - 'a']++; } } getServletContext().setAttribute("arr", arr); RequestDispatcher dispatcher = getServletContext() .getRequestDispatcher("/Demo2"); dispatcher.forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
3.创建一个Servlet,用于输出结果
public class Demo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得统计结果 int arr[] = (int[]) getServletContext().getAttribute("arr"); response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); for (int i = 0; i < arr.length; i++) { char ch = (char) ('a' + i); out.print(ch + "出现的次数是" + arr[i] + "</br>"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
案例六:读取文件
回顾Java读取文件
public class FileReaderTest { public static void main(String[] args) { // 读取项目目录文件 String path_1 = "1.txt"; reader(path_1); // 读取src下的文件 String path_2 = "src/2.txt"; reader(path_2); // 读取WebRoot下的文件 String path_3 = "WebRoot/5.txt"; reader(path_3); // 读取WEB-INF目录下的文件 String path_4 = "WebRoot/WEB-INF/4.txt"; reader(path_4); // 读取包下的文件 String path_5 = "src/cn/itcast/test/3.txt"; reader(path_5); } public static void reader(String path) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } } }
在Web中读取文件要使用绝对路径:
public class Demo3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 读取项目目录文件 因为项目下的文件没有发布 // 读取src下的文件 String path_2 = "/WEB-INF/classes/2.txt"; String real_path_2 = getServletContext().getRealPath(path_2); // 使用类加载器读取src下的文件 reader(real_path_2); String path = Demo3.class.getResource("/2.txt").getFile(); reader(path); // 读取WebRoot下的文件 String path_3 = "/5.txt"; String real_path_3 = getServletContext().getRealPath(path_3); reader(real_path_3); // 读取WEB-INF目录下的文件 String path_4 = "/WEB-INF/4.txt"; String real_path_4 = getServletContext().getRealPath(path_4); reader(real_path_4); // 读取包下的文件 String path_5 = "/WEB-INF/classes/cn/itcast/test/3.txt"; String real_path_5 = getServletContext().getRealPath(path_5); reader(real_path_5); } public static void reader(String path) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }