Servlet学习记录2

时间:2021-10-20 16:44:25

  读取web.xml参数

  上篇文章ImageServlet里只设置了JPG,GIF,DOC类型文件的Content-Type。如果这时候需求变化了,需要增加Excel文件格式的Content-Type,就得修改ImageServlet源代码,重新编译class文件,然后重新部署。一个看起来很微小的改动却带来了大量的工作,很让人心烦。

  现在在Java Web开发中,这种常量信息更倾向于写在某个配置文件里。需求变化时只需要修改一下配置文件就可以了,而不会修改源程序,也不会重新编译,维护起来相当方便。web.xml提供了设置初始化参数的功能,可以将这些信息配置在web.xml中。

  初始化参数(init-param)

  web.xml中配置Servlet的时候,标签<servlet>中可以包含标签<init-param>来配置初始化参数。一个Servlet可以配置0到多个初始化参数。接下来的例子InitParamServlet中配置了3个初始化参数。

<servlet>
<servlet-name>InitParamServlet</servlet-name>
<servlet-class>
com.helloweenvsfei.servlet.InitParamServlet
</servlet-class>
<init-param>
<param-name>helloween</param-name>
<param-value>password</param-value>
</init-param>
<init-param>
<param-name>admin</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<param-name>babyface</param-name>
<param-value>babyface</param-value>
</init-param>
</servlet>

  配置完毕后,Servlet中提供方法getInitParameter(String param)来获取初始化参数值。如果配置了名为param的参数,则返回参数值,否则返回null。在这段配置中,getInitParameter("helloween")将返回password,getInitParameter("Cobain")将返回null。还可以使用getInitParameterNames()方法返回所有的参数名称,返回结果为枚举类型(Enumeration)。

  这些初始化的参数也可以由ServletConfig对象取得。Servlet提供getServletConfig()这个ServletConfig对象。由ServletConfig取初始化参数与Servlet直接取方式一样。

  看一例子。公司要在网站上颁布一个机密文件notice.html,要求仅有少数几个人能够浏览到。我们来实现这种权限控制。

  (1)把notice.html放在/WEB-INF文件夹下面。因为Java Web应用程序的WEB-INF文件夹有这个特性,任何人都不能通过浏览器直接获取下面的文件,即使他知道文件的准确位置及名称。WEB-INF下的文件是受保护的,这样就保证了文件的安全性。如果浏览器中输入http://localhost:8080/servlet/WEB-INF/notice.html,服务器会报404 Error,尽管这个文件地址是对的。

  (2)编写一个程序InitParamServlet,提示用户输入用户名密码。如果密码验证通过,则通过程序转到notice.html上。虽然WEB-INF下的文件不能通过浏览器直接获取到,但是仍然可以通过程序读取到。程序如下:

package com.helloweenvsfei.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class InitParamServlet extends HttpServlet { private static final long serialVersionUID = 7298032096933866458L; public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>请登录查看 Notice 文件</TITLE></HEAD>");
out.println("<style>body, td, div {font-size:12px; }</style>");
out.println(" <BODY>"); out.println("<form action='" + request.getRequestURI() + "' method='post'>");
out.println("帐号:<input type='text' name='username' style='width:200px; '> <br/>");
out.println("密码:<input type='password' name='password' style='width:200px; '> <br/><br/>");
out.println("<input type='submit' value=' 登录 '>");
out.println("</form>"); if(true){
out.println("<br/><br/><br/><br/><br/><br/><br/>用户名、密码为:<br/>");
Enumeration params = this.getInitParameterNames();
while(params.hasMoreElements()){
String usernameParam = (String)params.nextElement();
String passnameParam = this.getInitParameter(usernameParam);
out.println("[" + usernameParam + ", " + passnameParam + "], ");
}
} out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 提交的 username 参数
String username = request.getParameter("username");
// 提交的 password 参数
String password = request.getParameter("password");
// 取所有的初始化参数名称
Enumeration params = this.getInitParameterNames();
while(params.hasMoreElements()){
String usernameParam = (String)params.nextElement();
// 取参数值
String passnameParam = this.getInitParameter(usernameParam);
// 如果 username 匹配且 password 匹配. username 大小写不敏感,password大小写敏感
if(usernameParam.equalsIgnoreCase(username)
&& passnameParam.equals(password)){
// 显示文件。/WEB-INF 下的文件不能通过浏览器访问到,因此是安全的
request.getRequestDispatcher("/WEB-INF/notice.html").forward(request, response);
return;
}
}
// username,password 不匹配,显示登录页面
this.doGet(request, response);
} }

  登陆前后的效果如图3.6所示。Servlet显示/WEB-INF/下的文件。

