Java Web系列:Java Web 專案基礎

剛哥521發表於2015-12-17

1.Java Web 模組結構

JSP檔案和AXPX檔案類似,路徑和URL一一對應,都會被動態編譯為單獨class。Java Web和ASP.NET的核心是分別是ServletIHttpHandler介面,因此無論是基礎的Page檔案(JSP、ASPX)方式還是後來發展的MVC方式(Spring MVC、ASP.NET MVC)都是基於核心介面的基礎上再次封裝和擴充套件(DispatcherServletMvcHandler)。

除JSP檔案外,其他全部檔案部署在應用目錄的WEB-INF子目錄下,WEB-INF目錄可以認為是ASP.NET中將web.config檔案、bin目錄和App_開頭的執行時目錄存放在了一個統一的根目錄中。

Java Web的配置檔案web.xml也存放在WEB-INF目錄下,而ASP.NET的配置檔案web.config一般直接存放在應用目錄下(ASP.NET其他目錄同樣可以有web.config檔案)。ASP.NET將所有的引用和程式碼生成的dll都部署在bin中,而Java Web的引用jar和生成的class分別存放在WEB-INF的子目錄lib和classes中(參考1)。

綜上,類似ASP.NET中的web.config、bin、App_Data等,Java Web中的WEB-INF、web.xml、lib和classes是我們必須瞭解和掌握的。

|--Assembly Root
  |---WEB-INF/
    |--web.xml
    |--lib/
    |--classes/
  1. WEB-INF目錄:Java Web檔案的根目錄。
  2. web.xml檔案:配置檔案(asp.net web.config)。
  3. lib目錄:存放類庫檔案(asp.net bin)。
  4. classes目錄:存放class檔案(asp.net bin)。

2.Java Web專案的基本結構[Eclipse Dynamic Web Project]

Eclipse Dynamic Web Project專案

(1)可以配置需要編譯的原始碼目錄和輸出目錄,預設編譯src目錄下的原始檔到build\classes目錄下。

(2)可以配置WEB-INF的根目錄,預設為WebContent

(3)可以選擇是否生成預設web.xml檔案。

我們建立一個命名為DynamicWP的預設生成web.xml的Dynamic Web Proejct專案。檔案結構如下:

|--DynamicWP
  |--.settings/
  |--build/
    |--classes/
  |--src/
  |--WebContent/
    |--META-INF/
      |--MANIFEST.MF
    |--WEB-INF/
      |--web.xml
      |--lib/

在Eclipse的專案資源管理器中DyanmicWP專案的檢視如下:

|--DynamicWP
  |--Deployment Desciptor
  |--JAX-WS Web Services
  |--Java Resources
  |--JavaScript Resources
  |--build
  |--WebContent
    |--META-INF/
      |--MANIFEST.MF
    |--WEB-INF/
      |--web.xml
      |--lib/
  1. .settings為Eclipse專案資料夾,存放了Eslipse專案的各種配置。在Eclipse專案檢視中不可見。
  2. src目錄存放原始碼。在Eclipse的專案檢視中對應為Java Resources/src。
  3. build存放編譯後的檔案。
  4. 可以在類似的\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\DynamicWP目錄中檢視執行時的檔案結構。

3.Maven Web專案的基本結構

鑑於目前Java IDE眾多並且都有一定的擁泵,Eclipse的Java Web專案不具有可移植性。Maven即解決了專案結構的規範問題又提供了強大引用處理等強大的功能,在專案佈局等方面已經是目前事實上的標準。Maven專案的主要結構如下(參考2):

|--root
  |--pom.xml
  |--src/
    |--main/
      |--java/
      |--resources/
      |--webapp/
    |--test/
      |--java/
      |--resources
  |--target/

Eclipse中新建一個Maven web app專案。檔案結構如下:

|--MavenWP
  |--pom.xml
  |--.project
  |--.classpath
  |--.settings/
  |--src/
  |--target/
    |--classes/
    |--m2e-wtp/
  1. pom.xml:maven專案配置檔案。
  2. .project檔案和.classpath檔案以及.settings目錄和target/m2e-wtp目錄下的檔案為Eclipse專案配置檔案。
  3. src和target:maven標準專案目錄。

Eclipse4.5.1中對應的專案資源管理檢視

