官术网_书友最值得收藏!

6.2 Servlet應用

本節將介紹一些在Web開發中常用的Servlet技術。例如,在Servlet中生成驗證碼、將表格數據導出到Excel、避免客戶端訪問的并發問題以及訪問數據庫連接池等。

實例172 記錄用戶訪問次數

光盤位置:光盤\MR\06\172

初級

實用指數:

實例說明

在瀏覽網站時,有些網站會有計數器的功能,瀏覽者每訪問一次網站,計數器就累加一次。運行本實例,當用戶訪問時,實現記錄用戶的訪問次數,運行結果如圖6.9所示。

圖6.9 記錄用戶的訪問次數

關鍵技術

實現計數器主要是在Servlet中應用ServletContext接口,Servlet容器在啟動一個Web應用時,會為它創建一個ServletContext對象。當Servlet容器終止一個Web應用時,ServletContext對象也會被銷毀,所以該對象與Web應用程序有同樣的生命周期。也就是說,整個Web應用的組件可以共享ServletContext對象中存放的共享數據。在ServletContext接口中存取共享數據的方法包括以下幾種。

? setAttribute(String name,Object object):在ServletContext對象中存放共享數據,參數name表示屬性名,參數object表示屬性值。

? removeAttribute(String name):根據指定參數name屬性名,刪除ServletContext對象中的共享數據。

? getAttribute(String name):根據指定的參數name屬性,獲取ServletContext對象中的共享數據。

設計過程

(1)新建名為CounterServlet的Servlet類,在該類的doPost()方法中實現統計用戶的訪問次數,關鍵代碼如下:

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            ServletContext context=getServletContext();                 //獲得ServletContext對象
            Integer count=(Integer)context.getAttribute("counter");     //從ServletContext中獲得計數器對象
            if(count==null){                                            //如果為空,則在ServletContext中設置一個計數器的屬性
                  count=1;
                  context.setAttribute("counter", count);
            }else{                                                      //如果不為空,則設置該計數器的屬性值加1
                  context.setAttribute("counter", count+1);
            }
            response.setContentType("text/html");                      //響應正文的MIME類型
            response.setCharacterEncoding("UTF-8");                    //響應的編碼格式
            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("  <BODY>");
            out.print("    <h2><font color='gray'>");
            out.print("您是第  "+context.getAttribute("counter")+" 位訪客!");
            out.println("</font></h2>");
            out.println("  </BODY>");
            out.println("</HTML>");
            out.flush();
            out.close();
        }

(2)在web.xml中配置CounterServlet類,關鍵代碼如下:

        <servlet>
            <servlet-name>CounterServlet</servlet-name>
            <servlet-class>com.lh.servlet.CounterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>CounterServlet</servlet-name>
            <url-pattern>/counter</url-pattern>
        </servlet-mapping>

秘笈心法

在實際開發應用中,經常需要通過ServletContext對象來訪問Web應用的各種資源。通過ServletContext對象可以實現以下操作:

(1)在Web應用范圍內存取共享數據。

(2)訪問當前Web應用的資源。

(3)訪問Servlet容器中的其他Web應用。

(4)訪問Servlet容器的相關信息。

(5)訪問服務器端的文件系統資源。

(6)向Servlet的日志文件中輸出日志。

實例173 將數據導出到Excel

光盤位置:光盤\MR\06\173

初級

實用指數:

實例說明

在實際的開發中,經常需要將一些數據導出到Excel或者Word來進行處理。本實例將介紹如何使用POI開源組件實現將數據導出到Excel文件中。運行本實例,輸入用戶注冊信息后,單擊“導出到Excel”按鈕后,會將用戶的信息導出到Excel文件中,如圖6.10所示。

圖6.10 將用戶注冊的信息導出到Excel中

關鍵技術

實現本實例的關鍵使用的是開源的POI組件,該組件中包含實現對Excel文件的創建和寫入操作的類。使用POI組件操作Excel文件的關鍵步驟如下:

(1)創建Excel的工作表。POI組件的HSSFWorkbook類提供了創建工作表的方法,語法格式如下:

        public HSSFSheet createSheet(String sheetname) //參數sheetname表示工作表的名稱

(2)創建表格的行。在保存之前需要創建行對象,該對象由工作表對象創建,語法格式如下:

        public HSSFRow createRow(int rownum) //參數rownum表示工作表中行對象的行號

(3)創建表格的單元格。Excel表格中的數據由多個單元格組成,在POI組件中,這些單元格對象由HSSF Row類的createRow()方法創建,語法格式如下:

        public HSSFCell createCell(int columnIndex)//參數columnIndex表示單元格對象的列編號

(4)寫入單元格內容。單元格對象定義了寫入各種類型數據的方法,其中最常用的就是String類型的字符串數據。本實例使用最多的也是這個方法,語法格式如下:

        public void setCellValue(String value)//參數value表示保存在Excel單元格中的數據

設計過程

(1)新建用戶注冊表單頁index.jsp,關鍵代碼如下:

        <form action="export"method="post">
              <table align="center">
                  <tr>
                      <td>用戶名:</td><td><input type="text"name="name"/></td>
                  </tr>
                      …由于篇幅有限,此處省略了表單中的其他元素
                  <tr>
                      <td colspan="2"align="center">
                            <input type="submit"value="導出到Excel"/>
                      </td>
                  </tr>
              </table>
        </form>

