JAVA面试javaweb

Servlet入门教程

2018-07-15  本文已影响23人  夏海峰

Servlet入门教程 源码下载

什么是Servlet?

Servlet 为创建基于 web 的应用程序提供了基于组件、独立于平台的方法,可以不受 CGI 程序的性能限制。Servlet 有权限访问所有的 Java API,包括访问企业级数据库的 JDBC API。

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。

Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。Servlet 是独立于平台的,因为它们是用 Java 编写的。

Servlet架构是怎样的?

servlet-arch.jpg

Servlet有什么用?

  1. 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
    读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
  2. 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
  3. 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
  4. 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。

如何配置Java Servlet开发环境?

set JAVA_HOME=d:\jdk      // jdk安装路径
set PATH=D:\jdk\bin;%PATH%         // 指定二进制文件路径

set CATALINA_HOME=D:\tomcat    // tomcat安装路径
set CLASSPATH=D:\tomcat\lib\servlet-api.jar;%CLASSPATH%    // 把tomcat下的servlet包添加至classpath下

Tips: 环境变量CLASSPATH有什么用?

设置Classpath的目的,在于告诉Java执行环境,在哪些目录下可以找到您所要执行的Java程序所需要的类或者包。

什么是Servlet生命周期?

Servlet-LifeCycle.jpg

第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。Servlet 容器在调用 service() 方法之前加载 Servlet。然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。过程如下:

  1. Servlet 通过调用 init () 方法进行初始化,只调用一次。
  2. Servlet 调用 service() 方法来处理客户端的请求,它会检查HTTP请求类型,并在适当的时候调用 doGet() / doPost()等方法。service() 方法由容器调用,你不需要对service()做任何处理,你要做的是对 doGet() / doPost() 进行重写。
  3. Servlet 通过调用 destroy() 方法终止(结束)。
  4. 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用 destroy() 方法之后,servlet 对象被标记为可垃圾回收。

第一个Servlet程序

Tips:固定格式的项目目录结构


目录结构.png

第1步:编写Servlet程序代码

package com.geekxia;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorld extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();

        String message = request.getParameter("message");

        if (message == null) {
            message = "Hello Geekxia";
        }

        writer.println("<html><body>");
        writer.println("<h1>" + message + "</h1>");
        writer.println("</body></html>");
    }
}

第2步:编译HelloWorld.java文件

javac HelloWorld.java    // 编译前,请确保servlet-api.jar已经添加到了classpath环境变量下

第3步:配置web.xml部署描述符文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <display-name>Hello</display-name>
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>
            com.geekxia.HelloWorld
        </servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

第4步:把当前项目拷贝(部署)至Tomcat/webapps/ROOT下
第5步:启动Tomcat
第6步:在浏览器地址栏中输出 localhost:8090/hello 即可访问

Servlet接收并处理客户端的请求数据?

  1. getParameter():您可以调用 request.getParameter() 方法来获取表单参数的值。
  2. getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框。
  3. getParameterNames():如果您想要得到当前请求中的所有参数的完整列表,则调用该方法。
public class FormTest extends HttpServlet {
    public FormTest() {
        super();
    }
    
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        String title = "GeekXia";
        
        // 中文数据进行转码
        String name = new String(req.getParameter("name").getBytes("ISO8859-1"), "UTF-8");
        String url = req.getParameter("url");
        
        out.println("<!DOCTYPE html><html><head><title>" + title + "</title></head>\n" +
            "<body><h1>" + name + "</h1>" + "<h1>" + url + "</h1></body></html>");
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        doGet(req, res);    // 调用doGet()方法
    }
}

进一步学习 HttpServletRequest 类与请求对象

Tips:使用HttpServletRequest,读取HTTP请求头信息

public class Test extends HttpServlet {
    public Test() {
        super();
    }
    
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        
        // 读取HTTP头信息
        Enumeration headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) {          
            String key = (String)headerNames.nextElement();
            String value = req.getHeader(key);
            out.println("<div>");
            out.println("<span>" + key + "</span>" );           
            out.println("<span>" + value + "</span>");
            out.println("</div>");
        }
}

