您的位置:首页 > 其它

servlet开发入门

2013-07-30 18:38 441 查看

01、servlet开发入门

动态web资源开发有两种技术:Servlet和JSP,只有把Servlet学明白了才能学JSP。

Sun公司在其API中提供了一个Servlet接口,用户若想用一个动态web资源(即开发一个java程序向浏览器输出数据),需要完成以下2个步骤:

* 编写一个java类,实现servlet接口

* 把开发好的java类部署到web服务器中

快速入门,用servlet向浏览器输出“hello servlet”

* 阅读servlet API ,解决两个问题:

* 输出hello servlet的java代码应该写在servlet的那个方法内?

* 如何向IE浏览器输出数据?

servlet api 文档:http://docs.oracle.com/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/

部分解释:

1、service(ServletRequest
req,
ServletResponse res)

Called by the servlet container to allow the servlet to respond to a request.

这个方法是谁调用的?

servlet是运行在服务器上的小程序,客户机向服务器请求数据,服务器调用这个方法,响应客户端的请求。

2、向客户端浏览器输出数据使用response下的getOutputStream()方法。

(1) 在webapps目录下新建一个程序文件夹,建立目录WEB-INF,并在旗下建立classes目录来存放java程序

(2)建立FirstServlet.java文件,并写 入如下代码:

package cn.myweb; //写这个程序必须要有包名,并且这个程序的访问权限必须为public,因为你写这个是给服务器调用的

import java.io.*; //用到了输入输出的内容

import javax.servlet.*; //用到了servlet的内容

public class FirstServlet extends GenericServlet{ //我要不就是实现servlet接口,要不就继承他的默认实现类

public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException //建议在文档中直接复制,否则大小写出错都不行。

{

OutputStream out=res.getOutputStream();

out.write("hello servlet!!".getBytes());

}

}

(3)在命令提示符中进入相应的目录进行编译 javac -d . firstservlet.java (-d是带包编译的意思,. 是把编译过的内容保存到当前目录下)

但是显示javax.servlet包不存在。



为什么找不到?

因为javac只能搜索到j2SE相关的API来编译程序,现在这里用到了j2EE相关的API,这个编译器找不到。

要把servlet API的所在的jar包加入到class path 中,让编译器找得到。

tomcat服务器目录下的lib中的servlet-api.jar加入,即set classpath=%classpath%;"c:\program file\...\servlet-api.jar"回车确定,或者直接在系统-环境变量中设定。

然后再来编译一次。发现在WEB-INF下生成cn,cn下生成myweb,旗下生成了FirstServlet.class这个类文件。

好的,现在servlet开发好了,我们要为他配置一个对外的访问路径。

对web资源的配置,通通都在web.xml中实现。

在WEB-INF下建立web.xml文件,这个文件怎么写,不知道,但是我们可以抄个类似的过来改。比如tomcat6.0下面的conf下的web.xml抄就是抄头抄屁股。

改写后的web.xml如下:

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">

<servlet>

<servlet-name>FirstServlet</servlet-name>

<servlet-class>cn.myweb.FirstServlet</servlet-class>

</servlet>

<servlet-mapping> //将FirstServlet映射到一个对外访问路径上

<servlet-name>FirstServlet</servlet-name> //名称

<url-pattern>/FirstServlet</url-pattern> //映射到web应用的这个路径上

</servlet-mapping>

</web-app>

开发好了,启动服务器,访问http://localhost:8080/day04/FirstServlet



显示成功,完成!

02 、servlet的调用过程和生命周期

打开UML绘图软件 Rational Rose,画出servlet的调用过程

Rational Rose中文破解版的安装,请参考:http://blog.csdn.net/snioper007/article/details/9283899



大图请看:http://img.my.csdn.net/uploads/201307/23/1374578724_6083.jpg

为什么不把servlet的destroy方法画出来?这是与servlet的生命周期有关的

生命周期:什么时候生,什么时候死,在生和死之间有什么事情会发生。

servlet什么时候生,客户机第一次访问的时候,服务器会创建这个servlet,服务器一旦把这个servlet对象创建起来,他就一直驻留在内存里面了,等待客户端的第二次访问, 而不会立即destroy。