(2)新建名為ExportServlet的Servlet類,在該類的doPost()方法中獲得用戶注冊信息,然后使用POI組件中的類將用戶注冊信息導出到Excel文件中,關鍵代碼如下:

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.ms-excel"); //響應正文的MIME類型,表示Excel
            response.addHeader("Content-Disposition", "attachment; filename=logininfo.xls");
            String name = request.getParameter("name");
            String pwd =request.getParameter("pwd");
            String sex = request.getParameter("sex");
            String age = request.getParameter("age");
            String email = request.getParameter("email");
            ServletOutputStream out=response.getOutputStream();     //響應輸出流對象
            HSSFWorkbook wb=new HSSFWorkbook();                     //創建Excel表格
            HSSFSheet  sheet=wb.createSheet("用戶注冊信息");          //創建工作簿
            sheet.setColumnWidth(4,5000);                           //設置列寬
            HSSFRow titleRow=sheet.createRow(0);                    //創建Excel中的標題行
            HSSFCell titleCell1=titleRow.createCell(0);             //在行中創建第1個單元格
            titleCell1.setCellValue("用戶姓名");                      //設置第1個單元格的值
            HSSFCell titleCell2=titleRow.createCell(1);             //在行中創建第2個單元格
            titleCell2.setCellValue("密碼");                         //設置第2個單元格的值
            HSSFCell titleCell3=titleRow.createCell(2);             //在行中創建第3個單元格
            titleCell3.setCellValue("性別");                         //設置第3個單元格的值
            HSSFCell titleCell4=titleRow.createCell(3);              //在行中創建第4個單元格
            titleCell4.setCellValue("年齡");                         //設置第4個單元格的值
            HSSFCell titleCell5=titleRow.createCell(4);              //在行中創建第5個單元格
            titleCell5.setCellValue("Email");                        //設置第5個單元格的值
            HSSFRow valueRow=sheet.createRow(1);                     //創建第2行
            HSSFCell nameCell=valueRow.createCell(0);                //在第2行中創建單元格
            nameCell.setCellValue(name);
            HSSFCell pwdCell = valueRow.createCell(1);
            pwdCell.setCellValue(pwd);
            HSSFCell sexCell = valueRow.createCell(2);
            sexCell.setCellValue(sex);
            HSSFCell ageCell = valueRow.createCell(3);
            ageCell.setCellValue(age);
            HSSFCell emailCell = valueRow.createCell(4);
            emailCell.setCellValue(email);
            HSSFCellStyle cellStyle = wb.createCellStyle();
            wb.write(out);                                       //將響應流輸入到Excel表格中
            out.flush();
            out.close();
        }

(3)在web.xml中配置ExportServlet類,具體代碼請參考實例172,只需要把實例172中的CounterServlet類換成本實例中的ExportServlet類即可。

秘笈心法

在Servlet中,通過HttpServletResponse對象的setContextType()方法設置不同MIME(Multipurpose Internet Mail Extension,多用途Internet郵件擴展)類型,Servlet可以生成不同類型的響應文件。例如,Word文檔、Excel文檔、XML文檔、圖像等。

實例174 利用Servlet生成動態驗證碼

光盤位置:光盤\MR\06\174

高級

實用指數:

實例說明

如今,絕大多數網站或者Web應用程序都實現了驗證碼的功能,加入驗證碼可以防止黑客利用惡意程序,在網站中進行頻繁登錄、注冊、灌水等操作。運行本實例,在用戶注冊的表單中包含一個驗證碼圖片和文本框,運行效果如圖6.11所示。

圖6.11 利用Servlet生成的驗證碼

關鍵技術

在Servlet中,首先要設置響應正文的類型為image/jpeg,表示響應的是一個圖片,然后通過java.awt包中的操作圖形圖像的類來生成一個圖像,主要用到以下幾個類。

? java.awt.image.BufferedImage:創建該對象時,會在緩存中構造一個圖像。

? java.awt.Graphics:該類的對象表示一個畫筆,可以用它來畫矩形、寫字、添加顏色、畫線等。

? java.util.Random:該類的對象用于生成隨機數。本實例中使用它來生成圖像上的隨機坐標的值、隨機顏色的值以及隨機的驗證碼數字。

設計過程

