小白学Java Web 13 探秘Servlet

     这一篇来分享一下有关servlet的那些事,我们之前也建立过servlet文件,现在我们对其进行稍稍一个深入的了解。

     我们还是先建立一个web项目并建立一个servlet文件吧。

     那我们就再来重温一下最初的Hello系列吧,O(∩_∩)O哈哈~

public class HelloServlet extends HttpServlet {

	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.getWriter().write("Hello");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doGet(request, response);
	}

}
      我们打开一下web.xml文件,还记得位置嘛,在WebRoot中WEB-INF文件夹下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name></display-name>
  
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.sqq.a_servlet.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/HelloServlet</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

一.如何开发一个servlet

    这个很基础而且比较重要,我们来分析一下servlet的执行步骤:

    首先我们是在浏览器中访问我们建立的这个servlet,http://localhost:8080/Day05/HelloServlet

   步骤:1.在Day05项目的web.xml文件中查找<url-pattern>中是否存在HelloServlet

                   这里我们将默认的“/servlet/HelloServlet”改为“/HelloServlet”就是为了这里查找访问方便。

               2.如果查找到匹配的内容,使用当前<servlet-mapping>标签中的<servlet-name>名称去web.xml文件中查找是否存在相同的名称。

               3.如果查找到相同的名称 则取出相对应的<servlet-class>中的内容。

               4.执行<servlet-class>中的servlet  根据请求方式不同调用该servlet中的doGet()和doPost()方法

     注意这里的一切都是在建立该servlet文件时系统自动为我们配置的,但我们应该知道它们存在的作用,之后也会出现一些东西需要我们手动配置。

二.servlet的映射路径

            <servlet-mapping>
            <servlet-name>HelloServlet</servlet-name>
            <url-pattern>/HelloServlet</url-pattern>
            </servlet-mapping>

     servlet的映射路径指的就是<url-pattern></url-pattern>标签中所填写的东西。

                                            url-pattern                              浏览器输入地址

        精确匹配                  /HelloServlet                   http://localhost:8080/Day05/HelloServlet

                                          /servlet/HelloServlet      http://localhost:8080/Day05/servlet/HelloServlet

       模糊匹配                   /*                                        http://localhost:8080/Day05/任意路径

                                         /servlet/*                            http://localhost:8080/Day05/servlet/任意路径

                                          *.后缀名

                                          *.action                             http://localhost:8080/Day05/任意路径.action

       看了上面这些,是不是对我最初所说的理解了?这里添加什么我们在浏览器访问时也要相应写上什么,所以之前所说的修改这里就是便于查找访问。

       不过这里需要注意三点:

             1.url-pattern内容要么以/开头 要么以*开头

             2.不能同时使用两种模糊匹配  ,例如/servlet/*.do这样是非法的,不可使用

             3.当输入的url地址有多个servlet同时匹配

                   精确匹配优先(长得最像的最先匹配) ,以后缀名的模糊匹配优先级最低

三.Servlet缺省路径

      servlet的缺省路径(<url-pattern>/</url-pattern>)是在tomcat中内置的一个路径,该路径对应的是一个DefaultServlet(缺省servlet),这个缺省的servlet主要的作用就是用于解析

web项目中的静态资源文件。

      那么我们想一下,输入网址:http://localhost:8080/Day05/01test.html 访问的并不是servlet文件,这个时候如何查找到文件的?

      步骤:1.当Day05web项目中的web.xml文件中查找是否存在匹配的url-pattern 01test.html

                  2.如果没有匹配url-pattern,则交给tomcat的内置的defaultServlet处理

                  3.defaultServlet会在Day05的webRoot下查找是否存在一个静态资源文件叫01test.html

                  4.如果能够查找到文件,则读取文件显示信息

                  5.查不到咋办?那就抛出咱们最不愿意看到的404异常

          在Tomcat/conf/web.xml中可以找到这段代码:
          ...
          <servlet>
          <servlet-name>default</servlet-name>
          <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
          < /servlet>
          <servlet-mapping>
          <servlet-name>default</servlet-name>
          <url-pattern>/</url-pattern>
           </servlet-mapping>
          ...

          在这一小节上我们能得出什么结论呢?在执行此类程序时,先查找的是动态资源,之后再查找html等静态资源。

四.servlet的生命周期

      这也是本篇需要重要理解的内容。

      Servlet的生命周期也是由Tomcat控制的,生命周期包括servlet类对象什么时候创建,工作时调用的什么方法,什么时候销毁的等。

      浏览器客户端发送请求时 tomcat服务器会首先调用service()方法处理请求, 在service方法的底层中是根据当前的请求方式分别调用doGet()和doPost()方法。

      就像下面酱个样子:

public class DemoServlet extends HttpServlet {
	/**
	 * service生命周期方法   
	 */
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//直接编写处理请求的代码
	}
	//当请求为get请求时
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
	//当请求为post请求时
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doPost(req, resp);
	}
	
	
}
       因为最常见的请求就是get和post, 所以我们在编写servlet时,最常见的形式就是直接覆盖doget()和dopost()方法。

      servlet的生命周期对应着相应地方法:

            构造方法:创建servlet对象时调用。默认情况下,第一次访问servlet时创建servlet对象,从头到尾只调用一次。这说明了servlet是单实例。

            init():        创建完servlet对象时调用。 只调用一次

           service(): 每次发出请求的时候调用。 调用N次

           destory(): 销毁servlet对象时调用。 停止服务或者重新部署Tomcat时web应用都会销毁Servlet,这个也只调用一次

     生命周期这里需要掌握两点:

            1.掌握生命周期对应的函数 调用时机 次数
            2.掌握生命周期函数的调用过程

           我们可以通过这个程序来看看生命周期方法都是在什么时候调用的