|--MavenWP
  |--Deployment Desciptor/
  |--Java Resources/
  |--JavaScript Resources/
  |--Deployed Resources/
  |--src
  |--target
  |--pom.xml
  1. 預設建立的專案會新增一個index.jsp並報錯:使用maven搜尋並新增servlet依賴更新後就可以正常執行。
  2. Java構建路徑問題警告:使用maven搜尋並新增compiler外掛並配置configuration節點更新就可以消除。
  3. 牆的問題配置maven映象,我採用的是http://maven.oschina.net/content/groups/public/。
  4. 預設建立的maven webapp缺少的src/main/java、src/test/java和src/test/resources等目錄需要自己手動新增。
  5. 修改.settings/org.eclipse.wst.common.project.facet.core.xml,更新<installed facet="jst.web" version="3.1"/>。
  6. web.xml根節點開始部分修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

 

Maven的配置檔案pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>me.test</groupId>
    <artifactId>MavenWP</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>MavenWP Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>MavenWP</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.servlet基礎

正如ASP.NET的核心是IHttpHandler一樣,Java Web的核心是Servlet介面,位於javax.servlet名稱空間中。Filter的概念可以參考ASP.NET的HttpModule,Servlet中的各種Listener可以參考ASP.NET HttpApplicaiton中類似的event。無論是Java還是.NET的Web技術,都是基於HTTP協議的具體實現。Java Web和ASP.NET中的一些核心項對應如下:

  Java 參考3 .NET 備註
Core javax.servlet.Servlet System.Web.IHttpHandler  
HTTP Request javax.servlet.ServletRequest System.Web.HttpRequest  
HTTP Response javax.servlet.ServletResponse System.web.HttpResponse  
Cookie javax.servlet.http.Cookie System.Web.HttpCookie  
Session javax.servlet.http.HttpSession System.Web.HttpSessionState  
Application javax.servlet.ServletContext System.Web.HttpApplication  
Begin Request javax.servlet.Servlet.RequestDispatcher System.Web.HttpApplication.BeginRequest event 
Begin\End Request javax.servlet.Servlet.ServletRequestListener System.Web.HttpApplication.BeginRequest\EndRequest event 
Filter javax.servlet.Filter System.Web.IHttpModule  
Application Event javax.servlet.ServletContextListener System.Web.HttpApplication.Application_Start\Application_End method

Servlet和ASP.NET的簡化示意圖:

用於簡化web.xml配置的Servlet的註解(3.0開始支援,在ASP.NET中沒有對應項):

(1)WebServlet:作用在javax.servlet.http.HttpServlet的實現類上。

(2)WebFilter:作用在javax.servlet.Filter的實現類上。

(3)WebListener:作用在Listener的實現類上(javax.servlet.ServletContextListener、javax.servlet.ServletContextAttributeListener、javax.servlet.ServletRequestListener、javax.servlet.ServletRequestAttributeListener、javax.servlet.http.HttpSessionListener、javax.servlet.http.HttpSessionAttributeListener)。

(4)WebInitParam:結合WebServlet和WebFilter註解用來配置屬性。

(5)MultipartConfig:作用在javax.servlet.http.HttpServlet的實現類上。標註請求是mime/multipart型別。

用於Servlet容器初始化的ServletContainerInitializer(可實現無web.xml,3.0開始支援,可類比ASP.NET的Application_Start方法):

(1)Servlet容器啟動時查詢ServletContainerInitializer的例項。

(2)ServletContainerInitializer例項使用HandlesTypes標註一個或多個型別,Servlet容器將在啟動時掃描classpath,獲取這些型別的例項。

(3)Servlet容器在啟動時呼叫ServletContainerInitializer實現類的onStartup方法,該方法可以獲取HandlesTypes標註的所有型別物件。

5.自定義Session

Session在儲存安全性要求較高的會話資訊方面是必不可少的,Session當然絕對不是用來儲存使用者登入狀態的,但類似驗證碼等敏感資訊卻必須儲存在Session中。對於分散式Web應用自定義Session支援獨立的狀態伺服器或叢集是必須的。

ASP.NET通過SessionStateModule通過配置檔案配置實際的Session提供程式,Session提供程式實現了SessionStateStoreProviderBase,因此在ASP.NET中實現自定義Session是通過繼承SessionStateStoreProviderBase實現,配置Session是通過Web.config。ASP.NET自定義session的程式碼參考github上的開源專案SQLiteSessionStateStore