进一步学习 HttpServletResponse 类与响应对象

Tips:使用 HttpServletResponse,设置HTTP响应头信息

public class Test extends HttpServlet {
    public Test() {
        super();
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 设置HTTP响应头信息
        res.setIntHeader("Refresh", 5);
        res.setContentType("text/html;charset=UTF-8");
        Calendar cale = Calendar.getInstance();
        Date tasktime = cale.getTime();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String nowTime = df.format(tasktime);
        PrintWriter out = res.getWriter();
        out.println("<h1>"+ nowTime +"</h1>");
    }
}

Tips:使用 HttpServletResponse,设置响应状态码

public void setStatus( int statusCode );
public void sendRedirect( String url );
public void sendError( int code, String message );

什么是Servlet过滤器?

Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet。Servlet 过滤器也可以附加到 Java Server Pages (JSP) 文件和 HTML 页面。调用 Servlet 前调用所有附加的 Servlet 过滤器。

Servlet 过滤器是可用于 Servlet 编程的 Java 类。用于在客户端的请求访问后端资源之前,拦截这些请求。还用于在服务器的响应发送回客户端之前,处理这些响应。

过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。当 Web 容器启动 Web 应用程序时,它会为部署描述符中声明的每一个过滤器都创建一个实例。Filter的执行顺序与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。

Servlet过滤器常见的有:身份验证过滤器、数据压缩过渡器、加密过渡器、触发资源访问事件过滤器、图像转换过滤器、日志记录和审核过滤器、MIME-TYPE链过滤器、标记化过滤器、XSL/T过滤器等。

第1步:编写过滤器代码

public class TestFilter implements Filter {
    // 1 - 过滤器初始化
    public void init(FilterConfig config) throws ServletException {
        String site = config.getInitParameter("Site");
        System.out.println("网站名称:" + site);
    }
    // 2 - 过滤器执行阶段
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws java.io.IOException, ServletException {
        System.out.println("geekxia");
        chain.doFilter(req, res);
    }
    // 3 - 过滤器销毁阶段
    public void destroy() {}
}

第2步:在web.xml中配置一个或多个过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <filter>
        <filter-name>TestFilter</filter-name>
        <filter-class>com.geekxia.TestFilter</filter-class>
        <init-param>
            <param-name>Site</param-name>
            <param-value>geekxia.cn</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>TestFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

第3步:编译、部署、运行测试。

注:过滤器的 init() 方法中提供了一个 FilterConfig 对象。它结合部署描述符的 <init-param> 一起使用。

Servlet异常处理

当一个 Servlet 抛出一个异常时,Web 容器在使用了 <exception-type> 元素的 web.xml 中搜索与抛出异常类型相匹配的配置。你必须在 web.xml 中使用 <error-page> 元素来指定对特定异常 或 HTTP 状态码 作出相应的 Servlet 调用。

第1步:编写用于处理“错误和异常”的程序

public class ErrorHandle extends HttpServlet {
    
    public ErrorHandle() {
        super();
    }   
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 获取请求对象中的错误/异常信息
        Throwable throwable = (Throwable)req.getAttribute("javax.servlet.error.exception");
        Integer statusCode = (Integer)req.getAttribute("javax.servlet.error.status_code");
        String servletName = (String)req.getAttribute("javax.servlet.error.servlet_name");
        if (servletName == null) {
            servletName = "Unknown";
        }
        String reqUrl = (String)req.getAttribute("javax.servlet.error.request_uri");
        if (reqUrl == null) {
            reqUrl = "Unknown";
        }
        
        res.setContentType("text/html;charset=UTF-8");
        
        PrintWriter out = res.getWriter();
        out.println("<p>"+servletName+"</p>");
        out.println("<p>"+reqUrl+"</p>");
    }
}