public class LifeServlet extends HttpServlet {
	/**
	 * 构造函数
	 */
	public LifeServlet(){
		System.out.println("1.servlet对象被创建了!");
	}
	/**
	 * init初始化
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("2.init方法被调用");
	}
	/**
	 * service
	 */
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("3.Service方法被调用");
	}
	/**
	 * destroy
	 */
	@Override
	public void destroy() {
		System.out.println("4.destroy方法被调用");
	}
}

          首先是这俩兄弟,创建时就有了,启动Tomcat后,被包裹在一大堆乱七八糟的东西里面。

          

         这位仁兄就不那么悲催了,浏览器访问一次就调用一次,咔咔的会分身。

         销毁或者重新布置一下web工程时,最后这位终于出来了

         

        其实每当我们接触一些新内容时,我们都要设法去编个代码试验试验,这样更便于我们理解这些知识点。

        真懒得贴图,变乱了,看起来瞬间整个文章就变low了,呵呵。

五.    Servlet的自动加载    

       默认情况下,第一次访问servlet的时候创建servlet的对象。

       如果servlet构造函数或是init()初始化方法中执行了较多的逻辑代码,那么就会导致servlet的加载速度比较慢,这样会直接影响用户体验。

         我们可以改变servlet对象创建的时机:提前到加载web应用的时候

       怎么做呢,我们在servlet配置信息中,也就是web.xml文件中, 添加<load-on-startup>即可

       就像酱个样子:

<servlet>
    <servlet-name>LifeServlet</servlet-name>
    <servlet-class>com.yl.life.LifeServlet</servlet-class>
    <!-- 让servlet对象自动加载  注意:数值越大 创建的优先级越低-->
    <load-on-startup>1</load-on-startup>
  </servlet>
六.有参数init()和无参数init()的区别?

        我们再来关注一下Init()方法,有参数init()和无参数的init()是有区别的:

        带有参数的init()方法是servlet的生命周期方法,是 一定会被Tomcat调用的。

        无参数的init()方法sun公司的开发人员编写的专门用于存放初始化代码的地方,主要是需要我们覆盖编写初始化代码的方法。

七.ServletConfig对象

       1.作用:主要用于加载servlet初始化配置参数。

            在一个web项目中可以有多个ServletConfig对象,但一个servlet只对应一个ServletConfig对象,servlet配置的参数只能由当前的servlet获取!

       2.    ServletConfig对象的创建和获得

            这个东东我们要在创建完servlet对象之后,在调用init()方法之前创建

            直接从带参数的init()方法中就可以获得该对象。

      3 .servlet初始化参数的配置

  <servlet>
           <servlet-name>ConfigDemo</servlet-name>
           <servlet-class>com.yl.config.ConfigDemo</servlet-class>
           <!-- 配置初始化参数:init-param标签配置的参数会加载到web应用中并且封装到ServletConfig对象中 -->
    <init-param>
    	<param-name>path</param-name>
    	<param-value>d:/test.txt</param-value>
    </init-param>
  </servlet>
         我们就是要在 <init-param>标签中设置name和value。

      4.ServletConfig的API

           this.getServletConfig().getInitParameter(name)                                                             根据参数名称获取参数值

           Enumeration<String> it=this.getServletConfig().getInitParameterNames();             获取servletConfig对象中所有的配置参数信息 

           this.getServletConfig().getServletName()                                                                        获取配置时servlet的名称

       每次遇到新的API的时候我们都应该写个小程序玩一玩,这样有助于消化。

       

