今天試圖在服務器上搭建一個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目錄下即可。
