Servlet开发(二)

来源:互联网 发布:大数据先进技术研究院 编辑:程序博客网 时间:2024/06/06 04:07

ServletConfig对象

配置Servlet初始化参数

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

<servlet>    <servlet-name>ServletDemo5</servlet-name>    <servlet-class>cn.itcast.ServletDemo5</servlet-class>    <!-- 配置ServletConfigDemo5的初始化参数 -->    <init-param>      <param-name>data1</param-name>      <param-value>xxxxx</param-value>    </init-param>    <init-param>      <param-name>data2</param-name>      <param-value>yyyyy</param-value>    </init-param>    <init-param>      <param-name>data3</param-name>      <param-value>zzzzz</param-value>    </init-param></servlet>

通过ServletConfig获取Servlet的初始化参数

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

public class ServletDemo5 extends HttpServlet {    private ServletConfig config;    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 得到指定的初始化参数        String value = config.getInitParameter("data1");        System.out.println(value);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }    @Override    public void init(ServletConfig config) throws ServletException {        this.config = config;    }}

在实际开发中,为了得到封装数据的ServletConfig,可以不这样写。因为查看GenericServlet类的源码可发现,以上代码都有,而且还提供一个方法——getServletConfig()来获取ServletConfig实例对象。所以我们一般这样写代码:

public class ServletDemo5 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 得到指定的初始化参数        String value = this.getServletConfig().getInitParameter("data1");        System.out.println(value);        // 得到所有的初始化参数        Enumeration<String> e = this.getServletConfig().getInitParameterNames();        while(e.hasMoreElements()) {            String name = e.nextElement();            String value1 = this.getServletConfig().getInitParameter(name);            System.out.println(name+"="+value1);        }    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

在Servlet的配置文件中,什么时候我们需要使用一个或多个<init-param>标签为Servlet配置一些初始化参数呢?
答:ServletConfig对象,用于封装Servlet的配置信息。在实际开发中,有一些数据不适合在Servlet程序中写死,这类数据就可以通过配置方式配给Servlet。例如:

  • Servlet采用哪个码表?

    <servlet>    <servlet-name>ServletDemo5</servlet-name>    <servlet-class>cn.itcast.ServletDemo5</servlet-class>    <init-param>        <param-name>charset</param-name>        <param-value>UTF-8</param-value>    </init-param></servlet>
  • Servlet连接哪个数据库?

    <servlet>    <servlet-name>ServletDemo5</servlet-name>    <servlet-class>cn.itcast.ServletDemo5</servlet-class>    <init-param>        <param-name>url</param-name>        <param-value>jdbc:mysql://localhost:3306/test</param-value>    </init-param>    <init-param>        <param-name>username</param-name>        <param-value>root</param-value>    </init-param>    <init-param>        <param-name>password</param-name>        <param-value>root</param-value>    </init-param></servlet>
  • Servlet读取哪个配置文件?

    <servlet>    <servlet-name>ServletDemo5</servlet-name>    <servlet-class>cn.itcast.ServletDemo5</servlet-class>    <init-param>        <param-name>config</param-name>        <param-value>/struts-config.xml</param-value>    </init-param></servlet>

ServletConfig对象的作用

通过以上什么时候需要为Servlet配置一些初始化参数问题的答案可知,该对象的作用:

  • 获得字符集编码
  • 获得数据库连接信息
  • 获得配置文件,查看struts案例的web.xml文件

ServletContext对象

WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext()方法获得ServletContext对象。
例,获得ServletContext示例。

public class ServletDemo6 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 得到ServletContext的方式一        ServletContext context = this.getServletConfig().getServletContext();        // 得到ServletContext的方式二        context = this.getServletContext();    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

显然第一种方式比较繁琐,故直接采用第二种方式。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象

ServletContext的应用

多个Servlet通过ServletContext对象实现数据共享

例,通过ServletContext实现ServletDemo7和ServletDemo8之间的数据共享。

  • ServletDemo7代码

    public class ServletDemo7 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        String data = "aaaa";        this.getServletContext().setAttribute("data", data);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}
  • ServletDemo8代码

    public class ServletDemo8 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        String value = (String) this.getServletContext().getAttribute("data");        System.out.println(value);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

    先运行ServletDemo7,将数据data存储到ServletContext对象中,然后运行ServletDemo8就可以从ServletContext对象中取出数据了,这样就实现了数据共享。

结论:
ServletContext域:

1. 这是一个容器。
2. ServletContext域说明了这个容器的作用范围,也就是应用程序范围。

获取WEB应用的初始化参数

在web.xml文件中使用<context-param>标签配置WEB应用的初始化参数,如下所示:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">  <display-name>day05</display-name>  <!-- 配置WEB应用的初始化参数 -->  <context-param>    <param-name>data1</param-name>    <param-value>xxxx</param-value>  </context-param>  <context-param>    <param-name>data2</param-name>    <param-value>xxxx</param-value>  </context-param>  <context-param>    <param-name>data3</param-name>    <param-value>xxxx</param-value>  </context-param>  <welcome-file-list>    <welcome-file>index.jsp</welcome-file>  </welcome-file-list></web-app>

