Spring Framework Core - The IoC Container (12) - 基於 java 的容器配置(3) 使用@Configuration註解

12.4 使用@Configuration註解

@Configuration是一個類級註釋,指示對象是bean定義的源。@Configuration類通過公共的@Bean註釋方法聲明bean。在@Configuration類上調用@Bean方法也可以用來定義bean之間的依賴關係。參見Basic Concepts: @Bean and @Configuration以獲得一般介紹。

注入Inter-bean依賴性

當bean彼此之間有依賴關係時,表示這種依賴關係就像讓一個bean方法調用另一個bean方法一樣簡單,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

在前面的示例中,beanOne通過構造函數注入接收對beanTwo的引用。

只有在@Configuration類中聲明@Bean方法時,這種聲明bean間依賴關係的方法纔有效。您不能通過使用普通的@Component類來聲明bean之間的依賴關係。

查找方法注入

如前所述,lookup method injection 是一個高級特性,您應該很少使用它。
它在singleton-scoped bean依賴於prototype-scoped bean的情況下非常有用。使用Java進行這種類型的配置爲實現這種模式提供了一種自然的方法。下面的例子展示瞭如何使用查找方法注入:

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

通過使用Java配置,您可以創建CommandManager的子類,其中以查找新的(prototype)命令對象的方式覆蓋了抽象的createCommand()方法。下面的例子演示瞭如何做到這一點:

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

關於基於java的配置如何在內部工作的更多信息

考慮下面的示例,其中顯示了兩次調用@Bean註釋的方法

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

在clientService1()和clientService2()分別調用一次clientDao()。因爲這個方法創建了一個新的ClientDaoImpl實例並返回它,所以您通常會希望有兩個實例(每個服務一個)。這肯定會有問題:在Spring中,實例化的bean默認有一個singleton範圍。這就是神奇之處:所有@Configuration類都是在啓動時用CGLIB子類化的。在子類中,子方法在調用父方法並創建新實例之前,首先檢查容器是否有緩存的(scoped)bean。

根據bean的範圍,行爲可能會有所不同。我們這裏說的是singleton。

從Spring 3.2開始,不再需要將CGLIB添加到類路徑中,因爲CGLIB類已經在org.springframework下重新打包了。cglib並直接包含在spring-core JAR中。

由於CGLIB在啓動時動態添加特性,所以有一些限制。特別是,配置類不能是final。但是,從4.3開始,配置類上可以使用任何構造函數,包括使用@Autowired或單個非默認構造函數聲明進行默認注入。
如果希望避免cglib強加的限制,可以考慮在非@Configuration 配置類上聲明@Bean方法(例如,在普通的@Component類上聲明)。然後不會攔截@Bean方法之間的交叉方法調用,因此必須完全依賴於構造函數或方法級別的依賴注入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章