Servlet学习记录2

  一个月后,公司要求增加Peter为可以浏览该机密文件的人选,同时禁止babyface的账号。由于没有将这些信息写在源程序里而是写在了web.xml里,因此改动相当简单。在参数配置里添加一个Peter,删掉babyface就可以了。

  初始化参数的好处是可以把某些变量拿到web.xml中配置,需要修改时只需要修改web.xml文件并重启服务器即可,而不需要修改Servlet类。

  上下文参数(context-param)

  由于init-param是配置在<servlet>标签里的,只能由这个Servlet来读取,因此它不是全局的参数,不能被其他的Servlet读取。

  如果需要配置一个所有Servlet都能够读取的参数,就需要用到上下文参数(Context-Param),或者叫文档参数。上下文参数使用标签<context-param>配置,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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"> <context-param>
<param-name>upload folder</param-name>
<param-value>attachment</param-value>
</context-param>
<context-param>
<param-name>allowed file type</param-name>
<param-value>.gif,.jpg,.bmp</param-value>
</context-param>

  获取context-param可以使用ServletContext对象。Servlet中通过getServletConfig().getServletContext()来获取一个ServletContext对象,使用ServletContext的getInitParameter()方法来获取指定名称的参数,通过getInitParameterNames()获取所有的context-param参数名称。下面是一个例子:

package com.helloweenvsfei.servlet;

import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class ContextParamServlet extends HttpServlet { private static final long serialVersionUID = 3194071196406358461L; public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>读取文档参数</TITLE></HEAD>");
out.println(" <link rel='stylesheet' type='text/css' href='../css/style.css'>");
out.println(" <BODY>");
out.println("<div align=center><br/>");
out.println("<fieldset style='width:90%'><legend>所有的文档参数</legend><br/>"); ServletContext servletContext = this.getServletConfig().getServletContext(); String uploadFolder = servletContext.getInitParameter("upload folder");
String allowedFileType = servletContext.getInitParameter("allowed file type"); out.println("<div class='line'>");
out.println(" <div align='left' class='leftDiv'>上传文件夹</div>");
out.println(" <div align='left' class='rightDiv'>" + uploadFolder + "</div>");
out.println("</div>"); out.println("<div class='line'>");
out.println(" <div align='left' class='leftDiv'>实际磁盘路径</div>");
out.println(" <div align='left' class='rightDiv'>" + servletContext.getRealPath(uploadFolder) + "</div>");
out.println("</div>"); out.println("<div class='line'>");
out.println(" <div align='left' class='leftDiv'>允许上传的类型</div>");
out.println(" <div align='left' class='rightDiv'>" + allowedFileType + "</div>");
out.println("</div>"); out.println("</fieldset></div>"); out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}

  运行效果如图:
Servlet学习记录2

  初始化参数与上下文参数只能配置简单的字符串类型的参数。如果需要配置更多更灵活的参数,更推荐把参数配置写到xml文件或者properties文件里,然后编写程序读取这些文件。

  资源注射(@Resource)

  上面的例子都是在Servlet里编写程序代码读取web.xml初始参数。Java EE 5提供了一种新的方案叫做资源注射(Resource Injection),或者叫资源注入。也就是说,不需要Servlet主动去读取资源,Tomcat启动的时候会把web.xml里配置的信息主动“注射”到Servlet里。这个过程是运行时自动完成的,不需要编写任何代码,不需要做任何工作。

  资源注射是通过注解(Annotation)完成的。注解是Java 5.0里引入的新特性。注解是一种特殊的接口,以“@”符号为标志。用法如下:

@Resource(name="messageNameInWebXml")
private String message;

  使用@Resource标注字符串变量message,表示message的值会在Servlet运行时动态注入。然后在web.xml中配置一个名为messageNameInWebXml的参数就可以了。注解以及变量可以写在一行代码中,看起来更简洁一些;

private @Resource(name="messageNameInWebXml") String message;

  看一个实例

package com.helloweenvsfei.servlet;

import java.io.IOException;
import java.io.PrintWriter; import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class InjectionServlet extends HttpServlet { private static final long serialVersionUID = -8526907492073769090L; // 注入的 字符串
private @Resource(name="hello") String hello;
// 注入的 整数
private @Resource(name="i") int i; // 注入更常见的写法
@Resource(name="persons")
private String persons; public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8"); response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>资源注入</TITLE></HEAD>");
out.println("<style>body {font-size:12px; }</style>"); out.println("<b>注入的字符串</b>:<br/>&nbsp;&nbsp;-&nbsp;" + hello + "<br/>");
out.println("<b>注入的整数</b>:<br/>&nbsp;&nbsp;-&nbsp;" + i + "<br/>");
out.println("<b>注入的字符串数组</b>:<br/>"); for(String person : persons.split(",")){
out.println("&nbsp;&nbsp;-&nbsp;" + person + "<br/>");
} out.println(" <BODY>");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
} }

  web.xml中使用标签<env-entry>来配置资源。<env-entry>仅能配置java.lang包下的标准类型的变量,如String,Integer,Double等。配置后的代码如下:

<env-entry>
<env-entry-name>hello</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>
Hello, Welcome to the JavaEE Resource Injection.
</env-entry-value>
</env-entry> <env-entry>
<env-entry-name>i</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>30</env-entry-value>
</env-entry> <env-entry>
<env-entry-name>persons</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>
Helloween, Cobain, Roses, Axl,
</env-entry-value>
</env-entry>

  程序运行效果:
Servlet学习记录2

  使用JNDI获取资源

  资源注射的工作原理是JNDI(Java命名与目录接口,Java Naming and Directory Interface)。InjectionServlet实例中使用<env-entry>配置了名为hello, i, persons的JNDI资源,然后使用@Resource将指定名称的JNDI资源注射到InjectionServlet里。

  如果不使用@Resource,通过查找JNDI同样可以获取到这三个资源,代码如下:

Context ctx = new InitialContext();   //实例化一个Context对象
String message = (String)ctx.lookup("message"); //查找资源message
Integer i = (Integer)ctx.lookup("i"); //查找资源i
String persons = (String)ctx.lookup("persons"); //查找资源persons

  注射数据源

  Servlet中不仅可以注射String,Integer等类型的变量,还可以注入自定义的Java Bean以及数据源等复杂类型变量。例如,下面的代码将会注射一个数据源。读者只需要在Tomcat中配置好数据源,然后使用下列代码就可以获取到数据源变量。这是相当方便的:

@Resource(name="dataBase") //声明数据源名称,数据源配置在Tomcat中
javax.sql.DataSource dataSource; //注射到dataSource属性上 public void getConnection(){ //方法中直接使用dataSource
Connection conn = dataSource.getConnection();
return conn;
}

  注射是利用@注解实现的。JDK5以上支持@注解。资源注射需要服务器的支持,Tomcat 6是支持的,但在某些其它Web服务器或者低版本的Tomcat可能不支持。