Servlet第二篇【Servlet调用图、Servlet细节、ServletConfig、ServletContext】

Servlet的调用图

前面咱们已经学过了Servlet的生命周期了,咱们根据Servlet的生命周期画出Servlet的调用图加深理解php

 

 

Servlet的细节

一个已经注册的Servlet能够被屡次映射

同一个Servlet能够被映射到多个URL上。web

<servlet>
	        <servlet-name>Demo1</servlet-name>
	        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
	    </servlet>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>/Demo1</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>/ouzicheng</url-pattern>
	    </servlet-mapping>

不管我访问的是http://localhost:8080/Demo1仍是http://localhost:8080/ouzicheng。我访问的都是Demo1。数据库

 

 

 

 

Servlet映射的URL可使用通配符

通配符有两种格式:apache

  1. *.扩展名
  2. 正斜杠(/)开头并以“/*”结尾。

匹配全部编程

 

 

匹配扩展名为.jsp的浏览器

 

 

若是*.扩展名和正斜杠(/)开头并以“/*”结尾两种通配符同时出现,匹配的是哪个呢?安全

  1. 看谁的匹配度高,谁就被选择
  2. *.扩展名的优先级最低

Servlet映射的URL可使用通配符和Servlet能够被映射到多个URL上的做用:服务器

  1. 隐藏网站是用什么编程语言写的【.php,.net,.asp实际上访问的都是同一个资源】
  2. 用特定的后缀声明版权【公司缩写】
<servlet>
	        <servlet-name>Demo1</servlet-name>
	        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
	    </servlet>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.jsp</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.net</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.asp</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.php</url-pattern>
	    </servlet-mapping>

Servlet是单例的

为何Servlet是单例的

浏览器屡次对Servlet的请求,通常状况下,服务器只建立一个Servlet对象,也就是说,Servlet对象一旦建立了,就会驻留在内存中,为后续的请求作服务,直到服务器关闭微信

每次访问请求对象和响应对象都是新的

对于每次访问请求,Servlet引擎都会建立一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,而后将这两个对象做为参数传递给它调用的Servlet的service()方法service方法再根据请求方式分别调用doXXX方法并发

线程安全问题

当多个用户访问Servlet的时候,服务器会为每一个用户建立一个线程当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题

原则:

  1. 若是一个变量须要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
  2. 若是一个变量不须要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题

load-on-startup

若是在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并建立Servlet的实例对象、以及调用Servlet实例对象的init()方法

 

 

 

 

做用:

  1. 为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用建立必要的数据库表和数据
  2. 完成一些定时的任务【定时写日志,定时备份数据】

在web访问任何资源都是在访问Servlet

当你启动Tomcat,你在网址上输入http://localhost:8080。为何会出现Tomcat小猫的页面?

这是由缺省Servlet为你服务的

  • 咱们先看一下web.xml文件中的配置,web.xml文件配置了一个缺省Servlet
<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>
  • 什么叫作缺省Servlet?凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理全部其余Servlet都不处理的访问请求
  • 既然我说了在web访问任何资源都是在访问Servlet,那么我访问静态资源【本地图片,本地HTML文件】也是在访问这个缺省Servlet【DefaultServlet】
  • 证明一下:当我没有手工配置缺省Servlet的时候,访问本地图片是能够访问获得的

 

 

  • 如今我本身配置一个缺省Servlet,Demo1就是我手工配置的缺省Servlet,覆盖掉web.xml配置的缺省Servlet
<servlet>
        <servlet-name>Demo1</servlet-name>
        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo1</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  • 下面我继续访问一下刚才的图片,此时输出的是Demo1这个Servlet写上的内容了

 

 

  • 总结:**不管在web中访问什么资源【包括JSP】,都是在访问Servlet。**没有手工配置缺省Servlet的时候,你访问静态图片,静态网页,缺省Servlet会在你web站点中寻找该图片或网页,若是有就返回给浏览器,没有就报404错误

ServletConfig对象

ServletConfig对象有什么用?

经过此对象能够读取web.xml中配置的初始化参数。

如今问题来了,为何咱们要把参数信息放到web.xml文件中呢?咱们能够直接在程序中均可以定义参数信息,搞到web.xml文件中又有什么好处呢

好处就是:可以让你的程序更加灵活【更换需求,更改配置文件web.xml便可,程序代码不用改】

获取web.xml文件配置的参数信息

  • 为Demo1这个Servlet配置一个参数,参数名是name,值是zhongfucheng
<servlet>
        <servlet-name>Demo1</servlet-name>
        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>zhongfucheng</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo1</servlet-name>
        <url-pattern>/Demo1</url-pattern>
    </servlet-mapping>
  • 在Servlet中获取ServletConfig对象,经过ServletConfig对象获取在web.xml文件配置的参数

ServletContext对象

什么是ServletContext对象?

当Tomcat启动的时候,就会建立一个ServletContext对象。它表明着当前web站点

ServletContext有什么用?

  1. ServletContext既然表明着当前web站点,那么全部Servlet都共享着一个ServletContext对象,因此Servlet之间能够经过ServletContext实现通信
  2. ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext能够获取的是配置整个web站点的参数信息
  3. 利用ServletContext读取web站点的资源文件
  4. 实现Servlet的转发【用ServletContext转发很少,主要用request转发】

Servlet之间实现通信

ServletContext对象能够被称之为域对象

到这里可能有一个疑问,域对象是什么呢?其实域对象能够简单理解成一个容器【相似于Map集合】

实现Servlet之间通信就要用到ServletContext的setAttribute(String name,Object obj)方法, 第一个参数是关键字,第二个参数是你要存储的对象

  • 这是Demo2的代码
//获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        String value = "zhongfucheng";

        //MyName做为关键字,value做为值存进   域对象【类型于Map集合】
        servletContext.setAttribute("MyName", value);
  • 这是Demo3的代码
//获取ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //经过关键字获取存储在域对象的值
        String value = (String) servletContext.getAttribute("MyName");

        System.out.println(value);
  • 访问Demo3能够获取Demo2存储的信息,从而实现多个Servlet之间通信

 

 

获取web站点配置的信息

若是我想要让全部的Servlet都可以获取到链接数据库的信息,不可能在web.xml文件中每一个Servlet中都配置一下,这样代码量太大了!而且会显得很是啰嗦冗余。

  • web.xml文件支持对整个站点进行配置参数信息全部Servlet均可以取到该参数信息
<context-param>
        <param-name>name</param-name>
        <param-value>zhongfucheng</param-value>
    </context-param>
  • Demo4代码
//获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //经过名称获取值
        String value = servletContext.getInitParameter("name");
        System.out.println(value);

 

 

  • 试一下Demo3是否能拿到,相同的代码
//获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //经过名称获取值
        String value = servletContext.getInitParameter("name");
        System.out.println(value);

 

 

读取资源文件

第一种方式:

  • 如今我要经过Servlet111读取1.png图片

 

 

  • 按咱们之前的方式,代码应该是这样的。
FileInputStream fileInputStream = new FileInputStream("1.png");
        System.out.println(fileInputStream);
  • 当咱们访问的时候,却出错了!说找不到1.png文件

 

 

  • 这是为何呢?咱们之前读取文件的时候,若是程序和文件在同一包名,能够直接经过文件名称获取获得的!,缘由很简单,之前咱们写的程序都是经过JVM来运行的,而如今,咱们是经过Tomcat来运行的
  • 根据web的目录规范,Servlet编译后的class文件是存放在WEB-INF\classes文件夹中的

 

 

  • 看到这里,咱们知道了要进入classes目录中读取文件,因此咱们将代码改为如下方式
FileInputStream fileInputStream = new FileInputStream("D:\\zhongfucheng\\web\\WEB-INF\\classes\\zhongfucheng\\web\\1.png");
        System.out.println(fileInputStream);
  • 再去读取时,就发现能够获取到文件了。
  • 可是如今问题又来了,我读取文件的时候都要写上绝对路径,这样太不灵活了。试想一下,若是我将该读取文件的模块移到其余的web站点上个人代码就又要修改了【由于web站点的名字不同】
  • 咱们经过ServletContext读取就能够避免修改代码的状况,由于ServletContext对象是根据当前web站点而生成的
  • 代码以下所示:
//获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //调用ServletContext方法获取到读取文件的流
        InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/classes/zhongfucheng/web/1.png");

 

 

第二种方式:

  • 若是个人文件放在web目录下,那么就简单得多了!,直接经过文件名称就能获取

 

 

  • 代码以下所示
//获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //调用ServletContext方法获取到读取文件的流
        InputStream inputStream = servletContext.getResourceAsStream("2.png");

 

 

第三种方式:

经过类装载器读取资源文件

  • 个人文件放在了src目录下【也叫作类目录】

 

 

  • 代码以下所示
//获取到类装载器,2018年2月5日15:11:58 根据@兰兰美如画评论,发现Thread.currentThread().getContextClassLoader()这种方式会更好。
        //ClassLoader classLoader = Servlet111.class.getClassLoader();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
         

        //经过类装载器获取到读取文件流
        InputStream inputStream = classLoader.getResourceAsStream("3.png");

 

 

  • 个人文件放在了src目录下的包下

 

 

  • 代码以下,添加包名路径便可。
//获取到类装载器
        ClassLoader classLoader = Servlet111.class.getClassLoader();

        //经过类装载器获取到读取文件流
        InputStream inputStream = classLoader.getResourceAsStream("/zhongfucheng/web/1.png");

原则:若是文件太大,就不能用类装载器的方式去读取,会致使内存溢出

若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章的同窗,能够关注微信公众号:Java3y

相关文章
相关标签/搜索