servlet什么时候被摧毁,web服务器停止的时候,或者这个web应用被从服务器里删除的时候。

servlet的运行过程:

Servlet程序是由WEB服务器调用,web服务器收到客户端的servlet访问请求后:

(1)Web服务器首先检查是否已经装载并创建了该servlet实例对象。如果是则直接执行第四步,否则执行第二步。

(2)装载并创建该servlet实例对象。

(3)调用servlet实例对象的init()方法。

(4)创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用servlet的service()方法,并将请求和响应对象作为参数传递进去。(服务器发现response对象中有内容,就回送给客户端)

(5)Web应用程序被停止或重新启动之前,Servlet引擎将卸载servlet,并在卸载之前调用servlet的destroy()方法。

03、使用eclipse开发servlet

在eclipse中新建一个web project工程,eclipse会自动创建下图所示的目录结构:



在src中写程序,工程在发布的时候会自动把写的程序编译并发布到WEB-INF下的classes目录下去。

Java EE 5.0和J2EE 1.4 ,Java EE 5.0是新版本,采用了新的命名方法,其中加入了许多开发包,J2EE 1.4相对纯洁些,比Java EE 5.0少些东西,做练习时我们选J2EE 1.4。

开发步骤:

(1)src下新建个class



package cn.mytest;

import java.io.IOException;

import javax.servlet.GenericServlet;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

public class MyServlet1 extends GenericServlet {

@Override

public void service(ServletRequest arg0, ServletResponse arg1)

throws ServletException, IOException {

// TODO Auto-generated method stub

}

}

注意:MyServlet1.java中自动生成的service()方法中的参数不是req和res,这是没有导原码的原因,

在ServletRequest上右键open declaration》Attach Source 导入你的tomcat6.0 src文件,然后点击MyServlet1,再实现以下这个方法,就自动变成了req和res

tomcat 6.0 src 下载链接:http://pan.baidu.com/share/link?shareid=3885370713&uk=3121924741

(2)向浏览器输出数据:res.getOutputStream().write("hello my first servlet!".getBytes());

(3)在web.xml 中给servlet配置一个对外访问路径

<servlet>

<servlet-name>MyServlet1</servlet-name>

<servlet-class>cn.mytest.MyServlet1</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>MyServlet1</servlet-name>

<url-pattern>/MyServlet1</url-pattern>

</servlet-mapping>

(4)将此servlet程序发布到服务器上去

现在myeclipse里面集成tomcat,myeclipse》window》preferences》MyEclipse Enterprise Work》servers》Tomcat》Tomcat 6.X

选择好目录,并勾选上面的Enable。

注意:tomcat是一个java程序,我们没有指定具体是哪个java虚拟机来跑这个程序。点开tomcat 6.x,下面有个JDK,默认为MyEclipse,一般来说不用改。Launch要处于debug node。

(5)可以将工程发布到你刚才集成的tomcat中去了

点击工具栏上的Deploy MyEclipse J2EE Project to server(部署J2EE工程到服务器),点击Add,增加一台服务器,选择tomcat。

(6)启动tomcat来访问刚才写好的程序

点击工具栏上的Run/Stop/Restart MyEclipse server 下拉列表,选择tomcat。

(7)打开浏览器,访问http://localhost:8080/day05/MyServlet1,显示:hello my first servlet!

成功!

这是种比较笨的方法,可以在src右键直接建个Servlet,使用这个模板,他会直接把servlet写好同时帮你部署好web.xml。

注:如果类名myservlet写错了,在pack区的类名上右键选择refactor>rename,则所有引用都更新!!但是eclipse不够聪明,web.xml没有更新,必须要手动改一下!

04、HttpServlet基础

Servlet接口实现类:

Servlet接口Sun公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

HttpServlet指能够处理HTTP请求的servlet,它在原有servlet接口上添加了一些与HTTP协议处理方法,它比servlet接口的功能更为强大。因此开发人员在编写servlet时,通常应继承这个类,而避免直接去实现servlet接口。