(1)新建名為ValidateCodeServlet的Servlet類,在該類的doPost()方法中實現生成驗證碼圖片,關鍵代碼如下:

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
              //禁止頁面緩存
              response.setHeader("Pragma", "No-cache");
              response.setHeader("Cache-Control", "No-cache");
              response.setDateHeader("Expires",0);
              response.setContentType("image/jpeg");                         //設置響應正文的MIME類型為圖片
              int width=60, height=20;
              /**創建一個位于緩存中的圖像,寬度為60,高度為20*/
              BufferedImage image=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
              Graphics g=image.getGraphics();                                //獲取用于處理圖形上下文的對象,相當于畫筆
              Random random=new Random();                                    //創建生成隨機數的對象
              g.setColor(getRandomColor(200,250));                           //設置圖像的背景色
              g.fillRect(0,0, width, height);                                //畫一個矩形,坐標(0,0),寬度為60,高度為20
              g.setFont(new Font("Times New Roman", Font.PLAIN,18));         //設定字體格式
              g.setColor(getRandomColor(160,200));
              for(int i=0; i<130; i++){                                      //產生130條隨機干擾線
                  int x = random.nextInt(width);
                  int y = random.nextInt(height);
                  int xl = random.nextInt(12);
                  int yl = random.nextInt(12);
                  g.drawLine(x, y, x+xl, y+yl);                              //在圖像的坐標(x, y)和坐標(x+x1, y+y1)之間畫干擾線
              }
              String strCode="";
              for (int i=0; i<4; i++){
                  String strNumber=String.valueOf(random.nextInt(10));
                      strCode=strCode+strNumber;
                  //設置字體的顏色
                  g.setColor(new Color(15+random.nextInt(120),15+random.nextInt(120),15+random.nextInt(120)));
                  g.drawString(strNumber,13*i+6,16);                       //將驗證碼依次畫到圖像上,坐標(x=13*i+6, y=16)
              }
              request.getSession().setAttribute("Code", strCode);          //把驗證碼保存到Session中
              g.dispose();                                                 //釋放此圖像的上下文以及它使用的所有系統資源
              ImageIO.write(image, "JPEG", response.getOutputStream());    //輸出JPEG格式的圖像
              response.getOutputStream().flush();                          //刷新輸出流
              response.getOutputStream().close();                          //關閉輸出流
        }

(2)在ValidateCodeServlet中添加一個獲取隨機顏色的方法,關鍵代碼如下:

        public  Color getRandomColor(int fc, int bc){
            Random random = new Random();
            Color randomColor = null;
            if(fc>255) fc=255;
            if(bc>255) bc=255;
            //設置0~255之間的隨機顏色值
            int r=fc+random.nextInt(bc-fc);
            int g=fc+random.nextInt(bc-fc);
            int b=fc+random.nextInt(bc-fc);
            randomColor = new Color(r, g, b);
            return randomColor;                                 //返回具有指定紅色、綠色和藍色值的不透明的sRGB顏色
        }

(3)新建用戶注冊表單頁index.jsp,在該頁的表單中使用<img>標簽的src屬性來調用ValidateCodeServlet類生成驗證碼,關鍵代碼如下:

        <form action=""method="post">
          <table align="center">
                  <tr>
                      <td>用戶名:</td><td><input type="text"name="name"/></td>
                  </tr>
                  ……                                         //省略了其他注冊信息的表單元素
                  <tr>
                      <td>驗證碼:</td><td><img alt=""src="validatecode"></td>
                  </tr>
                  <tr>
                      <td>輸入驗證碼:</td><td><input type="text"name="code"/></td>
                  </tr>
                  <tr>
                      <td colspan="2"align="center">
                            <input type="submit"value="注冊"/><input type="reset"value="重置"/>
                      </td>
                  </tr>
              </table>
        </form>

秘笈心法

由于服務器端的Servlet生成的圖像首先是存放在緩存中的,為了使客戶端獲取到最新的圖像,避免客戶端通過緩存獲取圖像,應該在Servlet中通過設置特定HTTP響應頭來禁止客戶端緩存頁面。在Servlet中,禁止客戶端緩存頁面的具體代碼如下:

        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "No-cache");
        response.setDateHeader("Expires", 0);

實例175 避免客戶端訪問的并發問題

光盤位置:光盤\MR\06\175

高級

實用指數:

實例說明

在Web應用程序開發或者網站開發中,一個Web應用可能會存在多個客戶同時訪問的情況,甚至可能同時訪問同一個Servlet,如果程序沒有及時地處理并發問題,可能會返回給客戶端錯誤的信息。本實例將講解如何避免客戶端的并發問題。運行本實例,如圖6.12所示,同時打開兩個瀏覽器,在表單中輸入同樣的數字,單擊“等于”按鈕提交之后,其中一個提交之后返回的值出現錯誤。

圖6.12 客戶端并發訪問出現的錯誤

關鍵技術

避免客戶端并發的問題主要有以下兩個解決辦法:

(1)合理決定在Servlet中定義的變量的作用域。確定變量是全局變量還是局部變量,如果定義錯誤,客戶端并發訪問時可能會出現問題。

(2)多個線程同時訪問共享數據而導致并發問題時,應該使用Java同步機制對多線程進行同步。Java同步機制確保在任意時刻,只允許有一個線程執行共享數據的代碼塊,只有當這個線程退出同步代碼塊時,其他線程才允許執行同步代碼塊,因此可以避免并發所帶來的問題。

設計過程

(1)新建名為SynchronizationServlet的Servlet類,在該類中定義一個全局變量,在doPost()方法中使用synchronized關鍵字避免并發問題,關鍵代碼如下:

        private int  sum=100; //定義一個全局變量
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");
            String num2 = request.getParameter("num2");
            int n2=Integer.parseInt(num2);
            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            out.println("<! DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
            out.println("<HTML>");
            out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
            out.println("  <BODY>");
            synchronized(this){           //同步以下代碼塊,同步之后,某一時刻只有當前線程可以訪問
                  out.println(sum+"+"+n2+"=");
                  try{
                      Thread.sleep(3000); //使當前線程休眠3000毫秒,然后繼續執行
                  }catch(Exception ex){
                      ex.printStackTrace();
                  }
                  sum += n2;
                  out.println(sum);
            }
            out.println("  </BODY>");
            out.println("</HTML>");
            out.flush();
            out.close();
        }