<context-param>标签为整个web应用配置初始化参数,web服务器只要一加载这个web应用, 他就创建代表这个web应用的ServletContext对象,把这个对象创建出来之后,它会自动把配置的初始化参数封装到ServletContext对象中。
ServletContext对象什么时候销毁呢?——如果停掉服务器,服务器就会销毁掉代表每一个web应用的ServletContext对象,或者你从服务器删掉某一个web应用,服务器就会销毁掉代表此web应用的ServletContext对象。
获取web应用的初始化参数,代码如下:

public class ServletDemo9 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 获取整个web站点的初始化参数        String value = this.getServletContext().getInitParameter("data1");        /*         * 客户机请求服务器,访问Servlet,Servlet产生数据,         * 数据不适合在Servlet输出!这时Servlet通常会把         * 请求转发给jsp,由jsp负责输出数据         */        response.getOutputStream().write(("<font color='red'>"+value+"</font>").getBytes());    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

注意:客户机请求服务器,访问Servlet,Servlet产生数据,数据不适合在Servlet输出!这时Servlet通常会把请求转发给jsp,由jsp负责输出数据。
什么时候我们需要使用一个或多个<context-param>标签为整个WEB应用配置一些初始化参数呢?
答:此问题可参考——在Servlet的配置文件中,什么时候我们需要使用一个或多个<init-param>标签为Servlet配置一些初始化参数呢?原理一样。

通过ServletContext实现请求转发

上面我们讨论过:客户机请求服务器,访问Servlet,Servlet产生数据,数据不适合在Servlet输出!这时Servlet通常会把请求转发给jsp,由jsp负责输出数据。

  • 转发
    你请求我这个Servlet,我这个Servlet把你的请求转给他处理。用大白话来说就是你找我借钱,我说我没有,我帮你去找他。客户机发了一次请求!
  • 重定向
    用大白话来说就是你找我借钱,我说我没有,我要你自己去找他。客户机发了二次请求!

ServletDemo10:

public class ServletDemo10 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        String data = "aaaaaaaaaaaaaaaa";        // 把数据带给1.jsp        /*         * ServletContext对象会被所有的web资源所共享,         * 通过他来带数据,会出现数据紊乱的现象。         * 在实际开发里面,不能通过context域,要通过request域         */        this.getServletContext().setAttribute("data", data);        RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/1.jsp"); // 获取请求转发对象(RequestDispatcher)        rd.forward(request, response); // 调用forward方法实现请求转发    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

注意: ServletContext对象会被所有的web资源所共享,通过它来带数据,会出现数据紊乱的现象。在实际开发里面,不能通过context域,要通过request域。
1.jsp:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"    pageEncoding="ISO-8859-1"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><body>    <h1>    <font color="red">    <%         String data = (String) application.getAttribute("data");        out.write(data);        %>    </font>    </h1></body></html>

注意:在jsp中,application就代表Servlet中的ServletContext对象。
运行结果:
这里写图片描述
访问的是ServletDemo10,浏览器显示的却是1.jsp的内容,这就是使用ServletContext实现了请求转发。

利用ServletContext对象读取资源文件

有一个资源文件db.properties,内容如下:

url=jdbc:mysql://localhost:3306/testusername=rootpassword=root

当资源文件db.properties位于整个项目中的如下位置时:
这里写图片描述

利用ServletContext对象读取该资源文件的代码为:

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

当资源文件db.properties位于整个项目中的如下位置时:
这里写图片描述

利用ServletContext对象读取该资源文件的代码为:

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/cn/itcast/db.properties");        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

当资源文件db.properties位于整个项目中的如下位置时:
这里写图片描述

利用ServletContext对象读取该资源文件的代码为:

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        InputStream in = this.getServletContext().getResourceAsStream("/db.properties");        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

当资源文件db.properties位于整个项目中的如下位置时:
这里写图片描述

使用如下传统代码去读取资源文件是不可行的:

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        FileInputStream in = new FileInputStream("src/db.properties");        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

那么将以上的一句代码

FileInputStream in = new FileInputStream("src/db.properties");

修改为:

FileInputStream in = new FileInputStream("classes/db.properties");

此时依然不可行,原因是这时读取的文件路径采用的是相对路径,相对路径相对于的是谁呢?这行代码是由服务器来调,服务器又是由JVM来运行的,所以是相对于JVM的启动目录。那么JVM是在哪个目录下面启动的呢?——启动服务器时同时启动JVM,所以在tomcat服务器中的bin目录下。
所以解决办法是在tomcat服务器中的bin目录下新建一个classes目录,目录下存放要读取的资源文件db.properties。如图:
这里写图片描述

此时再使用如下传统代码去读取资源文件是可行的:

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        FileInputStream in = new FileInputStream("classes/db.properties");        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