HttpServlet在实现servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求方式,则调用HttpServlet的doGet方法,如为POST请求时,则调用doPost方法,因此,开发人员在编写servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

默认生成的HttpServlet()方法,里面没用的说明太多,如何更改httpservlet()的模板?

在myeclipse的安装目录下搜索:Servlet.java文件,打开,修改doget、dopost等方法,删掉没用的说明内容。

05、servlet开发的一些重要细节

(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的对外访问路径。

同一个servlet,可以有多个<servlet-mapping>来映射多个URL来访问,地址上也可以使用通配符*,但是只能有两种固定的格式:

一种格式是*.扩展名

一种格式是以/开头,以/*结尾。

不能写成混合体 /XXX/*.html

注:web.xml改动,不需要重启服务器,即会自动重新加载(但是写java类的时候,一般都要重新加载)。

因为conf下的context.xml中配置的是全局的设置,被所有web应用共享,里面有句<WatchedResource>WEB-INF/web.xml</WatchedResource>

<url-pattern>/aa</url-pattern>

<url-pattern>/1.html</url-pattern>

因为使用了通配符,所以就可能会有地址冲突的问题:

例如,对于如下的映射关系:

。servlet1 映射到/abc/*

。servlet2 映射到/*

。servlet3 映射到/abc

。servlet4 映射到*.do

问题:

。当请求的URL为/abc/a.html,/abc/*和/*都匹配,到底哪个servlet响应呢? 应为:servlet1

。当请求的URL为/abc时,/*和/abc都匹配,到底哪个servlet响应呢? 应为:servlet3

。当请求的URL为/abc/a.do时,/abc/*和/*.do都匹配,到底哪个servlet响应呢?应为:servlet1

。当请求的URL为/a.do时,/*和*.do都匹配,到底哪个servlet响应呢? 应为:servlet2

。当请求的URL为/XXX/yyy/a.do时,/*和*.do 到底哪个servlet响应呢? 应为:servlet2

从第一个字母比较,哪个最像,最接近就是哪个响应。*.do的优先级是最低的。

(2)servlet是一个供其他java程序(servlet引擎)调用的java类,他不能独立运行,他的运行完全由servlet引擎来控制和调度。

针对客户端的多次servlet请求,通常情况下,服务器只会一个servlet实例对象,也就是说servlet实例对象一旦创建,他就会驻留在内存中,为后续的其他请求服务,直至web容器退出,servlet实例对象才会销毁。

在servlet的整个生命周期中,servlet的init()方法只被调用一次(对象创建时才调用,第一次访问servlet程序时调用)。而对一个servlet的每次访问请求都会导致servlet引擎调用一次servlet的service()方法。

对于每一次访问请求(一个人发10次请求,也是10个request),servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的servlet的service()方法,service()方法再根据请求方式分别调用doGet/doPost方法。

请求结束,request和response会立刻销毁释放,所以像新浪搜狐每天上亿次的请求,也不怕,基本不占内存空间。

(3)如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建servlet的实例对象、以及调用servlet实例对象的init()方法。

如果不配置的话,服务器接受第一次访问请求时才创建servlet的实例对象,才调用init()方法。

举例:

<servlet>

<servlet-name>invoker</servlet-name>

<servlet-class>

org.apache.catalina.servlets.InvokerServlet

</servlet-class>

<load-on-startup>2 </load-on-startup>

</servlet>

注:<load-on-startup>2</load-on-startup> 中数字的大小决定了启动的顺序。数字越小,优先级越高。

用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。

(4)如果某个servlet的映射路径仅仅为一个正斜杠/,那么这个servlet就成为当前web应用程序的缺省servlet。

凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,他们的访问请求都将交给缺省servlet处理,也就是说,缺省servlet用于处理所有其他servlet都不处理的请求访问。比如访问/day05/shifdfadjfaohfashfoafaoj,没有这个路径,自动访问缺省的servlet。

在<tomcat的安装目录>\cconf\web.xml文件中,注册了一个名称为org.apache.catalina.servlet.DefaultServlet的servlet,并将这个servlet设置为缺省servlet。

当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省servlet。

假如你在WebRoot下新建个1.html,访问/day05/1.html,是由缺省servlet来执行的。

记住:浏览器向服务器发送请求,不管你发送的是什么请求,静态的,或者动态的页面,都是去找servlet。

对1.html发送请求,没有servlet映射到这个地址上来,这时候服务器发现没有servlet服务器映射到这个地址,他就去找缺省的servlet。由缺省的servlet来查找名称为1.html的静态页面资源,若找到,则其读取1.html的数据。

打开web.xml,发现里面没有带/的缺省servlet配置,但是虽然没有明确写,但是服务器帮你配置了一个默认的缺省servlet。

如果我们自己写一个缺省的servlet,就会把服务器的缺省servlet给覆盖掉。

<servlet-mapping>

<servlet-name>firstweb</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

注意当你在浏览器中随便输入一个不存在的地址,比如/day05/djfaojfwjoejfjeowjf,浏览器会显示404,这个页面也是数据啊,也是由程序输出的。

谁输出的呢?还是缺省servlet,他找这个名称的静态页面资源,发现还是找不到,就给你回送找不到,404。

服务器背后这个缺省的servlet到底在哪呢? 在\conf\web.xml中。(这里面配置的web资源,由所有的web服务器共享!)

<servlet>

<servlet-name>default</servlet-name>

<servlet-class>org.apache.catalina.servlet.DefaultServlet</servlet-class>

......

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>default</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

06、servlet的线程安全问题

当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service()方法,因此service()方法内如果访问了同一个资源的话,就有可能引发线程安全问题。

如果100个线程同时执行下面代码,会产生线程安全问题吗?

public class MyServlet3 extends GenericServlet {

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

int i=0;

i++;

System.out.println(i);

}

每个线程有各自的i,所以不会有线程安全问题。

若改为

public class MyServlet3 extends GenericServlet {

int i=0;

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

i++;

System.out.println(i);

}

则会有线程安全问题,因为i,只有一个,100个线程同时来操作这同一个i。

线程安全问题,一般是指程序操作共享资源,比如文件,数据库,共享变量等。

若改为:

public class MyServlet3 extends GenericServlet {

int i=0;

public void service(ServletRequest req, ServletResponse res)

throws ServletException, IOException {

synchronized(this)

{

i++;

try {

Thread.sleep(1000*5);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(i);

res.getOutputStream().write((i+"").getBytes());

}

}

}

则可以正确的顺序输出,因为当两个并发线程访问同一个对象中的这个synchronized(this)同步代码块时,同一时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

但是在实际开发中,不能这么实现,因为一个用户的访问,就阻断其余所有的用户访问,这么做不现实。

servlet如何来解决这个问题呢?使用SingleThreadModel接口,这个接口里面啥都没有,没有定义任何方法,我们通常称为标记接口。

只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可,如果你的类实现了这个接口,就相当于你这个类打上了标号,即你的这个类就与众不同了,拥有了一些特定的功能。

你的类如果实现了SingleThreadModel接口,那么你这个类就是线程安全的。

public class MyServlet3 extends HttpServlet implements SingleThreadMdoel{}

如果某个servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service()方法。

对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个servlet实例对象,并发的每个线程分别调用一个独立的servlet实例对象,一个servlet对象对应一个请求。

实现SingleThreadModel接口并不能真正解决servlet的线程安全问题,因为servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。

07、SrvletConfig对象和它在开发中的应用场景



web服务器在调用servlet时到底传多少个对象呢?打开servlet文档,左侧N多的API,如果你把这些都掌握了,也就学好了。

servletConfig对象

在servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。

当servlet配置了初始化参数后,web容器在创建servlet实例对象时,服务器会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前Servlet的初始化参数信息。

例如,程序通常需要一些数据,但是这些数据写实在程序中又不太好,我们可以写在配置文件中:

<servlet>

<servlet-name>ServletConfigtest</servlet-name>

<servlet-class>cn.mytest.ServletConfigtest</servlet-class>

<init-param>

<param-name>data</param-name>

<param-value>datadevalue</param-value>

</init-param>


</servlet>

注:若有多个数据可以写多个<init-param>

在配置文件配置上了这些信息后,服务器会自动把这些配置信息封装到servletConfig对象当中。并在调用servlet的init()方法时,将ServletConfig对象传递给servlet 。我要在servlet中拿到数据,只需要覆写init()方法。

public class ServletConfigtest extends HttpServlet {

private ServletConfig config;

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String value=config.getInitParameter("data");

System.out.println(value);

}

public void init(ServletConfig config) throws ServletException {

// TODO Auto-generated method stub

this.config=config;

}

}

因为httpServlet的父类中已经定义过方法,所以可以简化为:

public class myconfig extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String value=this.getServletConfig().getInitParameter("data");

System.out.println(value);

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request,response);

}

}

若在web.xml中配置多个数据,则

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//得到指定的

String value=this.getServletConfig().getInitParameter("data");

System.out.println(value);

//得到所有的

Enumeration e=this.getServletConfig().getInitParameterNames();

while(e.hasMoreElements()){

String name=(String)e.nextElement();

String value1=this.getServletConfig().getInitParameter(name);

System.out.println(name+"="+value1);

}

}

那么在实际开发中,什么数据不适合写实,适合在servletconfig里面配置呢?

比如配置中文显示的字符集编码;

比如连接那个数据库,及数据库名和密码;

比如servlet具体读取那个配置文件,(struts中常用)。

08、ServletContext的介绍和应用

Web容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext对象,它代表当前web应用。(对象有相应的方法,及servletContext一定有管理所有web资源的方法,及也可以实现资源间的数据传递和共享。)

ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

如:

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//得到ServletContext的方法1

ServletContext context = this.getServletConfig().getServletContext();

//得到ServletContext的方法2

context=this.getServletContext();

}

由于一个Web应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过servletcontext对象来实现数据共享。servletcontext对象通常也被称之为context对象。(web发开中总共四个域,context、request、session、page域,难点!)

context域,

1、这是一个容器;

2、说明了这个容器的作用范围,也就是应用程序范围。

如下例子实现两个servlet共享传递数据:

在servletcontext1中写入:

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String data="xyz";

this.getServletContext().setAttribute("data", data);

}

在servletcontext2中写入:

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String a=(String)this.getServletContext().getAttribute("data");

System.out.println(a);

}

然后现在浏览器中访问1的URL,然后再访问2的URL,即可在console中收到显示的数据。

实际项目开发中,可以用在如聊天室中,两个人间的数据传递。

可以获取Web应用的初始化参数

在web.xml中,写

<context-param>

<param-name>data</param-name>

<param-value>XXX</param-value>

</context-param>

<context-param>为整个web应用配置初始化参数。<init-param>只是为某个具体的servlet配置初始化参数。

web服务器在加载这个应用的时候,就创建代表这个应用的servletcontext对象,这个对象创建出来了之后,他会自动把这个配置的初始化参数,封装到servlet的context对象当中。你在程序中直接取就可以了。

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String value=this.getServletContext().getInitParameter("data");

System.out.println(value);

}

多个初始化参数,可以参考上面的设置。

servlet如何连接数据库?如果有100个servlet都要连接数据库,是不是每个都要配置servletconfig?

可以用<context-param>来配置整个web应用的初始化参数,统一配置数据库的连接。

可以实现servlet的转发

转发,你请求A,A没有,A帮你去找。客户机只发了一次请求。

重定向是,你请求A,A没有,A让你自己去找。客户机发了两次请求。

转发在web开发中很常用,产生的数据,转发给JSP,让JSP去美化,生成HTML输出。

例如,新建servlet

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String data="aaaaaaaaaaaaa";

//把数据存在servletcontext中带给1.jsp,可以实现但是是不合理的。

this.getServletContext().setAttribute("data", data);

RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/1.jsp");

rd.forward(request, response);

}

在WebRoot目录下新建1.jsp

<body>

<h1>

<font color="red">

<%

String value=(String)application.getAttribute("data"); //application就代表servletcontext

out.write(value);

%>

</font>

</h1>

</body>

发布后,在浏览器地址栏上写上servlet的映射地址,既可以输出排版后的数据aaaaaaaaaaaaa。这就是请求转发,servlet去请求1.jsp然后反馈给客户端浏览器。

将数据存储在servletcontext中带给1.jsp是不合理的,因为servletcontext是被所有人共享的。比如有很多人同时来访问,他们的数据都存储在data,第一个用户的数据可能没来得及转发给1.jsp去显示,就被第二个用户给覆盖掉了,结果把第二个用户的数据给显示出来了。

那么servlet的数据由谁给带给jsp呢?不能通过context域,要通过request域,且见下回分解。

servletContext对象的生命周期

创建,服务器启动的时候,会对服务器里面的每一个应用分别创建一个servletContext。webapps里面有多少个工程,就创建多少个servletContext。

销毁,停掉服务器,或者从服务器里删除某个web应用。

09、利用servletContext对象来读取web应用中的资源文件

比如,应用程序中有许多的servlet都要连接数据库,到底连接那个库,我们可以使用一个配置文件来保存。

在软件开发里面,用作配置文件的文件类型通常有两种,一种是xml文件,一种是properties

如果配置文件中的数据间是有关系的,则用xml文件,反之使用properties

连接数据库的是没有关系的,所以使用properties。在src上右键new>file>db.properties。

url=jdbc:mysql://localhost:3306/test

username=root

password=root

程序要连接数据库,需要读取这个配置文件,这个文件在web工程里面也是属于web资源,可以通过servletContext来读取。

新建一个servlet,

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//InputStream in=this.getServletContext().getResourceAsStream("/src/db.properties"); //这样写地址是错的,/代表web工程,src在发布到服务器时会写到classes文件下面。

InputStream in=this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");//这样的地址才是正确的

//下面是模板代码,读取properties文件通通都是这么读,要求熟练掌握

Properties pros=new Properties();

pros.load(in);

String url=pros.getProperty("url");

String username=pros.getProperty("username");

String password=pros.getProperty("password");

System.out.println(url);

System.out.println(username);

System.out.println(password);

}

在浏览器里输入当前servlet的URL,即可在myeclipse的console里面显示出properties对应的信息。

注:传统的文件读取方式为:FileInputStream in=new FileInputStream(“classes/db.properties”);

他的相对路径是相对于java 虚拟机的目录的,即bin目录。

所以一般不使用这种方式,使用servletcontext就可以避免这个问题。

也可以通过servletcontext的getRealPath方法,先获得绝对路径,然后再调用传统的FileInputStream。

String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");//获得文件的绝对路径

FileInputStream in=new FileInputStream(path);

String filename=path.substring(path.lastIndexOf("\\")+1); //获得文件的真实名称

//下面是模板代码,读取properties文件通通都是这么读,要求熟练掌握

Properties pros=new Properties();

pros.load(in);

String url=pros.getProperty("url");

String username=pros.getProperty("username");

String password=pros.getProperty("password");

System.out.println(url);

System.out.println(username);

System.out.println(password);

System.out.println(filename);

这种方式一般用来做下载用,因为他可以获得真实名称,而第一种方法,只能得到流。

用户请求servlet,当需要操作数据库时,有专门的dao程序来负责操作数据库,servlet不去操作数据库,要去调用dao,来操作数据库。

如果读取资源文件不是servlet,只是普通的java程序的话,就只能通过类装载器来读。比如在cn.myweb.dao下新建一个UserDao.java,就不能直接操作servletcontext。

InputStream in= UserDao.class.getClassLoader().getResourceAsStream("db.properties");

但是用类加载器加载的文件会到内存中,所以文件不能太大。

注意:使用类装载器,他只装载一次进入内存,当你修改文件后,再次访问时,他发现内存中已经有这个类了,就直接从内存读取,即读取的仍然是旧的文件。

解决办法,不能通过类加载的方式来读,传统方法来读,需要文件的路径,所以我们通过类加载获得路径后再使用传统方法来读取。这样就可以及时读取文件的更新了。

String path=UserDao.class.getClassLoader().getResource("db.properties").getPath();

FileInputStream in=new FileInputStream(path);

Properties pros=new Properties();

pros.load(in);

System.out.println(pros.getProperty("url"));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: