(轉)論文學習筆記 - Rotate to Attend: Convolutional Triplet Attention Module
來源:廖雪峰的官方網站
Spring提供的容器又稱為IoC容器,什麼是IoC?
IoC全稱Inversion of Control,直譯為控制反轉。那麼何謂IoC?在理解IoC之前,我們先看看通常的Java元件是如何協作的。
我們假定一個線上書店,通過BookService獲取書籍:
public class BookService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public Book getBook(long bookId) { try (Connection conn = dataSource.getConnection()) { ... return book; } } }
為了從資料庫查詢書籍,BookService持有一個DataSource。為了例項化一個HikariDataSource,又不得不例項化一個HikariConfig。
現在,我們繼續編寫UserService獲取使用者:
public class UserService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUser(long userId) { try (Connection conn = dataSource.getConnection()) { ... return user; } } }
因為UserService也需要訪問資料庫,因此,我們不得不也例項化一個HikariDataSource。
在處理使用者購買的CartServlet中,我們需要例項化UserService和BookService:
public class CartServlet extends HttpServlet { private BookService bookService = new BookService(); private UserService userService = new UserService(); protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long currentUserId = getFromCookie(req); User currentUser = userService.getUser(currentUserId); Book book = bookService.getBook(req.getParameter("bookId")); cartService.addToCart(currentUser, book); ... } }
類似的,在購買歷史HistoryServlet中,也需要例項化UserService和BookService:
public class HistoryServlet extends HttpServlet {
private BookService bookService = new BookService();
private UserService userService = new UserService();
}
上述每個元件都採用了一種簡單的通過new建立例項並持有的方式。仔細觀察,會發現以下缺點:
-
例項化一個元件其實很難,例如,BookService和UserService要建立HikariDataSource,實際上需要讀取配置,才能先例項化HikariConfig,再例項化HikariDataSource。
-
沒有必要讓BookService和UserService分別建立DataSource例項,完全可以共享同一個DataSource,但誰負責建立DataSource,誰負責獲取其他元件已經建立的DataSource,不好處理。類似的,CartServlet和HistoryServlet也應當共享BookService例項和UserService例項,但也不好處理。
-
很多元件需要銷燬以便釋放資源,例如DataSource,但如果該元件被多個元件共享,如何確保它的使用方都已經全部被銷燬?
-
隨著更多的元件被引入,例如,書籍評論,需要共享的元件寫起來會更困難,這些元件的依賴關係會越來越複雜。
-
測試某個元件,例如BookService,是複雜的,因為必須要在真實的資料庫環境下執行。
從上面的例子可以看出,如果一個系統有大量的元件,其生命週期和相互之間的依賴關係如果由元件自身來維護,不但大大增加了系統的複雜度,而且會導致元件之間極為緊密的耦合,繼而給測試和維護帶來了極大的困難。
因此,核心問題是:
-
誰負責建立元件?
-
誰負責根據依賴關係組裝元件?
-
銷燬時,如何按依賴順序正確銷燬?
解決這一問題的核心方案就是IoC。
傳統的應用程式中,控制權在程式本身,程式的控制流程完全由開發者控制,例如:
CartServlet建立了BookService,在建立BookService的過程中,又建立了DataSource元件。這種模式的缺點是,一個元件如果要使用另一個元件,必須先知道如何正確地建立它。
在IoC模式下,控制權發生了反轉,即從應用程式轉移到了IoC容器,所有元件不再由應用程式自己建立和配置,而是由IoC容器負責,這樣,應用程式只需要直接使用已經建立好並且配置好的元件。為了能讓元件在IoC容器中被“裝配”出來,需要某種“注入”機制,例如,BookService自己並不會建立DataSource,而是等待外部通過setDataSource()方法來注入一個DataSource:
public class BookService {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
不直接new一個DataSource,而是注入一個DataSource,這個小小的改動雖然簡單,卻帶來了一系列好處:
- BookService不再關心如何建立DataSource,因此,不必編寫讀取資料庫配置之類的程式碼;
- DataSource例項被注入到BookService,同樣也可以注入到UserService,因此,共享一個元件非常簡單;
- 測試BookService更容易,因為注入的是DataSource,可以使用記憶體資料庫,而不是真實的MySQL配置。
因此,IoC又稱為依賴注入(DI:Dependency Injection),它解決了一個最主要的問題:將元件的建立+配置與元件的使用相分離,並且,由IoC容器負責管理元件的生命週期。
因為IoC容器要負責例項化所有的元件,因此,有必要告訴容器如何建立元件,以及各元件的依賴關係。一種最簡單的配置是通過XML檔案來實現,例如:
<beans>
<bean id="dataSource" class="HikariDataSource" />
<bean id="bookService" class="BookService">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="userService" class="UserService">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
上述XML配置檔案指示IoC容器建立3個JavaBean元件,並把id為dataSource的元件通過屬性dataSource(即呼叫setDataSource()方法)注入到另外兩個元件中。
在Spring的IoC容器中,我們把所有元件統稱為JavaBean,即配置一個元件就是配置一個Bean。
依賴注入方式
我們從上面的程式碼可以看到,依賴注入可以通過set()方法實現。但依賴注入也可以通過構造方法實現。
很多Java類都具有帶引數的構造方法,如果我們把BookService改造為通過構造方法注入,那麼實現程式碼如下:
public class BookService {
private DataSource dataSource;
public BookService(DataSource dataSource) {
this.dataSource = dataSource;
}
}
Spring的IoC容器同時支援屬性注入和構造方法注入,並允許混合使用。
無侵入容器
在設計上,Spring的IoC容器是一個高度可擴充套件的無侵入容器。所謂無侵入,是指應用程式的元件無需實現Spring的特定介面,或者說,元件根本不知道自己在Spring的容器中執行。這種無侵入的設計有以下好處:
- 應用程式元件既可以在Spring的IoC容器中執行,也可以自己編寫程式碼自行組裝配置;
- 測試的時候並不依賴Spring容器,可單獨進行測試,大大提高了開發效率。