一、Servlet概述
1.sun公司提供的动态web资源开发技术。本质是上一段java小程序,要求这个小程序必须实现Servlet接口,以便服务器能够调用。
sun提供的一种动态web资源开发技术.本质上就是一段java小程序.可以将Servlet加入到Servlet容器中运行.
*Servlet容器 -- 能够运行Servlet的环境就叫做Servlet容器. --- tomcat
*web容器 -- 能够运行web应用的环境就叫做web容器 --- tomcat
2.开发Servlet的两个步骤:
(1)写一个类实现sun公司定义的Servlet接口;
(2)将写好的类配置到tomcat中的web应用的web.xml中,(配置对外访问路径)。
*实验:Servlet的快速入门
(1)步骤一:写一个java程序实现Servlet接口(此处直接继承了默认实现类GenericServlet)。
(带包编译)
package com.lmd;
import java.io.*;
import javax.servlet.*;
public class FirstServlet extends GenericServlet {
//此方法已声明为抽象方法,因此子类(比如 HttpServlet)必须重写它。
//req 包含客户端请求的 ServletRequest 对象
//res 将包含 servlet 的响应的 ServletResponse 对象
//Throws ServletException: 如果发生妨碍 servlet 正常操作的异常
//Throws java.io.IOException: 如果发生输入或输出异常
public void service(ServletRequest req, ServletResponse res)
throws ServletException, java.io.IOException {
res.getWriter().write("Hello servlet!");
}
}
set classpath=xclasspathx;F: omcat8libservlet-api.jar
javac -d . FirstServlet.java
出现F:webexamplecomlmd下的FirstServlet.class文件。
(2)步骤二:将编译好的带包的.class放到WEB-INF/classes下以外,还要配置web应用的 web.xml注册Servlet。(F: omcat8webapps
ewsWEB-INFclassescomlmdFirstServlet.class)
F: omcat8webapps
ewsWEB-INF下的web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.lmd.FirstServlet</servlet-class>
</servlet>
<!-- 配置对外访问路径 -->
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/servlet/FirstServlet</url-pattern>
</servlet-mapping>
</web-app>
3.利用MyEclipse开发Servlet
package com.lmd;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MyFirstServlet
*/
@WebServlet("/MyFirstServlet")
public class MyFirstServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
response.getWriter().write("now time:" + new Date().toLocaleString());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
其中疑问之处:HttpServletResponse接口(无法实例化)直接调用方法?
虽然是接口,但是要知道这个service()方法是服务器调用的,所以response等参数是由服务器传进来的。虽然使用接口接的,但是服务器调用这个方法传的是这个接口的实现类的一个对象,是使用父类接子类。
****多态的思想,父父new子****
****多态的思想,父父new子****
********Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:********
(1)Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
(2)装载并创建该Servlet的一个实例对象。
(3)调用Servlet实例对象的init()方法。
(4)创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
(5)WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
二、Servlet的详述
1. 生命周期:一件事物什么时候生,什么时候死,在生存期间必然会做的事情,所有这些放在一起就是该事物的声明周期。
2. Servlet的生命周期:通常情况下,servlet第一次被访问的时候在内存中创建对象,在创建后立即调用init()方法进行初始化。对于每一次请求都调用service(req,resp)方法处理请求,此时会用Request对象封装请求信息,并用Response对象(最初是空的)代表响应消息,传入到service方法里供使用。当service方法处理完成后,返回服务器服务器根据Response中的信息组织称响应消息返回给浏览器。响应结束后servlet并不销毁,一直驻留在内存中等待下一次请求。直到服务器关闭或web应用被移除出虚拟主机,servlet对象销毁并在销毁前调用destroy()方法做一些善后的事情。
********Servlet的生命周期:********
(1)通常情况下,服务器会在Servlet第一次被调用时创建该Servlet类的实例对象(servlet出生);一旦被创建出来,该Servlet实例就会驻留在内存中,为后续请求服务;直至web容器退出,servlet实例对象才会被销毁(servlet死亡)。
(2)在Servlet的整个生命周期内,Servlet的init方法只有在servlet被创建时被调用一次。
而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
(3)servlet被销毁前,会调用destroy() 方法。
上述方法都是服务器调用的。
3. Servlet接口的继承结构
Servlet接口:定义了一个servlet应该具有的方法,所有的Servlet都应该直接或间接实现此接口
|
|----GenericServlet:对Servlet接口的默认实现,通用Servlet,这是一个抽象类,其中的大部分方法都做了默认实现,只有service方法是一个抽象方法需要继承者自己实现
|
|----HttpServlet:在通用Servlet的基础上,基于HTTP协议进行了进一步优化。HttpServlet:对HTTP协议进行了优化的Servlet,继承自GenericServlet类,并且实现了其中的service抽象方法,默认的实现中判断了请求的请求方式,并根据请求方式的不同分别调用不同的doXXX()方法。通常我们直接继承HttpServlet即可
========myeclipse 2016:修改servlet模板文件 servlet.java====================
|| D:Program FilesMyEclipse2016plugins ||
|| com.genuitec.eclipse.wizards_13.0.0.me201606220647.jar emplatesservlet.java ||
===================================================================
4.web.xml注册Servlet的注意事项
4.1利用<servlet><servlet-mapping>标签注册一个Servlet
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.itheima.FirstServlet</servlet-class> 注意:此处要的是一个Servlet的完整类名,不是包含.java或.class扩展的文件路径
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>
4.2一个<servlet>可以对应多个<servlet-mapping>
4.3可以用*匹配符配置<serlvet-mapping>,但是要注意,必须是*.do或者/开头的以/*结束的路径。
~由于匹配符的引入有可能一个虚拟路径会对应多个servlet-mapping,此时哪个最像找哪个servlet,并且*.do级别最低。
4.4可以为<servlet>配置<load-on-startup>子标签,指定servlet随着服务器的启动而加载,其中配置的数值指定启动的顺序
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
~在conf/web.xml中配置了缺省servlet,对静态资源的访问和错误页面的输出就是由这个缺省servlet来处理的。如果我们自己写一个缺省servlet把爸爸web.xml中的缺省servlet覆盖的话,会导致静态web资源无法访问。所以不推荐配置。
4.6servlet的线程安全问题
4.6.1由于通常情况下,一个servlet在内存只有一个实例处理请求,当多个请求发送过来的时候就会有多个线程操作该servlet对象,此时可能导致线程安全问题。
(1)serlvet的成员变量可能存在线程安全问题
*实验:定义一个成员变量 int i = 0;在doXXX()方法中进行i++操作并输出i值到客户端,此时由于延迟可能导致线程安全问题
(2)serlvet操作资源文件时,多个线程操作同一文件引发线程安全问题
*实验:请求带着一个参数过来,servlet将请求参数写入到一个文件,再读取该文件,将读取到的值打印到客户端上,有可能有线程安全问题
4.6.2解决方法
(1)利用同步代码块解决问题。缺陷是,同一时间同步代码块只能处理一个 请求,效率很低下,所以同步代码块中尽量只包含核心的导致线程安全问题的代码。
(2)为该servlet实现SingleThreadModel接口,此为一个标记接口,被标记的servlet将会在内存中保存一个servlet池,如果一个线程来了而池中没有servlet对象处理,则创建一个新的。如果池中有空闲的servlet则直接使用。这并不能真的解决线程安全问题。此接口已经被废弃。
(3)两种解决方案都不够完美,所以尽量不要在servlet中出现成员变量。
5.Servlet的一些细节问题:
5.1 == 由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
== <servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
== 一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
5.2 == 同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
== 在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:
一种格式是“ *.扩展名 ” ; 另一种格式是以正斜杠(/)开头并以“ /* ”结尾。
*代表任何内容。
<servlet-mapping>
<servlet-name> AnyName </servlet-name>
<url-pattern> *.do </url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name> AnyName </servlet-name>
<url-pattern> /action/* </url-pattern>
</servlet-mapping>
对于如下的一些映射关系:
Servlet1 映射到 /abc/* Servlet2 映射到 /*
Servlet3 映射到 /abc Servlet4 映射到 *.do
问题:当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应
Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
总结:优先匹配最精确的,其中 *.后缀 的匹配度永远最低。
5.3 == (1)Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
5.4 == 如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
== (2)针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
== (3)在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
举例:
<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>
com.lmd.MyFirstServlet</servlet-class><load-on-startup>2</load-on-startup>
</servlet>
== 用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
5.5 == (1)如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
== (2)凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
== (3)在<tomcat的安装目录>confweb.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。
当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。
5.5 == 线程安全问题
== (1) 当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
== (2) 如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
== (3) SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
== (4) 对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
== (5) 实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(1)一个<servlet>可以对应多个<serlvet-mapping>,从而一个Servlet可以有多个路径来访问
(2)url-partten中的路径可以使用*匹配符号进行配置,但是要注意,只能是/开头/*结尾或*.后缀这两种方式
~由于*的引入,有可能一个路径被多个urlpartten匹配,这是优先级判断条件如下:
哪个最像找哪个
*.后缀永远匹配级最低
(3)<serlvet>可以配置<load-on-startup>可以用来指定启动顺序
(4)缺省Servlet:如果有一个Servlet的url-partten被配置为了一根正斜杠,这个Servlet就变成了缺省Serlvet.其他Servlet 都不处理的请求,由缺省Servlet来处理.
其实对于静态资源的访问就是由缺省Servlet来执行
设置404页面500页面等提示页面也是由缺省Servlet来执行
通常我们不会自己去配置缺省Servlet
(5)线程安全问题
由于默认情况下Servlet在内存中只有一个对象,当多个浏览器并发访问Servlet时就有可能产生线程安全问题
解决方案:
加锁--效率降低
SingleThreadModel接口 -- 不能真的防止线程安全问题
最终解决方案:在Servlet中尽量少用类变量(多用局部变量),如果一定要用类变量则用锁来防止线程安全问题,但是要注意锁住内容应该是造成线程安全问题的核心代码,尽量的少锁主内容,减少等待时间提高servlet的响应速度。
三、ServletConfig接口 : 代表当前Servlet在web.xml中的配置信息,有以下方法:
String getServletName() -- 获取当前Servlet在web.xml中配置的名字
String getInitParameter(String name) -- 获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames() -- 获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext() -- 获取代表当前web应用的ServletContext对象
====Servlet接口中: public void init(ServletConfig config) throws ServletException
由 servlet 容器调用,指示将该 servlet 放入服务。
1.代表servlet配置的对象,可以在web.xml中<servlet>中配置
<servlet>
<servlet-name>Demo5Servlet</servlet-name>
<servlet-class>cn.itheima.Demo5Servlet</servlet-class>
<init-param>
<param-name>data1</param-name>
<param-value>value1</param-value>
</init-param>
</servlet>
然后在servlet中利用this.getServletConfig()获取ServletConfig对象,该对象提供了getInitParameter()和getInitParameterNames()方法,可以遍历出配置中的配置项。
不想在servlet中写死的内容可以配置到此处。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig config = this.getServletConfig();
//获取当前Servlet在web.xml中配置的名称,不常用
String sName = config.getServletName();
System.out.println(sName);
// 获取当前Servlet指定名称的初始化参数的值
Enumeration enumeration = config.getInitParameterNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String valueString = config.getInitParameter(name);
System.out.println(name + ":" + valueString);
}
//获取当前Servlet所有初始化参数的名字组成的枚举
//获取代表当前web应用的ServletContext对象
}
四、******ServletContext******:代表当前web应用的对象。
(1)WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
(2)ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。
(3)由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
1、重要重要!!!
(1).代表当前web应用的对象。
(2).重要作用:作为域对象使用,可以在整个web应用范围中共享数据。
域对象——在一个可以被看见的范围内共享数据用到对象。
作用范围:整个web应用范围内共享数据
生命周期:当服务器启动web应用加载后创建出ServletContext对象后,域产生。当web应用被移除出容器或服务器关闭,随着web应用的销毁域销毁。
void setAttribute(String,Object);
Object getAttribute(String);
void removeAttribute(String);
在不同servlet之间传递数据,作用范围是整个web应用
生命周期:当web应用被加载进容器时创建代表整个web应用的ServletContext对象。当服务器关闭或web应用被移除出容器时,ServletContext对象跟着销毁。
~域:一个域就理解为一个框,这里面可以放置数据,一个域既然称作域,他就有一个可以被看见的范围,这个范围内都可以对这个域中的数据进行操作,那这样的对象就叫做域对象。
2、用来获取web应用的初始化参数
(1)请求参数 parameter --- 浏览器发送过来的请求中的参数信息
(2)初始化参数 initparameter --- 在web.xml中为Servlet或ServletContext配置的初始化时带有的基本参数
(3)域属性 attribute --- 四大作用域中存取的键值对(此处讲了一个)
在web.xml可以配置整个web应用的初始化参数,利用ServletContext去获得
<context-param>
<param-name>param1</param-name>
<param-value>pvalue1</param-value>
</context-param>
(ServletContext context = this.getServletContext();)
this.getServletContext().getInitParameter("param1"); this.getServletContext().getInitParameterNames();
3、实现Servlet的转发
请求重定向 : 302+Location ==请求重定向两次请求两次响应
请求转发 : 服务器内不进行资源流转 ==请求转发是一次请求一次响应实现资源流转;.
==在不同servlet之间进行转发==
4、加载资源文件:在Servlet中读取资源文件时:
this.getServletContext().getRequestDispatcher("/servlet/Demo10Servlet").forward(request, response);
方法执行结束,service就会返回到服务器,再有服务器去调用目标servlet,其中request会重新创建,并将之前的request的数据拷贝进去。
/**
* ServletContext请求转发
*/
@WebServlet("/TransmitServlet")
public class TransmitServlet extends HttpServlet {
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 转发器,包装位于特定路径上的服务器资源或通过特定名称给定的服务器资源的包装器。
RequestDispatcher dispatcher = this.getServletContext().getRequestDispatcher("/TransmitServletDemo");
//将请求从一个 servlet 转发到服务器上的另一个资源(servlet、JSP 文件或 HTML 文件)
dispatcher.forward(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
/**
* ServletContext请求转发
*/
@WebServlet("/TransmitServletDemo")
public class TransmitServletDemo extends HttpServlet {
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("$1000000");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
地址栏的URL地址不会改变
*****************************************************************************************************************
*1.请求转发 * 请求转发是指将请求再转发到另一资源(一般为JSP或Servlet)。此过程依然在**同一个请求范围内,转发后浏览器地址栏内容不变。 地址栏的URL地址不会改变
* 请求转发使用RequestDispatcher接口中的forward()方法来实现,该方法可以**把请求转发到另外一个资源,并让该资源对浏览器的请求进行响应。
*2.重定向 * 重定向是指页面重新定位到某个新地址,之前的请求失效,进入一个新的请求,且跳转后浏览器地址栏内容将变为新的指定地址。 地址栏的URL地址会改变
RequestDispatcher rd = request.getRequestDispatcher(path);
rd.forward(request,response);
- 第二种
request.getRequestDispatcher(path).forward(request,response);
req.getRequestDispatcher("main.jsp").forward(request, response);
重定向是通过HttpServletResponse对象的sendRedirect()来实现,该方法相当于浏览器重新发送一个请求。
response.sendRedirect(path);
response.sendRedirect(req.getContentType()+"/main.jsp");
我来说一下为什么sendRedirect会陷入死循环,而getRequestDispatcher.forward不会。
因为前者是使用的过滤器类型是request,访问index.jsp页面时,触发过滤器,重定向到main.jsp,由于过滤器类型是request,所以会继续触发main.jsp的过滤器,所以会陷入死循环。
后者在访问index.jsp的时候,过滤器类型是request,所以会触发过滤器,然后利用转发的方式访问main.jsp,这时候过滤器的类型是forward,而此时web.xml中定义的匹配main的过滤器类型还是request,所以不执行main所匹配的过滤器,因此只执行一次
*****************************************************************************************************************
在web项目下的WebRoot文件夹下添加一个配置文件:config.properties
username=lily
password=123
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
//以前这两句话在main()方法中可以得到结果,
//当前程序启动的目录;而此处config.properties不在此目录下,而程序在Tomcat中启动
//File file = new File("config.properties");
//System.out.println(file.getAbsolutePath());
//硬盘路径是可以,但是换发布环境,易出错误
//Properties prop = new Properties();
//prop.load(new FileReader("F:\webexample\JAVAWEB\Day03\WebRoot\config.properties"));
//使用ServletContext提供了getRealPath方法
Properties prop = new Properties();
prop.load(new FileReader(this.getServletContext().getRealPath("config.properties")));
System.out.println(prop.getProperty("username"));
System.out.println(prop.getProperty("password"));
}
(1)如果写相对路径和绝对路径,由于路径将会相对于程序启动的目录--在web环境下,就是tomcat启动的目录即tomcat/bin--所有找不到资源
(2)如果写硬盘路径,可以找到资源,但是只要一换发布环境,这个硬盘路径很可能是错误的,同样不行.
(3)为了解决这样的问题ServletContext提供了getRealPath方法,在这个方法中传入一个路径,这个方法的底层会在传入的路径前拼接当前web应用的硬盘路径从而得到当前资源的硬盘路径,这种方式即使换了发布环境,方法的底层也能得到正确的web应用的路径从而永远都是正确的资源的路径
this.getServletContext().getRealPath("config.properties"); WebRoot下面
(4) 如果在非Servlet环境下要读取资源文件时可以采用类加载器加载文件的方式读取资源
Service.class.getClassLoader().getResource("config.properties").getPath();
package com.lmd.service; (Service.java,有一个方法)
package com.lmd; (ServiceDemo.java, Servlet)
路径默认找F: omcat8webappsDay03WEB-INFclasses下面。
1)配置文件在src下时,在F: omcat8webappsDay03WEB-INFclasses下面
,改为("config.properties");
2)配置文件在另一个包(com.lmd)下时,在F: omcat8webappsDay03WEB-INFclassescomlmd下面,改为("com/lmd/config.properties");
3)配置文件在WEB-INF下时,在F: omcat8webappsDay03WEB-INF下面,改为("../config.properties");
4)配置文件在WebRoot下时,在F: omcat8webappsDay03下面,改为("../../config.properties");
5)配置文件不允许在Day03下,出错 ,java.lang.IllegalArgumentException;
package com.lmd.service
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Service{
public void method1() throws FileNotFoundException, IOException {
Properties prop = new Properties();
prop.load(new FileReader( Service.class.getClassLoader()
.getResource("config.properties").getPath()));
System.out.println(prop.getProperty("username"));
System.out.println(prop.getProperty("password"));
}
}
5、读取资源文件
5.1由于相对路径默认相对的是java虚拟机启动的目录,所以我们直接写相对路径将会是相对于tomcat/bin目录,所以是拿不到资源的。如果写成绝对路径,当项目发布到其他环境时,绝对路径就错了。
5.2为了解决这个问题ServletContext提供了this.getServletContext().getRealPath("/1.properties"),给进一个资源的虚拟路径,将会返回该资源在当前环境下的真实路径。this.getServletContext().getResourceAsStream("/1.properties"),给一个资源的虚拟路径返回到该资源真实路径的流。
5.3当在非servlet下获取资源文件时,就没有ServletContext对象用了,此时只能用类加载器
classLoader.getResourceAsStream("../../1.properties"),此方法利用类加载器直接将资源加载到内存中,有更新延迟的问题,以及如果文件太大,占用内存过大。
classLoader.getResource("../1.properties").getPath(),直接返回资源的真实路径,没有更新延迟的问题。