public class ConfigDemo extends HttpServlet {
	/**
	 * 1)Tomcat服务器会将web.xml文件中init-param标签中的内容封装到ServletConfig
	 * 2)Tomcat服务器调用init()方法是将ServletConfig对象传入
	 */

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		/**
		 * 读取servletConfig中的配置参数
		 */
		String path=this.getServletConfig().getInitParameter("path");
		File file=new File(path);
		BufferedReader br=new BufferedReader(new FileReader(file));
		String line=null;
		while((line=br.readLine())!=null){
			System.out.println(line);
		}
		System.out.println("------------------------");
		//获取servlet中所有的配置参数
		Enumeration<String> it=this.getServletConfig().getInitParameterNames();
		while(it.hasMoreElements()){
			String paramName=it.nextElement();
			String paramValue=this.getServletConfig().getInitParameter(paramName);
			System.out.println(paramName+"="+paramValue);
		}
		//获取servlet的名称
		String serlvetName=this.getServletConfig().getServletName();
		System.out.println(serlvetName);
	}

}
              我们在xml文件中的配置:

<servlet>
    <servlet-name>ConfigDemo</servlet-name>
    <servlet-class>com.yl.config.ConfigDemo</servlet-class>
    <init-param>
      <param-name>path</param-name>
      <param-value>d:/test.txt</param-value>
    </init-param>
    <init-param>
      <param-name>name</param-name>
      <param-value>yl</param-value>
    </init-param>
  </servlet>
         在浏览器中访问这个servlet文件,我们可以在控制台中看到相应结果:

         

           首先打印出的是path对应的d:/test.txt中的内容,这个txt文件是随便找的一个啦,哈哈。

           下面两行是通过Enumeration得到的所有name和value,打印出来。

           最后一行是获取到的servlet的名称。

           这下明白上面那三行API的作用了吧,多直观。

八.ServletContext对象

        又来一个对象,话说回来,宝宝还木有对象呢··········不对,跑题了
     1.作用
         ServletContext对象,称为Servlet上下文对象,表示一个当前的web应用环境。
          注意一个web应用只有一个ServletContext对象。
     2.对象的创建和获得
         我们在加载web应用时创建servletContext对象,从ServletConfig对象中调用getServletContext()可以获得到该对象。
     3.ServletContext核心的API

         前方高能,大量API来袭,看官们注意啦

         java.lang.String getContextPath()  得到当前web应用路径

         java.lang.String getInitParameter(java.lang.String name)
         java.util.Enumeration getInitParameterNames()  获取web应用的初始化配置参数

         void setAttribute(java.lang.String name,java.lang.Object object)
         void removeAttribute(java.lang.String name)
         java.lang.Object getAttribute(java.lang.String name) 域对象相关的方法

        RequestDispatcher getRequestDispatcher(java.lang.String path) 转发

        java.lang.String getRealPath(java.lang.String path) 获取web应用资源文件
        java.io.InputStream getResourceAsStream(java.lang.String path)

       这都什么乱七八糟的啊,下面我们来逐一解释这些API的作用。

    4.得到web应用路径

         java.lang.String getContextPath()                在页面跳转时通过该方法动态的获取web应用的路径

/**
 * 得到web应用路径
 */
public class ContextDemo1 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//1.得到ServletContext对象
//		ServletContext context=this.getServletConfig().getServletContext();
		ServletContext context=this.getServletContext();//推荐使用
		
		//2.获得web应用路径
		String contextPath=context.getContextPath();
		System.out.println(contextPath);
		//让页面跳转一下
		response.sendRedirect(context.getContextPath()+"/01test.html");
	}

}

        打印出来的结果自然是:


       哎呀,不要在意细节,这其实就是我的项目名······看官们还是自己运行程序体验吧

    5.获取web应用的初始化配置参数(全局配置)

       java.lang.String getInitParameter(java.lang.String name)
       java.util.Enumeration getInitParameterNames() 

       首先我们先在web应用的web.xml文件中针对整个web应用进行初始化配置:

<!-- 针对整个web应用的初始化配置   -->
  <context-param>
  	 <param-name>version</param-name>
  	 <param-value>1.1</param-value>
  </context-param>
  <context-param>
  	 <param-name>code</param-name>
  	 <param-value>xxxxxx</param-value>
  </context-param>
     注意:web应用的全局配置参数可以让当前的web中所有的servlet都可以访问

     然后再创建一个servlet文件:

/**
 * 获取web应用的全局配置参数
 */