但是为什么我仍然会报异常java.io.FileNotFoundException: classes\db.properties (系统找不到指定的路径。)呢?到底是为什么啊!谁能告诉我啊?
总结:读取资源文件需要注意的问题:以上传统代码不可行,最好采用ServletContext去读。
通过ServletContext读取资源文件还有一种方式——通过ServletContext的getRealPath()方法得到资源的绝对路径后,再通过传统流读取资源文件。

public class ServletDemo11 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 返回资源文件在硬盘的绝对路径        String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");        // System.out.println(path); // F:\Tomcat_8\apache-tomcat-8.0.36\webapps\day05\WEB-INF\classes\db.properties        String filename = path.substring(path.lastIndexOf("\\")+1);        System.out.println("当前读取到的资源名称是:"+filename);        FileInputStream in = new FileInputStream(path);        Properties props = new Properties(); // 内部是map集合        props.load(in);        String url = props.getProperty("url");        String username = props.getProperty("username");        String password = props.getProperty("password");        System.out.println("当前读取到的资源数据是:");        System.out.println(url);        System.out.println(username);        System.out.println(password);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

使用这种方式的一个最大优点就是可以获得当前读取到的资源文件的名称。

WEB应用中的普通Java程序如何读取资源文件

假设Servlet程序(ServletDemo12)要通过数据访问层(dao)去访问数据库,数据访问层(dao)就是专门负责和数据库打交道的,所以要读取数据库的一些连接信息,这些信息通常在一个配置文件中,比如db.properties,那么这种普通Java程序如何读取资源文件?
以下这种做法如何?
ServletDemo12代码:

public class ServletDemo12 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        /*         * 在实际开发中,是不能这样做的, 这样做就相当于web层侵入了数据访问层         * (web层侵入了dao层)         * 层和层之间耦合在一起了,层和层之间最好上层不依赖于下层,         * 不符合软件设计思想         */        ServletContext context = this.getServletContext();        UserDao dao = new UserDao();        dao.update(context);    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

Userdao代码:

public class UserDao {    public void update(ServletContext context) {        context.getResourceAsStream("xxxxx");    }}

在实际开发中,是不能这样做的, 这样做就相当于web层侵入了数据访问层,层和层之间耦合在一起了,层和层之间最好是上层不依赖于下层,这样做不符合软件设计思想。
Servlet调用其他程序,在其他程序中如何去读取资源文件呢?
答:通过类转载器。
ServletDemo12代码:

public class ServletDemo12 extends HttpServlet {    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        UserDao dao = new UserDao();        dao.update();    }    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

UserDao代码:

public void update() throws IOException {    Properties dbconfig = new Properties();    InputStream in = UserDao.class.getClassLoader().getResourceAsStream("db.properties");    dbconfig.load(in);    System.out.println(dbconfig.getProperty("url"));}

那么问题又来了,数据访问层UserDao里不只有改的方法,还有增删查的方法,每个方法里都要读取资源文件,那必然是愚蠢的!!!所以我们可以将读取资源文件的代码存放在类的静态代码块中。

public class UserDao {    private static Properties dbconfig = new Properties();    static {        try {            InputStream in = UserDao.class.getClassLoader().getResourceAsStream("db.properties");            dbconfig.load(in);        } catch (IOException e) {            //抛一个初始化错误            throw new ExceptionInInitializerError(e);        }    }    public void update() throws IOException {        System.out.println(dbconfig.getProperty("url"));    }    public void find() {        ....    }    public void delete() {        ....    }}

还有一个小细节:若UserDao类中的update()方法为:

public void update() throws IOException {    // 以下代码虽然可以读取到资源文件的数据,但是无法获取更新后的数据。    Properties dbconfig = new Properties();    InputStream in = UserDao.class.getClassLoader().getResourceAsStream("db.properties");    dbconfig.load(in);    System.out.println(dbconfig.getProperty("url"));}

以上代码虽然可以读取到资源文件的数据,但是无法获取更新后的数据,即此时修改资源文件db.properties中的内容,但读取不到最新的内容。这一般不是我们所希望的,那么怎么解决这个问题呢?
解决办法:通过类装载的方式得到资源文件的位置,再通过传统方式读取资源文件的数据,这样就可以读取到更新后的数据。代码如下:

public void update() throws IOException {    // 通过类装载的方式得到资源文件的位置,再通过传统方式读取资源文件的数据,这样就可以读取到更新后的数据。    String path = UserDao.class.getClassLoader().getResource("db.properties").getPath();    FileInputStream in = new FileInputStream(path);    Properties dbconfig = new Properties();    dbconfig.load(in);    System.out.println(dbconfig.getProperty("url"));}

总结:
如果读取资源文件的程序不是Servlet的话,就只能通过类装载器去读了。而通过类装载器去读取资源文件需要注意两个问题:

  • 资源文件不能太大!!!
  • 通过类装载的方式去读,采用类装载的方式去读文件,经常读不到更新后的数据。
1 0
原创粉丝点击