(2)在web.xml中配置SynchronizationServlet類,關鍵代碼如下:

        <servlet>
            <servlet-name>SynchronizationServlet</servlet-name>
            <servlet-class>com.lh.servlet.SynchronizationServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>SynchronizationServlet</servlet-name>
            <url-pattern>/synchronization</url-pattern>
        </servlet-mapping>

秘笈心法

在實際的開發應用過程中,應該考慮到客戶端并發訪問所帶來的問題,解決并發問題的辦法主要是合理決定在Servlet中定義的變量的作用域以及使用Java同步機制對線程進行同步。

實例176 在Servlet中使用JDBC訪問數據庫

光盤位置:光盤\MR\06\176

初級

實用指數:

實例說明

JDBC是Java API中對數據庫操作的重要組件,也是Java程序訪問數據庫的標準接口。為了幫助程序員編寫可以在不同數據庫引擎之間通用的代碼,在Java API中提供了訪問數據庫的JDBC API,使用JDBC中提供的接口和類可以在任何關系數據庫中以相同的方式執行SQL語句。本實例將介紹如何在Servlet中通過JDBC技術來訪問數據庫。運行本實例,如圖6.13所示,在表單頁中輸入用戶注冊信息,單擊“注冊”按鈕后會將信息保存到數據庫中。

圖6.13 將用戶注冊信息保存到數據庫中

關鍵技術

java.sql包中提供了用于連接數據庫的JDBC API,使用JDBC連接不同的數據庫需要加載不同數據庫廠商所提供的數據庫驅動類,具體的驅動類包可以到數據庫廠商的官方網站進行下載。本實例使用的是MySQL數據庫,該數據庫的驅動包文件可以到MySQL官方網站進行下載。使用JDBC連接數據庫的步驟如下。

(1)注冊加載數據庫連接的驅動程序。加載數據庫驅動程序使用的是Class.forName()方法,調用此方法會將指定的類加載到JVM中,關鍵代碼如下:

        Class.forName("com.mysql.jdbc.Driver");

(2)設置訪問數據庫連接URL,關鍵代碼如下:

        String url = "jdbc:mysql://localhost:3306/db_database06"; //連接URL

(3)建立數據庫連接。通過JDBC API中DriverManager類的getConnection()方法來創建與數據庫之間的連接,getConnection()方法需要接收3個String類型的參數,分別是連接URL、用戶名以及密碼,關鍵代碼如下:

        Connection  con=DriverManager.getConnection(url, user, pwd);

(4)建立連接之后,使用數據庫連接對象Connection創建用戶操作SQL語句的Statement對象,關鍵代碼如下:

        Statement stmt = con.createStatement();

也可以用Connection對象創建PreparedStatement對象來執行SQL語句,使用的是prepareStatement()方法,關鍵代碼如下:

        PreparedStatement pstmt = con.prepareStatement("select * from tb_user");

(5)通過Statement對象的execute()方法,編譯執行sql語句,關鍵代碼如下:

        String sql="update from tb_user set age=30 where userId=1";
        stmt.execute (sql);

(6)關閉數據庫連接。數據庫用完后要及時關閉與數據庫之間的連接,釋放與數據庫關聯的資源,關鍵代碼如下:

      con.close(); //關閉連接

設計過程

(1)新建用戶注冊表單頁index.jsp,關鍵代碼如下:

        <form action="login"method="post">
              <table align="center">
                  <tr>
                      <td>用戶名:</td><td><input type="text"name="name"/></td>
                  </tr>
                  <tr>
                      <td>密碼:</td><td><input type="password"name="pwd"/></td>
                  </tr>
        ……       //此處省略了表單中其他選項
                  <tr>
                      <td colspan="2"align="center">
                            <input type="submit"value="注冊"/><input type="reset"value="重置"/>
                      </td>
                  </tr>
              </table>
        </form>

(2)新建名為UserInfo的JavaBean類,用于封裝用戶的注冊信息,關鍵代碼如下:

        public class UserInfo {
        private String id;                                                //用戶編號
        private String name;                                              //用戶名
        private String pwd;                                               //密碼
        private String sex;                                               //性別
        private int age;                                                  //年齡
        private String email;                                             //Email
        public UserInfo(){}
        ……  //此處省略了屬性的getXXX()和setXXX()方法
        }

(3)在Servlet中使用JDBC訪問數據庫。可以將數據庫連接的代碼單獨寫成一個類,這樣在其他的功能模塊中可以實現代碼的重用。新建名為MySQLDBCon的類,該類主要包含一個建立數據庫連接的方法,關鍵代碼如下:

        public class MySQLDBCon {
        private static Connection conn=null;
        public static Connection getConn(){
              try{
                  Class.forName("com.mysql.jdbc.Driver");                    //加載數據庫驅動類
                  String user="root";                                        //用戶名
                  String pwd="111";                                          //密碼
                  String url="jdbc:mysql://localhost:3306/db_database06";    //連接URL
                  conn=DriverManager.getConnection(url, user, pwd);          //創建數據庫連接
              }catch(Exception ex){
                  ex.printStackTrace();
              }
              return conn;
        }
        }