public class ContextDemo2 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//得到servletContext对象
		ServletContext context=this.getServletContext();
		String value=context.getInitParameter("version");
		System.out.println(value);
		
		System.out.println("--------------");
		
		Enumeration<String> enums=context.getInitParameterNames();
		while(enums.hasMoreElements()){
			String paramName=enums.nextElement();
			String paramValue=context.getInitParameter(paramName);
			System.out.println(paramName+"="+paramValue);
		}
	}

}

     然后在浏览器访问这个servlet,在控制台中能看到这些配置信息:   

    6.域对象的相关方法

      void setAttribute(java.lang.String name,java.lang.Object object)
      void removeAttribute(java.lang.String name)
      java.lang.Object getAttribute(java.lang.String name) 域对象相关的方法

      首先我们需要知道什么是域对象,域对象用于保存数据,获取数据,目的是在不同的动态资源之间可以传递(共享)数据。

      至于具体怎么用,我们这里来看一个案例:

      案例:存在两个servlet需要进行传递数据

           这里也许我们会有一个思路,我们可以用之前的方法(也就是上两篇文章)来实现传递数据这个功能啊

            还记得这样的代码吗?

          response.sendRedirect("http://localhost:8080/Day05_web/ContextDemo4?name=Frank");
          String name=request.getParameter("name");

          也就是通过传递请求参数的形式共享数据,这样就能获取到name了啊,不过这样做有个局限,这样只能传递字符串数据。

          我们用这一小节的内容来实现这个功能,首先我们需要明确的是:ServletContext就是一个域对象!而且作用范围是整个web应用程序。

          上面三行API的作用是:

           保存数据void setAttribute(java.lang.String name,java.lang.Object object)
           获取数据 java.lang.Object getAttribute(java.lang.String name)
           删除数据 void removeAttribute(java.lang.String name)

         接下来我们就来实现这个案例的功能:

          首先我们先来创建一个Student类文件,并获得对应的方法。

public class Student {
	private int id;
	private String name;
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	
	
}	
       建这玩意干什么?我们通过这个来说明这样做并不只局限于传递字符串!

       接下来我们就来创建两个互相传递的servlet:

/**
 * 保存数据 跳转到4并且传递数据
 */
public class ContextDemo3 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//1.得到域对象
		ServletContext context=this.getServletContext();
		//2.把数据存储到域对象中
		context.setAttribute("name", "Frank");
		context.setAttribute("student", new Student(1, "张三", 18));
		System.out.println("保存成功!");
	}

}
       这里new了一个Student,知道是为了什么吧。

/**
 * 获取3传递过来的数据
 */
public class ContextDemo4 extends HttpServlet {
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//1.得到域对象
		ServletContext context=this.getServletContext();
		//2.根据key获取相应的值
		String name=(String) context.getAttribute("name");
		Student stu=(Student) context.getAttribute("student");
		System.out.println(name+"---"+stu.getName()+","+stu.getAge());
	}

}
       首先呢,我们先用浏览器来访问第一个servlet,存储后会在控制台显示“保存成功!”。

       接着访问第二个servlet文件,我们会获得第一个servlet中存储的值。

       

      好吧,完美!那么域对象都有哪些呢?

      其实servletContext ,HttpServletRequest,httpSession ,pageContext这些都是域对象。

   7.转发

        RequestDispatcher getRequestDispatcher(java.lang.String path)

        一说到转发就能想到重定向,小小比较一下:

          转发:
                a)地址栏不会发生改变
                b)转发只能转发到当前web的应用资源
                c)转发过程中,可以将数据存储到request域对象
          重定向:
                a)地址栏会发生改变 变成重定向的地址
                b)重定向可以跳转到当前web应用 也可以跳转到其他外部应用甚至可以跳转到外部域名
                c)重定向中不能将数据存储到request域对象中

        所以说嘞,如果要使用reques域对象实现数据共享,只能用转发技术!

        上代码咯:

/**
 * 转发
 */
public class ForwardDemo extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//转发 注意:不能转发到本应用之外的资源

		request.setAttribute("name", "rose");
		this.getServletContext().getRequestDispatcher("/GetRequestDemo").forward(request, response);
	}

}
public class RedirectDemo extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//重定向

		/*
		 * 注意:重定向不光可以跳转到web工程中的资源  也可以访问其它的应用
		 * 甚至可以访问外部域名
		 */
		//response.sendRedirect("/Day04/adv.html");
		//response.sendRedirect("http://www.baidu.com");
		
		//保存数据到request域对象中
		request.setAttribute("name", "rose");
		response.sendRedirect(this.getServletContext().getContextPath()+"/GetRequestDemo");
	}

}
         这俩servlet呢,一个转发,一个重定向,都是要跳转到下面这个servlet:

/**
 * 从request域对象中获取数据
 */
public class GetRequestDemo extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String name=(String) request.getAttribute("name");
		System.out.println(name);
	}

}
        我们要用浏览器访问这两个servlet,它们会自动跳转并将获取的值打印在控制台上:

  

      通过转发获取到了值,通过重定向没有获取到值,原因在上面已经提到过。


     这篇文章就分享这么多,这里的理论知识点貌似多了点,不过对于基础的代码的编写还是要多多练习才好,总之多多理解多多练习,虽然不实用,但基础并不是没有用的。

下一篇来分享会话技术。

猜你喜欢

转载自blog.csdn.net/yl416306434/article/details/65448820