今天試圖在服務器上搭建一個web服務器,順便回顧了java web項目的入門,使用Servlet處理HTTP請求,並記錄日志等操作。當很久沒有做過web項目時,有些東西還是很容易忘記的。
Maven配置
使用maven進行整個項目的構建,使用intellij idea IDE,填寫完groupId和artifactId之后,聲明packaging元素為war包,在build中注意需要設置war-plugin的webResources:
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <webResources> <resource> <directory>web</directory> </resource> </webResources> </configuration> </plugin> </plugins>
其中的dependency項中除了要包含的依賴jar包外,有些編譯期依賴的jar包也需要填寫(scope=provided),比如javaee-api。
Servlet編寫和配置
Java Web項目中使用Servlet來處理具體的http請求,請求url的處理是配置在webResources目錄下的web.xml文件中的:
<servlet> <servlet-name>monitor</servlet-name> <servlet-class>具體的ServletClass</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>monitor</servlet-name> <url-pattern>/monitor</url-pattern> </servlet-mapping>
其中servlet-mapping中的/monitor就是對應的處理URL,也即http://主機名稱:web服務器端口/web項目的Context/url-pattern。
在Servlet中通常繼承javax.servlet.http.HttpServlet類,重寫其中的doGet和doPost方法(分別處理GET和POST請求)。事實上,Servlet中包含HTTP的所有請求方式的相關方法(PUT, DELETE等)一般情況下,我們對於數據量稍微比較大的數據都使用POST方式提交HTTP請求(GET方式一般用於查詢資源,會限制提交數據長度,GET請求的參數數據會顯示在瀏覽器的地址欄URL中)。
通過HttpServletRequest.getParameter(parameterName)來獲取請求中提交的參數數據,這里指的僅僅是Request范圍的參數名。
Servlet的數據返回
如何返回Servlet中的數據,這需要我們使用參數中的HttpServletResponse的相關方法了,其中getWriter()方法提供了一個輸出流,可以將html中的數據寫入到這個輸出流中,這樣在瀏覽器就能以頁面到形式查看到這個html頁面。
Servlet可以以Java程序的方式對請求進行處理並返回,可以說,Servlet是Java代碼中包含html頁面,如果生成的html頁面比較大,其中的getWriter().print()的代碼會非常恐怖而且難以理解。JSP正是基於這個原因出現的,JSP使用的方式是html頁面加入java代碼(scriptlet),在html頁面較大而java邏輯較少的情況下比較適用。
在Servlet中也可以根據處理邏輯來forword到對應的jsp頁面,使用如下的方法:
getServletConfig().getServletContext().getRequestDispatcher(jsp的相對路徑).forward(request,response);
我們知道HTTP返回的代碼代表這不同的含義,比如
1xx-信息提示; 2xx-成功; 3xx-重定向; 4xx-客戶端錯誤; 5xx-服務器錯誤;
我們可以手動在HttpServletResponse.setStatus()方法中指定返回的HTTP Code,給客戶端對應的提示。
在Web項目處理邏輯中,經常需要處理本地資源,比如讀取本地(Web項目中)的配置文件。這就需要使用ServletContext中的getResource系列方法, getResource和getResourceAsStream方法以“/”開頭的字符串為參數,它指定上下文根路徑的資源相對路徑。文檔的層級可能存在於服務器的文件系統,war文件,遠程服務器或者在一些其它位置中,注意在使用完成后,需要將流關閉。
日志(log4j)配置
在進行任何項目開發都需要記錄必要的日志,尤其是對應web項目的開發,你需要能夠查詢到對應的處理錯誤,這里使用了log4j來進行日志的處理。
日志的配置需要進行初始化,這個初始化的時機需要在web項目啟動時做。這里就需要為log4j單獨創建一個Servlet,用於初始化。在web.xml中建立對應的Servlet,並不需要聲明servlet-mapping,因為它並不負責真正處理HTTP請求。
<servlet> <servlet-name>log4j-init</servlet-name> <servlet-class>com.xxx.monitor.servlet.Log4jInitServlet</servlet-class> <init-param> <param-name>log4j</param-name> <param-value>WEB-INF/log4j.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
將其load-on-startup聲明的順序改成1,這樣就能保證在其他Servlet初始化之前,Log4j已經初始化完畢。其中的init-param參數指定了log4j配置文件對應WebResources的位置,這在ServletConfig中可以通過getInitParameter來進行獲取。
在Log4jInitServlet中,由於不需要處理HTTP的各種類型請求,只需要重寫初始化方法init:
@Override public void init(ServletConfig servletConfig) throws ServletException { String prefix = servletConfig.getServletContext().getRealPath("/"); String filePath = String.format("%s/%s", prefix, servletConfig.getInitParameter("log4j")); FileInputStream inputStream = null; Properties properties = new Properties(); try { inputStream = new FileInputStream(new File(filePath)); properties.load(inputStream); String logFilePath = String.format("%s%s", prefix, properties.getProperty("log4j.appender.R.File")); properties.setProperty("log4j.appender.R.File", logFilePath); PropertyConfigurator.configure(properties); } catch (IOException e) { throw new ServletException("log4j module initialized failed!"); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { } } } }
這里log4j.properties中的log4j.appender.R.File參數只是指定輸出log文件的相對地址,這就需要我們使用servletConfig.getServletContext().getRealPath("/")將其拼接成運行時的絕對地址。
HTTP請求測試
在編寫代碼完后,我們都需要對其正確性進行測試。Java中提供了對於HTTP請求發送的相關API,在這個基礎上,我們進行測試代碼的編寫:
URL postUrl = null; try { postUrl = new URL(url); } catch (MalformedURLException e) { throw new RuntimeException(e); } HttpURLConnection connection = null; DataOutputStream dataOutputStream = null; BufferedReader reader = null; try { connection = (HttpURLConnection) postUrl.openConnection(); //Read from the connection connection.setDoInput(true); //http body is in the content connection.setDoOutput(true); //we use post method connection.setRequestMethod("POST"); //post can't use caches connection.setUseCaches(false); connection.setInstanceFollowRedirects(true); connection.connect(); dataOutputStream = new DataOutputStream(connection.getOutputStream()); String content = "userName=clamaa&password=bbb&json=jsonstring"; dataOutputStream.writeBytes(content); dataOutputStream.flush(); dataOutputStream.close(); reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); System.out.println("===================="); System.out.println("read line started..."); StringBuilder result = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { System.out.println(line); result.append(line).append(System.getProperty("line.separator")); } System.out.println("===================="); return result.toString(); } catch (IOException e) { throw new RuntimeException(e); } finally { if (connection != null) { connection.disconnect(); } if (reader != null) { try { reader.close(); } catch (IOException e) { } } }
至此,一個基本的Java項目就已經編寫完畢,由於整個項目使用maven來構建的,只要在項目目錄下,執行maven clean install命令,將生成的target/下的war包部署到tomcat的webapp目錄下即可。