(4)新建名為LoginDao的類,該類中包含一個保存用戶注冊信息到數據庫的方法和一個獲得本類實例的靜態方法,關鍵代碼如下:

        public class LoginDao {
        private static LoginDao instance=null;
        public static LoginDao getInstance(){
            if(instance==null){
                  instance=new LoginDao();
            }
            return instance;
        }
        //保存用戶注冊信息
        public boolean saveUser(UserInfo user){
            Connection conn = null;
            try{
                  conn=MySQLDBCon.getConn();                                                                 //建立數據庫連接
                  String sql="insert into tb_userinfo(name, pwd, sex, age, email)values(? , ? , ? , ? , ? )";   //insert SQL語句
                  PreparedStatement pstmt = conn.prepareStatement(sql);
                  pstmt.setString(1, user.getName());                                                        //為SQL語句第1個參數賦值
                  pstmt.setString(2, user.getPwd());                                                         //為SQL語句第2個參數賦值
                  pstmt.setString(3, user.getSex());                                                         //為SQL語句第3個參數賦值
                  pstmt.setInt(4, user.getAge());                                                            //為SQL語句第4個參數賦值
                  pstmt.setString(5, user.getEmail());                                                       //為SQL語句第5個參數賦值
                  pstmt.executeUpdate();                                                                     //執行insert語句
                  return true;
            }catch(Exception ex){
                  ex.printStackTrace();
            }finally{
                  try {
                      conn.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
            }
            return false;
        }
        }

(5)新建名為LoginServlet的Servlet類,在該類的doPost()方法中獲得表單提交的用戶注冊信息,并將這些信息封裝到用戶信息類UserInfo中,然后調用LoginDao中的方法將注冊信息保存到數據庫中,關鍵代碼如下:

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");                                        //設置請求字符編碼格式
            /**以下是獲得表單提交過來的值*/
            String name = request.getParameter("name");
            String pwd= request.getParameter("pwd");
            String sex = request.getParameter("sex");
            String age = request.getParameter("age");
            int userAge =0;
            if(age! =null&&! age.equals("")){
                  userAge=Integer.parseInt(age);
            }
            String email = request.getParameter("email");
            /**以下代碼將獲得的表單值封裝到用戶信息對象中*/
            UserInfo user = new UserInfo();
            user.setName(name);
            user.setPwd(pwd);
            user.setSex(sex);
            user.setAge(userAge);
            user.setEmail(email);
            boolean res=LoginDao.getInstance().saveUser(user);                             //將用戶注冊信息保存到數據庫
            ……//此處省略了一些非關鍵代碼
        }

秘笈心法

在本實例中,將獲取數據庫連接的代碼、表單數據以及數據庫的操作都封裝在單獨的類中,而Servlet只是負責調用這些類,這樣便充分地體現了Servlet作為控制器(Controller)角色的作用。

實例177 利用Servlet訪問數據庫連接池

光盤位置:光盤\MR\06\177

高級

實用指數:

實例說明

由于建立數據庫連接需要消耗大量的系統資源,頻繁創建數據庫連接會大大降低應用程序的性能,針對這一問題,可以使用數據庫連接池,通過它可以提高訪問數據庫的效率。以實例176為基礎,本實例將介紹如何利用Servlet訪問數據庫連接池。

關鍵技術

數據庫連接池的工作原理是:首先會在連接池中建立一定數量的數據庫連接,當程序需要訪問數據庫時,會從池中取出一個空閑連接,然后會將此連接鎖定,標記為“忙”;當本次連接執行結束時,會將此連接放回連接池,并標記為“空閑”;當池中沒有空閑連接時,池驅動程序會根據配置再創建指定數目的數據庫連接。

可以在Tomcat服務器中配置數據庫的連接池,由于Tomcat服務器的版本不同,具體的連接池配置也不相同,本實例是按照Tomcat 7來配置的,具體配置過程請參見設計過程。

設計過程

(1)在Tomcat服務器的安裝文件目錄中,包含一個conf文件夾,該文件夾中有一個名為context.xml的文件,主要是在該文件的<Context>元素中配置數據庫的連接池,具體配置如下:

        <Context  path="/mysql"reloadable="true">
        <! --
              name:             指定Resource的JNDI名字
              type:             指定Resource所屬的Java類名
              auth:             指定管理Resource的Manager, Container表示由容器來管理Resource
              maxActice:        處于活動狀態的數據庫連接的最大數目
              maxIdle:          處于空閑狀態的數據庫連接的最大數目
              maxWait:          指定數據庫連接池中處于空閑狀態連接的最長時間,以毫秒為單位
              username:         連接數據庫的用戶名
              password:         連接數據庫的密碼
              driverClassName:  連接數據庫的JDBC驅動程序
              url:              連接數據庫的URL
         -->
        <Resource name="jdbc/mysql"auth="Container"
                type="javax.sql.DataSource"
                  maxActive="100"
                  maxIdle="30"
                  maxWait="10000"
                  username="root"
                  password="111"
                  driverClassName="com.mysql.jdbc.Driver"
                  url="jdbc:mysql://localhost:3306/db_database06"/>
            <ResourceLink global="jdbc/mysql"name="jdbc/mysql"type="javax.sql.DataSource"/>
            <! -- Default set of monitored resources -->
            <WatchedResource>WEB-INF/web.xml</WatchedResource>
        </Context>

(2)配置完數據庫連接池后,需要將MySQL數據庫的驅動程序文件復制到Tomcat安裝目錄的lib文件夾中。

(3)建立數據庫連接的MySQLDBCon類,關鍵代碼如下:

        public class MySQLDBCon {
        private static Connection conn=null;
        public static Connection getConn(){
              try{
                  //通過這個類訪問Tomcat配置文件Context.xml中指定的JNDI數據源
                  InitialContext ctx = new InitialContext();
                  //使用lookup()方法查找匹配的JNDI名字,獲得數據源
                  DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mysql");
                  //從數據源中獲得數據庫連接
                  conn=ds.getConnection();
              }catch(Exception ex){
                  ex.printStackTrace();
              }
              return conn;
        }
        }

秘笈心法

JNDI(Java Naming and Directory Interface),可以簡單地把JNDI理解為一種將對象和名字綁定的技術,對象工廠負責生產出對象,這些對象都和唯一的JNDI名字綁定,外部程序通過JNDI名字來獲取某個對象的引用。例如,在本實例中,在Tomcat容器中構造一個數據源,即javax.sql.DataSource的實例,然后把它發布為JNDI資源,名稱為jdbc/mysql,最后在Java應用中通過JNDI API中的javax.nameing.Context接口來獲取這個數據源的引用。

實例178 Servlet實現的個人所得稅計算器

光盤位置:光盤\MR\06\178

初級

實用指數:

實例說明

本實例將介紹如何通過Servlet來實現個人所得稅的計算。運行本實例,如圖6.14所示,在文本框中輸入收入金額和起征金額,單擊“計算個稅”按鈕,將調用Servlet計算個人所得稅并顯示結果。

圖6.14 個人所得稅計算器

關鍵技術

工資、薪金的個人所得稅計算公式為:(收入金額-起征點金額)×稅率-速算扣除數。不同的工資范圍其稅率和速算扣除數也不同。薪資在扣除起征點金額之后的范圍的稅率以及速算扣除數如表6.2所示。

表6.2 薪資在扣除起征點金額之后的范圍的稅率和速算扣除數

設計過程

(1)新建表單頁index.jsp,關鍵代碼如下:

        <form action="incometax"method="post">
                  <table>
                      <tr>
                            <td>收入金額:</td>
                            <td>
                                <input type="text"name="laborage"  />元
                            </td>
                      </tr>
                      <tr>
                            <td>起征金額:</td>
                            <td>
                                <input type="text"name="startpoint"value="2000"/>元
                            </td>
                      </tr>
                      <tr>
                            <td align="center"colspan="2">
                                <input type="submit"  value="計算個稅"/>
                            </td>
                      </tr>
                  </table>
        </form>

(2)新建名為IncomeTaxServlet的Servlet類,該類中實現計算個人所得稅的方法,在doPost()方法中根據獲得的收入金額和起征金額來計算個人所得稅,關鍵代碼如下:

        //計算個人所得稅
        public double getTax(double charge){
            double tax = 0;
            if(charge<=0){
                  tax = 0;
            }else if(charge>0&&charge<=500){
                  tax = charge*0.05;
            }else if(charge>500&&charge<=2000){
                  tax = charge*0.1-25;
            }else if(charge>2000&&charge<=5000){
                  tax = charge*0.15-125;
            }else if(charge>5000&&charge<=20000){
                  tax = charge*0.2-375;
            }else if(charge>20000&&charge<=40000){
                  tax = charge*0.25-1375;
            }else if(charge>40000&&charge<=60000){
                  tax = charge*0.30-3375;
            }else if(charge>60000&&charge<=80000){
                  tax = charge*0.35-6375;
            }else if(charge>80000&&charge<=100000){
                  tax = charge*0.4-10375;
            }else if(charge>100000){
                  tax = charge*0.45-15375;
            }
            return tax;
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            double laborage=Double.parseDouble(request.getParameter("laborage"));             //獲得表單提交的工資收入
            double startPoint=Double.parseDouble(request.getParameter("startpoint"));         //獲得表單提交的征稅起點金額
            double  myTax=this.getTax(laborage-startPoint);                                   //調用計算個人所得稅的方法
            request.setAttribute("Tax", myTax);                                               //將個人所得稅的值保存在請求中
            request.getRequestDispatcher("result.jsp").forward(request, response);            //請求轉發到result.jsp頁
        }

(3)在web.xml中配置IncomeTaxServlet類,關鍵代碼如下:

        <servlet>
            <servlet-name>IncomeTaxServlet</servlet-name>
            <servlet-class>com.lh.servlet.IncomeTaxServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>IncomeTaxServlet</servlet-name>
            <url-pattern>/incometax</url-pattern>
        </servlet-mapping>

(4)新建result.jsp頁,從請求范圍內取出個人所得稅的計算結果并顯示,關鍵代碼如下:

        <table>
              tr>
                  <td>您應交納的個人所得稅為:</td>
                  <td>
                      <%=request.getAttribute("Tax").toString() %>元
                  </td>
              </tr>
        </table>

秘笈心法

本實例實現的只是工資、薪金的個人所得稅計算,而實際的個人所得稅還包括很多種,例如,個體工商戶的生產經營所得稅、企業單位的承包經營所得稅、勞動報酬所得稅等,其具體的稅率算法也有所差異。如果讀者對此感興趣,可以查閱相關資料進行了解,此處不再詳細介紹。