第2步:在部署描述符对“错误或异常”进行相关配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"> 
    <servlet>
        <servlet-name>ErrorHandle</servlet-name>
        <servlet-class>com.geekxia.ErrorHandle</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ErrorHandle</servlet-name>
        <url-pattern>/ErrorHandle</url-pattern>
    </servlet-mapping>

    <error-page>
        <error-code>404</error-code>
        <location>/ErrorHandle</location>
    </error-page>
    <error-page>
        <exception-type>javax.servlet.ServletException</exception-type>
        <location>/ErrorHandle</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/ErrorHandle</location>
    </error-page>
</web-app>

对上述代码进行编译、部署后,即可测试。注意:<location>标签用于指定发生指定错误或异常时,要执行的程序逻辑位于哪个路径下,它与<servlet-mapping>中的<url-pattern>相对应。

Servlet Cookie

Tip1:Cookie的工作原理是怎样的?

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。Java Servlet 支持 HTTP Cookie。Cookie 通常设置在 HTTP 头信息中。
Cookie的工作原理如下:服务器脚本向浏览器发送一组 Cookie,例如:姓名、年龄或识别号码等;浏览器将这些信息存储在本地计算机上,以备使用;当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。
Tip2:创建Cookie、读取Cookie、删除Cookie

public class TestCookie extends HttpServlet {
    public TestCookie() {
        super();
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 从HTTP中读取Cookie
        Cookie[] cookies = null;        
        cookies = req.getCookies();
        
        if (cookies == null) {
            // 创建Cookie、设置Cookie有效期、添加Cookie至HTTP中
            Cookie name = new Cookie("name", "geekxia");
            name.setMaxAge(60*60*24);
            res.addCookie(name);
        } else {
            if ((cookies[0].getName()).compareTo("name") != 0) {
                PrintWriter out = res.getWriter();
                out.println("<p>" + cookies[0].getName() + " : " + cookies[0].getValue() + "</p>");
            }
            // 删除cookie时,只需把cookie的有效期设置为0即可实现
            // cookies[0].setMaxAge(0);
            // res.addCookie(cookies[0]);
        }
    }
}

Servlet Session

Tip1: 为什么需要Session来实现HTTP会话?

HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。

Servlet 提供了 HttpSession 接口,该接口提供了一种跨多个页面请求或访问网站时识别用户以及存储有关用户信息的方式。Servlet 容器使用这个接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,可跨多个连接或多个页面进行请求。

public class TestSession extends HttpServlet {
    public TestSession() {
        super();
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 如果不存在session会话,就创建一个session对象
        HttpSession session = req.getSession(true);
        // 使用session对象
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        out.println("<h1>"+ session.getId() +"</h1>");
    }
}

Tip2:在部署描述符中,还可设置Session的过期时间(Tomcat默认的session过期时间是30分钟)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

上述部署描述符中,设置了session的过期时间为15分钟。在一个 Servlet 中的 getMaxInactiveInterval() 方法会返回 session 会话的超时时间,以秒为单位。所以,如果在 web.xml 中配置的 session 会话已经超时,那么 getMaxInactiveInterval() 会返回 900。

Servlet如何访问数据库?

使用 mysql-connector-java-5.1.39-bin.jar 包,进行Servlet数据库访问与操作。

Tip1:创建数据库表

