1. 程式人生 > 實用技巧 >(轉)論文學習筆記 - Rotate to Attend: Convolutional Triplet Attention Module

(轉)論文學習筆記 - 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容器,可單獨進行測試,大大提高了開發效率。