實例179 利用Servlet實現用戶永久登錄

光盤位置:光盤\MR\06\179

高級

實用指數:

實例說明

在訪問一些網站時,用戶在登錄網站之后,網站會將該用戶信息保存一段時間,當該用戶再訪問該網站時,不需要輸入用戶名和密碼就會自動進入登錄狀態。運行本實例,如圖6.15所示,輸入賬號和密碼,然后選擇有效期為“30天內有效”,單擊“登錄”按鈕之后,該賬號會保存30天,再次訪問時不必登錄,會直接進入登錄狀態,只有單擊“注銷登錄”超鏈接時,該用戶的登錄狀態才會失效。

圖6.15 利用Servlet實現用戶永久登錄

關鍵技術

本實例主要是在Servlet中通過Cookie技術來實現的。首先在Servlet中獲得用戶輸入的賬號、密碼和有效期,然后將賬號信息保存在Cookie中,并設置該Cookie的最大保存時間,然后將此Cookie保存在客戶端的Cookie中。

本實例的實現還用到了MD5加密技術。考慮到賬號密碼的安全性,由于不能將密碼保存在Cookie中,因此可以在Servlet中,通過MD5加密算法將用戶賬號生成一個密鑰并保存在Cookie中,然后在用戶登錄頁中,就可以根據該密鑰來判斷頁面顯示的是用戶登錄還是登錄后的狀態。MD5加密是通過java.security.Message Digest類實現的,可以使用MD5或SHA字符串類型的值作為參數來構造一個MessageDigest類對象,并使用update()方法更新該對象,最后通過digest()方法完成加密運算,代碼如下:

        String pwd="123456";
        MessageDigest md=MessageDigest.getInstance("MD5");     //創建具有指定算法名稱的摘要
        md.update(pwd.getBytes());                             //使用指定的字節數組更新摘要
        byte mdBytes[]=md.digest();                            //進行哈希計算并返回一個字節數組

設計過程

(1)新建名為MakeMD5的類,該類實現了將字符串轉換為MD5值的方法,關鍵代碼如下:

        public class MakeMD5 {
        public final static String getMD5(String str){
              char hexDiagitArr[] ={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
        'c', 'd', 'e', 'f'};
              MessageDigest digest = null;
              try{
                  digest=MessageDigest.getInstance("MD5");      //創建MD5算法摘要
                  digest.update(str.getBytes());                //更新摘要
                  byte mdBytes[]=digest.digest();               //加密并返回字節數組
                  //新建字符數組,長度為myBytes字節數組的2倍,用于保存加密后的值
                  char newCArr[] = new char[mdBytes.length*2];
                  int k=0;
                  for(int i=0; i<mdBytes.length; i++){           //循環字節數組
                      byte byte0=mdBytes[i];                     //獲得每一個字節
                      newCArr[k++] = hexDiagitArr[byte0 >>> 4 &0xf];
                      newCArr[k++] = hexDiagitArr[byte0 & 0xf];
                  }
                  return String.valueOf(newCArr);               //返回加密后的字符串
              }catch(Exception ex){
                  ex.printStackTrace();
              }
              return null;
        }
        }

(2)新建用戶登錄頁index.jsp,該頁中包含一個用戶登錄表單和一個登錄之后狀態的顯示信息,如果用戶第一次訪問該頁會顯示用戶登錄表單,并不會顯示登錄之后的信息,當用戶登錄之后再次訪問用戶登錄頁時,會判斷Servlet返回的Cookie信息,根據Cookie信息來決定是否顯示用戶登錄之后的信息,關鍵代碼如下:

        <body>
          <%
              if(loginFlag){
          %>
          <fieldset class="style1"><legend>歡迎您回來</legend>
                  <table align="center">
                      <tr>
                            <td><%=account %>,歡迎您登錄本網站!</td>
                            <td align="center">
                                <a href="<%=basePath%>foreverlogin? action=logout">注銷登錄</a>
                            </td>
                      </tr>
                  </table>
          </fieldset>
          <%}else{ %>
          <fieldset class="style1"><legend>用戶登錄</legend>
            <form action="foreverlogin? action=login"method="post">
              <table align="center">
                  <tr>
                      <td>賬號:</td>
                      <td><input type="text"name="account"></td>
                  </tr>
                  <tr>
                      <td>密碼:</td>
                      <td><input type="password"name="pwd"></td>
                  </tr>
                  <tr>
                      <td>有效期:</td>
                      <td>
                            <input type="radio"name="timeout"value="-1"checked="checked">
                            關閉瀏覽器即失效<br/>
                            <input type="radio"name="timeout"value="<%=30*24*60*60 %>">
                            30天內有效<br/>
                            <input type="radio"name="timeout"value="<%=Integer.MAX_VALUE %>">
                            永久有效
                      </td>
                  </tr>
                  <tr>
                      <td colspan="2"align="center"><input type="submit"value="登 錄"></td>
                  </tr>
            </table>
          </form>
          </fieldset>
          <%} %>
        </body>