CREATE TABLE `geekxia` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '站点名称',
  `url` varchar(255) NOT NULL DEFAULT '',
  `alexa` int(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名',
  `country` char(10) NOT NULL DEFAULT '' COMMENT '国家',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

Tip2:Servlet访问数据库

public class DatabaseAccess extends HttpServlet {
    public DatabaseAccess() {
        super();
    }
    
    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    private static final String DB_URL = "jdbc:mysql://localhost:3306/GEEKXIA";
    private static final String USER = "root";
    private static final String PASS = "123456";
    
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        Connection conn = null;
        Statement stmt = null;
        
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        
        try {
            // 注册JDBC驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 创建一个数据库连接
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            // 执行SQL语句
            stmt = conn.createStatement();
            String sql = "SELECT id, name, url FROM geekxia";
            ResultSet rs = stmt.executeQuery(sql);
            
            // 从结果集中读取数据
            while(rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String url = rs.getString("url");
                
                out.println("<p>"+ id +"</p>" + "<p>"+ name +"</p>" + "<p>"+ url +"</p>");
            }
            // 完成后关闭
            rs.close();
            stmt.close();
            conn.close();
        } catch(SQLException e) {
            // 处理JDBC错误
            e.printStackTrace();
        } catch(Exception e) {
            // 处理Class.forName错误
            e.printStackTrace();
        } finally {
            // 关闭资源 
            try {
                if (stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
 }

Servlet文件上传

  1. 下载 commons-fileupload-1.3.2.jar / commons-io-2.5.jar 这两个jar包。并在 classpath 环境变量中加入这两个 jar包的路径。
  2. form表单,要使用 POST方式,enctype 属性应该设置为 multipart/form-data 。input元素的 type="file" 。

Tip1:编写文件上传程序,代码如下:

public class UploadFile extends HttpServlet {
    public UploadFile() {
        super();
    }
    // 指定上传目录
    private static final String UPLOAD_DIR = "upload";
    
    // 上传配置
    private static final int MEMORY_THRESHOLD = 1024*1024*3;
    private static final int MAX_FILE_SIZE = 1024*1024*40;
    private static final int MAX_REQUEST_SIZE = 1024*1024*50;
    
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        PrintWriter out = res.getWriter();
        // 检测是不是多媒体上传
        if (!ServletFileUpload.isMultipartContent(req)) {           
            out.println("表单必须包含 enctype=multipart/form-data");
            out.flush();
            return;
        }
        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        ServletFileUpload upload = new ServletFileUpload(factory);
        
        upload.setFileSizeMax(MAX_FILE_SIZE);
        upload.setSizeMax(MAX_REQUEST_SIZE);
        upload.setHeaderEncoding("UTF-8");
        
        String uploadPath = req.getServletContext().getRealPath("./")+File.separator + UPLOAD_DIR;
        
        // 如果目录不存在,就创建目录
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        try {
            List<FileItem> formItems = upload.parseRequest(req);
            if(formItems != null && formItems.size() > 0) {
                // 迭代表单数据
                for (FileItem item: formItems) {
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);    
                        // 保存文件至硬盘
                        item.write(storeFile);
                        req.setAttribute("message", "文件上传成功");
                    }
                }
            }
        } catch (Exception e) {
            req.setAttribute("message", "错误信息:"+e.getMessage());
        }
        // 跳转至新页面
        req.getServletContext().getRequestDispatcher("./success.html").forward(req, res);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>UploadFile</servlet-name>
        <servlet-class>
            com.geekxia.UploadFile
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadFile</servlet-name>
        <url-pattern>/UploadFile</url-pattern>
    </servlet-mapping>
</web-app>

编译、部署上述代码,即可实现文件上传。

Servlet处理日期和时间

  1. 使用 Servlet 的最重要的优势之一是,可以使用核心 Java 中的大多数可用的方法。本节将使用 java.util 包中的 Date 类,用来处理日期和时间。
  2. SimpleDateFormat 是一个以语言环境敏感的方式来格式化和解析日期的具体类。 SimpleDateFormat 允许您选择任何用户定义的日期时间格式化的模式。

Tip1:编写处理日期和时间的程序

public class TestDate extends HttpServlet {
    public TestDate() {
        super();
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        
        // 获取当前日期
        Date now = new Date();
        // 创建格式化对象
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss E a");
        out.println("<p>"+ now.toString() +"</p>");
        out.println("<p>"+ ft.format(now) +"</p>");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>TestDate</servlet-name>
        <servlet-class>
            com.geekxia.TestDate
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestDate</servlet-name>
        <url-pattern>/TestDate</url-pattern>
    </servlet-mapping>
</web-app>

Tip2:Date类有很多实用的 api,详见 java.util.Date 手册。关于"yyyy-MM-dd hh:mm:ss E a" 等参数的使用,详见 java.text.SimpleDateFormat 手册。

Servlet网页重定向

当文档移动到新的位置,我们需要向客户端发送这个新位置时,我们需要用到网页重定向。当然,也可能是为了负载均衡,或者只是为了简单的随机,这些情况都有可能用到网页重定向。

重定向请求到另一个网页的最简单的方式是使用 response 对象的 sendRedirect() 方法。该方法把响应连同状态码和新的网页位置发送回浏览器。您也可以通过把 setStatus() 和 setHeader() 方法一起使用来达到同样的效果。

Tip1:下面代码实现Servlet重定向

public class TestRedirect extends HttpServlet {
    public TestRedirect() {
        super();
    }
    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        String site = new String("http://evatsai.com");
        // 实现重定向,重定向到另一个网点 http://evatsai.com
        res.setStatus(res.SC_MOVED_TEMPORARILY);
        res.setHeader("Location", site);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>TestRedirect</servlet-name>
        <servlet-class>
            com.geekxia.TestRedirect
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestRedirect</servlet-name>
        <url-pattern>/TestRedirect</url-pattern>
    </servlet-mapping>
</web-app>

编译、部署上述代码,重启Tomcat 即可测试。

Servlet实现网站/网页的访问量统计

如何统计网站的某个特定页面上的总访问量?使用 Servlet 来统计访问量是非常简单的,因为一个 Servlet 的生命周期是由它运行所在的容器控制的。实现思路如下:

  1. 第1步:在 init() 方法中初始化一个全局变量。
  2. 第2步:每次调用 doGet() 或 doPost() 方法时,让这个全局变量 +1。
  3. 第3步:如果需要,在 destroy() 时,可以使用一个数据库表来存储这个全局变量的值。在下一次初始化 Servlet 时,再在 init() 方法内被读取它。
  4. 第4步:如果您只想对一个 session 会话计数一次页面点击,那么请使用 isNew() 方法来检查该 session 会话是否已点击过相同页面。
  5. 第5步:把这个全局计数器的值渲染到这个网页上,实时展示当前页面的总访问量。
public class Counter extends HttpServlet {
    public Counter() {
        super();
    }
    
    private int counter;
    // 在 init()方法中初始化这个计数器,或者从数据表中查询这个计数器并初始化
    public void init() {
        counter = 0;
    }
    // 在 doGet() 和 doPost() 中累加这个计数器
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        // 累加计数器
        counter++;
        PrintWriter out = res.getWriter();
        out.println("<p>"+ counter +"</p>");
    }
    public void destroy() {
        // 把这个计数器存储到数据表中
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>Counter</servlet-name>
        <servlet-class>
            com.geekxia.Counter
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Counter</servlet-name>
        <url-pattern>/Counter</url-pattern>
    </servlet-mapping>
</web-app>

如何统计整个网站的总访问量?在 Servlet 中,这也是非常简单的,我们可以使用过滤器来实现。实现思路如下:

  1. 在过滤器的 init() 方法中初始化一个全局变量。
  2. 每次调用 doFilter() 方法时,让这个全局变量 +1。
  3. 如果需要,可以在过滤器的 destroy() 中使用一个数据库表来存储这个全局变量的值。在下一次初始化过滤器时,再在 init() 方法中从数据库中读取这个计数器即可。
public class FilterCounter implements Filter {
    private int counter;
    
    // 在 init() 方法中初始化这个计数器
    public void init(FilterConfig config) throws ServletException {
        counter = 0;
    }
    // 在 doFilter() 方法中,累加这个计数器
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        // 访问量累加
        counter++;
        System.out.print("网站访问量:"+counter);
        // 把请求和响应,回传至过滤器链
        chain.doFilter(req, res);
    }
    public void destroy() {
        // 在这里,把计数器的值,写入数据库
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <filter>
        <filter-name>FilterCounter</filter-name>
        <filter-class>com.geekxia.FilterCounter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterCounter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

编译、部署上述代码,即可测试!

Servlet实现页面定时自动刷新

Java Servlet 提供了一个机制,使得网页会在指定的时间间隔自动刷新页面。刷新网页的最简单的方式是使用响应对象的方法 res.setIntHeader(),此方法把头信息 "Refresh" 连同一个表示时间间隔的整数值(以秒为单位)发送至客户端。常见场景有比赛成绩动态、股票动态或货币兑换率动态等网站。

public class AutoRefresh extends HttpServlet {
    public AutoRefresh() {
        super();
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 设置每 5 秒自动刷新一次页面
        res.setIntHeader("Refresh", 5);
        
        res.setContentType("text/html;charset=UTF-8");
        // 获取当前时间
        Calendar cal = new GregorianCalendar();
        String am_pm;
        int hour = cal.get(Calendar.HOUR);
        int min = cal.get(Calendar.MINUTE);
        int second = cal.get(Calendar.SECOND);
        if (cal.get(Calendar.AM_PM) == 0) {
            am_pm = "AM";
        } else {
            am_pm = "PM";
        }
        String CT = hour + ":" + min + ":" + second + " " + am_pm;
        
        PrintWriter out = res.getWriter();
        out.println("<h1>"+ CT +"</h1>");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>AutoRefresh</servlet-name>
        <servlet-class>
            com.geekxia.AutoRefresh
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AutoRefresh</servlet-name>
        <url-pattern>/AutoRefresh</url-pattern>
    </servlet-mapping>
</web-app>

编译、部署上述代码,即可测试!

Servlet发送电子邮件

使用 Servlet 发送一封电子邮件是很简单的。下载 activation.jar / mail.jar 这两个jar包,放置到 Tomcat/lib 目录下,并在 classpath 环境变量中追加这两个jar包的路径,使用这两个包可以发送普通邮件、HTML邮件、带附件的邮件等。示例代码如下:

public class Email extends HttpServlet {
    public Email() {
        super();
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 收件人
        String to = "108508992@qq.com";
        // 发件人
        String from = "448914712@qq.com";
        // 发送邮件的主机地址
        String host = "localhost";
        // 获取系统属性
        Properties props = System.getProperties();
        // 设置邮件服务器,并向邮件服务器提供用户验证
        props.setProperty("mail.smtp.host", host);
        props.setProperty("mail.user", "myuser");
        props.setProperty("mail.password", "mypwd");
        // 获取默认的 session 对象
        Session session = Session.getDefaultInstance(props);
        // 设置响应内容类型
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        
        try {
            // 创建一个默认的 MimeMessage 对象
            MimeMessage msg = new MimeMessage(session);
            // 设置 from
            msg.setFrom(new InternetAddress(from));
            // 设置 to
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // 设置 subject
            msg.setSubject("这是首行");
            // 创建消息体部分
            BodyPart msgBody = new MimeBodyPart();
            // 添加邮件内容
            msgBody.setText("这是邮件内容");
            // 创建一个多部分消息
            Multipart multi = new MimeMultipart();
            // 设置文本消息部分
            multi.addBodyPart(msgBody);
            // 附件
            msgBody = new MimeBodyPart();
            String filename = "file.txt";
            DataSource source = new FileDataSource(filename);
            msgBody.setDataHandler(new DataHandler(source));
            msgBody.setFileName(filename);
            multi.addBodyPart(msgBody);
            
            // 发送邮件
            msg.setContent(multi);
            Transport.send(msg);
            
            out.println("<h1>发送电子邮件成功</h1>");
        } catch(MessagingException e) {
            e.printStackTrace();
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>Email</servlet-name>
        <servlet-class>
            com.geekxia.Email
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Email</servlet-name>
        <url-pattern>/Email</url-pattern>
    </servlet-mapping>
</web-app>

编译上述代码,如果没有报错,则程序语法正确。上述代码仅为学习使用,部署、测试,是无法实现邮件发送的,因为上述代码并没有连接真实的邮件服务器。

Servlet包结构与部署规范

  1. 要严格遵守Servlet API指导规范,建议合理的项目包结构。如 /myapp/WEB-INF/classes/com/myorg/MyServlet.class 。
  2. 在类文件的首行,指定包名。如 package com.myorg;
  3. 使用 javac 工具对.java文件进行编译,请确保在编译前已经把项目中用到的第三方.jar包添加到了classpath环境变量中。
  4. 在部署描述符 web.xml 中,对各种功能进行部署。同时把编译好的代码文件,按照规范放置到 Tomcat 下。
  5. 执行 Tomcat 的 startup.bat / startup.sh 工具,对 Tomcat 进行启动。

Servlet调试

  1. System.out.println() 是作为一个标记来使用的,用来测试一段特定的代码是否被执行。我们也可以打印出变量的值。
  2. 使用适当的日志记录方法来记录所有调试、警告和错误消息,推荐使用 log4J 来记录所有的消息日志。ServletContext 把它的文本消息记录到 Servlet 容器的日志文件中。对于 Tomcat,这些日志可以在 <Tomcat-installation-directory>/logs 目录中找到。这些日志文件确实对新出现的错误或问题的频率给出指示。因此,建议在通常不会发生的异常的 catch 子句中使用 log() 函数。
  3. 您可以使用调试 applet 或应用程序的 jdb 命令(JDB 调试器)来调试 Servlet。
  4. 注释可用在调试过程中。使用 Java 注释和单行注释(//...),多行注释(/* ...*/)可用于暂时移除部分 Java 代码。
  5. 有时,当一个 Servlet 并没有像预期那样时,查看原始的 HTTP 请求和响应是非常有用的。如果您熟悉 HTTP 结构,您可以阅读请求和响应,看看这些头信息究竟是什么。

Servlet国际化之Locale对象

  1. 国际化(i18n):这意味着一个网站提供了不同版本的翻译成访问者的语言或国籍的内容。
  2. 本地化(l10n):这意味着向网站添加资源,以使其适应特定的地理或文化区域,例如网站翻译成印地文(Hindi)。
  3. 区域设置(locale):这是一个特殊的文化或地理区域。它通常指语言符号后跟一个下划线和一个国家符号。例如 "en_US" 表示针对 US 的英语区域设置。

如何通过差异化定位(即区域设置)来让网页以不同语言呈现? Servlet 可以根据请求者的区域设置获取相应版本的网站,并根据当地的语言、文化和需求提供相应的网站版本。使用 java.util.Locale req.getLocale() 方法来获取 Locale 对象,进一步使用该对象来检测请求者的地理位置、语言和区域设置。

public class GetLocale extends HttpServlet {
    public GetLocale() {
        super();
    }
    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        // 获取客户端的区域对象
        Locale loc = req.getLocale();
        
        // 获取当前区域的语言、国家
        String language = loc.getLanguage();
        String country = loc.getCountry();
        
        // 对当前区域的日期和时间进行格式化
        String date = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT, loc).format(new Date());
        
        // 对当前区域的货币格式化、百分比格式化
        NumberFormat nft = NumberFormat.getCurrencyInstance(loc);
        String formattedCurr = nft.format(1000000);
        String formattedPerc = nft.format(0.51);
        
        
        // 设置语言
        res.setHeader("Content-Language", "es");
        // 设置响应内容类型
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter out = res.getWriter();
        
        out.println("<h1>"+ language +"</h1>");
        out.println("<h1>"+ country +"</h1>");
        out.println("<h1>"+ date +"</h1>");
        out.println("<h1>"+ formattedCurr +"</h1>");
        out.println("<h1>"+ formattedPerc +"</h1>");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0">
    <servlet>
        <servlet-name>GetLocale</servlet-name>
        <servlet-class>
            com.geekxia.GetLocale
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>GetLocale</servlet-name>
        <url-pattern>/GetLocale</url-pattern>
    </servlet-mapping>
</web-app>

Locale loc = req.getLocale(); 获取 Locale对象,对当前区域的语言、国家、日期、时间、货币、百分比等进行格式化,这是国际化的基础。编译、部署上述代码,即可测试!

更多学习资源

Apache | Servlet | MySQL | Java SE | Sun Developer Network


完!!!
上一篇下一篇

猜你喜欢

热点阅读