一個簡單的Java Web專案搭建流程
今天試圖在伺服器上搭建一個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,也即: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)
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目錄下即可。