(3)新建名為ForeverLoginServlet的Servlet類,在該類的doPost()方法中根據提交過來的action參數值來判斷調用用戶登錄方法或用戶注銷的方法,關鍵代碼如下:

        public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");                             //設置請求編碼格式
            response.setCharacterEncoding("UTF-8");                            //設置響應編碼格式
            String action=request.getParameter("action");                       //獲得action參數,主要判斷是登錄還是注銷
            if("login".equals(action)){
                  this.login(request, response);                               //調用login()方法
            }else if("logout".equals(action)){
                  this.logout(request, response);                              //調用logout()方法
            }
        }
        /**
         * 該方法處理用戶登錄
         */
        public void login(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException{
            String account=request.getParameter("account");                    //獲得賬號
            String pwd=request.getParameter("pwd");                            //獲得密碼
            int timeout=Integer.parseInt(request.getParameter("timeout"));     //獲得登錄保存期限
            String md5Account=MakeMD5.getMD5(account);                         //將賬號加密
            account=URLEncoder.encode(account, "UTF-8");                       //如果賬號是中文,需要轉換Unicode才能保存在Cookie中
            Cookie accountCookie=new Cookie("account", account);               //將賬號保存在Cookie中
            accountCookie.setMaxAge(timeout);                                  //設置賬號Cookie的最大保存時間
            Cookie md5AccountCookie = new Cookie("md5Account", md5Account);    //將加密后的賬號保存在Cookie中
            md5AccountCookie.setMaxAge(timeout);                               //設置加密后的賬號最大保存時間
            response.addCookie(accountCookie);                                 //寫到客戶端的Cookie中
            response.addCookie(md5AccountCookie);                              //寫到客戶端的Cookie中
            try {
                  Thread.sleep(1000);                                          //將此線程暫停1秒后繼續執行
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            response.sendRedirect("index.jsp? "+System.currentTimeMillis());   //將頁面重定向到用戶登錄頁
        }
        /**
         * 該方法處理用戶注銷
         */
        public void logout(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException{
            Cookie accountCookie=new Cookie("account", "");                    //創建一個空的Cookie
            accountCookie.setMaxAge(0);                                        //設置此Cookie保存時間為0
            Cookie md5AccountCookie=new Cookie("md5Account", "");              //創建一個空的Cookie
            md5AccountCookie.setMaxAge(0);                                     //設置此Cookie保存時間為0
            response.addCookie(accountCookie);                                 //寫到客戶端Cookie中,將覆蓋名為account的Cookie
            response.addCookie(md5AccountCookie);                              //寫到客戶端Cookie中,將覆蓋名為md5AccountCookie的Cookie值
            try {
                  Thread.sleep(1000);                                          //將此線程暫停1秒后繼續執行
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            //將頁面重定向到用戶登錄頁
            response.sendRedirect("index.jsp? "+System.currentTimeMillis());
        }

(4)在index.jsp頁中,設置一個保存是否登錄的標記loginFlag為false,通過request內置對象獲得所有Cookie信息的數組,循環該數組查找賬號的Cookie信息和加密賬號之后的Cookie信息,然后通過MD5算法將賬號加密生成密鑰,將該密鑰值與Cookie中保存的加密賬號比較,如果兩值匹配則將loginFlag標記改為true,然后在頁面中根據loginFlag的值來判斷顯示用戶登錄之后的信息,關鍵代碼如下:

        <%
        boolean loginFlag=false;                                                //設置一個變量,用于保存是否登錄
        String account=null;                                                    //聲明用于保存從Cookie中讀取的賬號
        String md5Account=null;                                                 //聲明用于保存從Cookie中讀取的加密的賬號
        Cookie cookieArr[]=request.getCookies();                                //獲取請求中所有的Cookie
        if(cookieArr! =null&&cookieArr.length>0){
              for(Cookie cookie: cookieArr){                                    //循環Cookie數組
                  if(cookie.getName().equals("account")){
                      account=cookie.getValue();                                //找到賬號的Cookie值
                      account=URLDecoder.decode(account, "UTF-8");              //解碼,還原中文字符串的值
                  }
                  if(cookie.getName().equals("md5Account")){
                      md5Account=cookie.getValue();                             //找到加密賬號的Cookie值
                  }
              }
        }
        if(account! =null&&md5Account! =null){
              loginFlag = md5Account.equals(MakeMD5.getMD5(account));
        }
        %>

秘笈心法

當Servlet向客戶端寫Cookie時,關鍵是要通過Cookie類的setMaxAge(int expiry)方法來設置Cookie的有效期。參數expiry以秒為單位,如果expiry大于零,就指示瀏覽器在客戶端硬盤上保持Cookie的時間為expriy秒;如果expiry等于零,就指示瀏覽器刪除當前Cookie;如果expiry小于零,就指示瀏覽器不要把Cookie保存到客戶端硬盤,當瀏覽器進程關閉時,Cookie也就消失。

主站蜘蛛池模板: 富川| 朝阳区| 浮山县| 临泉县| 资中县| 内乡县| 天全县| 吴川市| 内江市| 泸西县| 海阳市| 太和县| 游戏| 来宾市| 衡东县| 万安县| 奎屯市| 荆门市| 瓦房店市| 浙江省| 雅江县| 辽宁省| 永兴县| 乌拉特中旗| 乌拉特前旗| 河源市| 搜索| 广饶县| 萨嘎县| 桦川县| 昂仁县| 慈溪市| 哈尔滨市| 昭苏县| 广平县| 通河县| 南陵县| 富源县| 屯留县| 册亨县| 剑河县|