tomcat體系結構
可以從tomcat的server.xml檔案中元素的層次結構來理解tomcat的體系結構:
Server(可以視為tomcat本身)->經由connector可以有多個(coyote預設為Bio阻塞式io)處理socket分發->Service可以有多個(Catalina Container容器)(一般由catalina container來呼叫使用者的web app java程式碼)->一個engine->多個host虛擬主機->多個context(就是web app)
每個由host定義的虛擬主機中可以定義多個context(也就是web app或者說modules)
war打包部署:
jar cvf myapp.war webproject-directory
該命令將建立myapp.war部署檔案。
需要注意的是該檔案對於tomcat來說也是一個資料夾。只要訪問url到了/myapp,tomcat就會首先將myapp.war解壓,並且根據該war中的web.xml配置來定址url對應的class並且執行後返回結果
maven
maven是一個apache基金會開源的java構建,依賴管理的工具,類似C語言下的make,本身也是java寫的
maven的特點:
1. 約定優先,maven非常強烈地建議相應最佳實踐下的目錄結構
2. 內建提供了第三方依賴管理,支援自建自管倉庫,專案的依賴直接從這個倉庫中下載
3. 提供了一致的構建過程
4. 外掛式架構,大量的可用外掛完成我們的構建流程
5. 方便和IDE整合
maven安裝使用過程:
1. 下載http://mirror.bit.edu.cn/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.zip
2. 解壓,並且將對應bin目錄放到path環境變數中,配置環境變數M2_HOME=/path/to/maven
3. mvn -v確保正常輸出
由於國內訪問maven倉庫非常慢,阿里雲公益心做了對應的映象。
<mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror>
maven的pom.xml(project object model)檔案
專案座標:
groupId,組織,比如com.example
artifactId,專案識別符號,比如mymodule,myproject等;
version, 版本,比如1.0.1-SNAPSHOT,這裡snapshot在maven構建時將被替換為timestamp
dependencies
這裡描述本專案的依賴
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>provided</scope> </dependency> </dependencies>
classpath
classpath用於連線java run time library和檔案系統。它定義編譯器和直譯器應該在何處去查詢要載入的.class檔案。其基本思想是:檔案系統的層次結構就反映了java包的層次結構,而classpath則定義了檔案系統中的哪些目錄可以作為java包層次結構的根(root)
https://www.ibm.com/developerworks/cn/java/j-classpath-windows/
請求轉發forward和重定向redirect
請求轉發是由servlet將當前的request和response交給另外的元件處理,最終由其他元件負責返回瀏覽器響應。對於瀏覽器來說,這是一次請求,一次響應。請求的轉發發生在服務端內部。瀏覽器位址列並不會改變;
RequestDispatcher(forward或者include(include是所有元件都可以輸出資訊))的獲取
通過HttpServletRequest獲取,通過ServletContext獲取
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher("/forwardExample"); rd = this.getServletContext().getNamedDispatcher( "ServletForwardExample"); rd = this.getServletContext().getRequestDispatcher("/forwardExample"); rd.forward(req, resp); }
sendRedirect
通過response物件傳送給瀏覽器一個新的url地址,往往就是http location,瀏覽器自動跳轉
監聽器
監聽器分類
按照監聽器所監聽的物件可以分為:
監聽應用程式環境(ServletContext),又可以細緻分為ServletContextListener(建立和銷燬),ServletContextAttributeListener(物件屬性的CRUD),
監聽使用者請求物件(ServletRequest),也可以細緻分為:ServletRequestListener(建立和銷燬),ServletRequestAttributeListener(物件屬性的CRUD)
監聽使用者會話物件(HttpSession),也可以細緻分為HttpSessionListener(建立和銷燬),HttpSessionAttributeListener(物件屬性的CRUD),HttpSessionActivationListener監聽session持久化到磁碟,或者從磁碟恢復到記憶體的事件。
HttpSessionBindingListener: attribute方法呼叫或者removeAttribute方法呼叫時會觸發
監聽器,過濾器,Servlet的啟動順序
監聽器,過濾器或者Servlet的啟動順序首先取決於在部署描述符web.xml中定義的順序,按照其定義順序而順序建立的。
監聽器優先順序,高於過濾器,高於Servlet
servlet併發
servlet併發執行緒模型:
多個請求訪問同一個servlet時,由於被容器分別以多個worker執行緒來呼叫單例項servlet對應的service方法,因此在servlet的開發中,我們必須注意執行緒安全問題!!
servlet併發處理的特點:
1. 單例項,我們知道servlet的生命週期中只初始化建立一次,也就是單例項
2. 多執行緒,當多個使用者同時訪問同一個servlet時,對應的servlet的service方法或者get,post方法將在不同的worker thread中同時執行,也就是具有多執行緒執行模式;
3.執行緒不安全,也正是因為1,2兩個特點導致了servlet是執行緒不安全的。
如何做到servlet執行緒安全?
變數的執行緒安全:
1. 引數變數本地化,由於local變數並不暴露在不同的執行緒中,因此是執行緒安全的;
2. 對於需要同步訪問的執行緒不安全變數寫訪問時,必須加上synchronized鎖
屬性的執行緒安全:
1. ServletContext的屬性是執行緒不安全的
2. HttpSession理論上是執行緒安全的,但是如果同一個使用者在瀏覽器同一個程式中開啟多個tab也可能不安全;
3.ServletRequest是執行緒安全的,因為它是在每一次request時建立並供使用的資料
以下兩點需要注意:
1. 避免在Servlet中再建立新的執行緒,這將會導致程式異常複雜,容易出錯;
2. 多個Servlet需要訪問同一個外部物件時,必須加鎖處理
public class ConcurrentServlet extends HttpServlet { String name; // 儘量避免使用這類例項變數,因為這是執行緒不安全的,如果必須使用則必須加鎖 /** * */ private static final long serialVersionUID = -6948878379930865229L; @Override public void init() throws ServletException { super.init(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { synchronized (this) { name = req.getParameter("username"); PrintWriter out = resp.getWriter(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } out.println("username: " + name); } } @Override public void destroy() { super.destroy(); } }