同理,Java Servlet中使用自定義Session通過Filter可以實現。由於不同的servlet容器對Session的實現不同,所以通用性最好的方式是繼承HttpServletRequestWrapper重寫getSession方法返回自定義的Session物件。Filter採用了職責鏈模式(chain of responsibility),HttpServletRequestWrapper採用了裝飾模式(Decorator),可以通過《Head First 設計模式》閱讀模式的相關內容。

(1)首先自定義繼承HttpSession的MySession(為了便於演示,僅包裝了容器的session並轉發呼叫)。

import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;

public class MySession implements HttpSession {

    private HttpSession _containerSession;

    public MySession(HttpSession session) {
        this._containerSession = session;
    }

    @Override
    public long getCreationTime() {
        return this._containerSession.getCreationTime();
    }

    @Override
    public String getId() {
        return this._containerSession.getId();
    }

    @Override
    public long getLastAccessedTime() {
        return this._containerSession.getLastAccessedTime();
    }

    @Override
    public ServletContext getServletContext() {
        return this._containerSession.getServletContext();
    }

    @Override
    public void setMaxInactiveInterval(int interval) {
        this._containerSession.setMaxInactiveInterval(interval);
    }

    @Override
    public int getMaxInactiveInterval() {
        return this._containerSession.getMaxInactiveInterval();
    }

    @SuppressWarnings("deprecation")
    @Override
    public HttpSessionContext getSessionContext() {
        return this._containerSession.getSessionContext();
    }

    @Override
    public Object getAttribute(String name) {
        return this._containerSession.getAttribute(name);
    }

    @SuppressWarnings("deprecation")
    @Override
    public Object getValue(String name) {
        return this._containerSession.getValue(name);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return this._containerSession.getAttributeNames();
    }

    @SuppressWarnings("deprecation")
    @Override
    public String[] getValueNames() {
        return this._containerSession.getValueNames();
    }

    @Override
    public void setAttribute(String name, Object value) {
        this._containerSession.setAttribute(name, value);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void putValue(String name, Object value) {
        this._containerSession.putValue(name, value);
    }

    @Override
    public void removeAttribute(String name) {
        this._containerSession.removeAttribute(name);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void removeValue(String name) {
        this._containerSession.removeValue(name);
    }

    @Override
    public void invalidate() {
        this._containerSession.invalidate();
    }

    @Override
    public boolean isNew() {
        return this._containerSession.isNew();
    }

}
View Code

(2)自定義繼承HttpServletRequestWrapper的MyRequest

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;

public class MyRequest extends HttpServletRequestWrapper {

    public MyRequest() {
        super(null);
    }

    public MyRequest(HttpServletRequest request) {
        super(request);
        // TODO 自動生成的建構函式存根
    }

    @Override
    public HttpSession getSession(boolean create) {
        return new MySession(super.getSession(create));
    }

    @Override
    public HttpSession getSession() {
        return new MySession(super.getSession());
    }
}
View Code

(3)自定義Filter將Request包裝為MyRequest

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter("/*")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO 自動生成的方法存根

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(new MyRequest((HttpServletRequest) request), response);

    }

    @Override
    public void destroy() {
        // TODO 自動生成的方法存根

    }

}
View Code

通過註解配置了Filter,也可以通過原始的web.xml方式配置。

6.參考

1.https://docs.oracle.com/javaee/7/tutorial/packaging003.htm

2.http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

3.https://docs.oracle.com/javaee/7/tutorial/webapp005.htm

小結:

你至少應該知道的:

(1)配置檔案:ASP.NET的web.config和Java的web.xml

(2)Web核心:ASP.NET的IHttpHandler介面和Java的Servlet介面

(3)攔截器:ASP.NET的HttpModule和Java的Filter

(4)應用程式事件:ASP.NET的HttpApplication event和Java的各種Listener

(5)啟動器:ASP.NET的Application_Start和Java的ServletContainerInitializer

(6)引用管理:ASP.NET的Nuget和Java的Maven

感想:

ASP.NET的核心物件不像Java Servlet一樣,從一開始就基於介面,這是缺點。但Java Servlet的核心物件全靠容器實現,就連HttpSession同樣如此,這也是缺點。比如自定義個Session十分麻煩,沒有像ASP.NET一樣簡單配置即可。另外Servlet的一些抽象定義有點過頭了,不夠簡潔。

相關文章