領先一步
VMware 提供培訓和認證,以加速您的進展。
了解更多正如你們大多數人現在已經知道的,Spring 不僅僅是 關於 XML,因為最近核心的一些「官方」擴展提供配置容器的替代方法。
Spring Java Configuration 1.0 M2 是 發布 在 JavaOne 期間的產品之一,雖然仍然標記為里程碑,但進行了重要的更新和錯誤修復
<li>scoped beans are fully supported</li>
<li>the bean name generation can be customized</li>
<li>the distribution contains a 'transformed' sample (petclinic) which uses XML, JavaConfig and Groovy.</li>
事實上,1.0 M2 的大部分工作都是整合 收到的對最初公告的回饋;非常感謝所有參與者!
在這篇文章中,我想給出一些 Java 組態的範例,作為真正的基於 IoC 註解的組態。 讓我們從 Mark 的範例開始,該範例在他的 Spring 2.1 中使用了 annotation-driven 依賴注入。
總之,以下是 Mark 使用的介面和類的圖表
連接透過 @Autowired 完成,而某些方法透過 @PostConstruct 和 @PreDestroy 標記為生命週期的一部分。
將基於註解的組態轉換為 Java 組態非常簡單
@Configuration
public abstract class JavaCfg {
@Bean (destroyMethodName = "tearDownDatabase")
public JdbcMessageRepository messageRepo() {
JdbcMessageRepository repo = new JdbcMessageRepository();
repo.createTemplate(dataSource());
// call custom init method
repo.setUpDatabase();
return repo;
}
@Bean
public GreetingService greetService() {
GreetingServiceImpl impl = new GreetingServiceImpl();
impl.setMessageRepository(messageRepo());
return impl;
}
@ExternalBean
public abstract DataSource dataSource();
}
首先,使用標記為 @Configuration 的 Java 類創建組態。 在其中聲明了 2 個 bean,並引用了一個外部 bean。
聲明的第一個 bean 是 messageRepo(與方法名稱相同),它也定義了一個銷毀方法。 請注意,自定義初始化方法是透過程式碼調用的,因此不需要任何註解或聲明。 您仍然可以使用 Spring InitializingBean 介面或 @Bean initMethodName 參數,但我建議不要這樣做。 上面的程式碼更清晰簡潔,更不用說您可以傳入參數,這是使用宣告式初始化方法時無法做到的。
定義的第二個 bean 是 greetService,它使用 messageRepo 作為依賴項。 這就是 Java 組態的魔力發生的地方,因為每次創建 greetService 時,Spring 容器都會提供 messageRepo 後面的 bean 實例。 也就是說,如果 messageRepo 是單例,則每次都會返回相同的實例。 但是,如果指定了不同的範圍,則在必須創建新實例時,將調用您的程式碼。 Rod 已經解釋了這一點,因此請參閱他的部落格 文章以獲取更多資訊。
1.0 M2 的一個新增功能是 @ExternalBean 註解,它引用在當前組態之外聲明的 bean,同時仍然依賴 Java 強型別,因此您的 IDE 驗證。 @ExternalBean 在運行時使用 getBean() 查找覆蓋聲明它的方法,如下所示
public DataSource dataSource() {
return (DataSource) context.getBean("dataSource");
}
當然,可以使用 ConfigurationSupport 類手動執行相同的操作,但 @ExternalBean 使事情變得容易得多。 請注意,在最初的範例中,我使用了抽象方法來強調外部化,但是可以使用任何類型的非最終方法
@ExternalBean
public DataSource dataSource() {
throw new UnsupportedOperationException("this line will NEVER execute since the method will be overridden");
}
現在已創建組態,將其宣告為普通 bean 以及 JavaConfiguration 後處理器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="config" class="blog.javaconfig.JavaCfg" />
<bean id="processor"
class="org.springframework.config.java.process.ConfigurationPostProcessor" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
</beans>
你已經準備好了(如果你正在運行 Mark 的測試,請確保使用 Java 組態 xml 檔案)。
由於一張圖片勝過千言萬語,請參閱下面透過 SpringIDE 相同的設置:
我使用了最新的 SpringIDE 快照,它為 Java 組態註解提供可視化、導航和驗證(例如,外掛程式檢查 destroyMethodName 是否指向 bean 創建方法返回類型上的正確方法)。
Java 組態支援大多數 XML 宣告功能,也就是說,您可以在 bean 級別(透過 @Bean)和作為預設值(透過 @Configuration)指定範圍、自動裝配策略、懶惰性、depends-on 以及自定義元資料。 在 1.0 M2 中,您甚至可以獲得 @ScopedProxy 註解,它是 <aop:scoped-proxy/> 的直接替代品。
但是,Java 組態提供的優於傳統 XML 容器的一個新功能是「bean 可見性」 - 定義無法在其組態之外使用的 bean 的能力。 再次,讓我們看一些程式碼
@Configuration
public class VisibilityConfiguration {
@Bean(scope = DefaultScopes.PROTOTYPE)
public Object publicBean() {
List list = new ArrayList();
list.add(hiddenBean());
list.add(secretBean());
System.out.println("creating public bean");
return list;
}
@Bean(scope = DefaultScopes.SINGLETON)
protected Object hiddenBean() {
System.out.println("creating hidden bean");
return new String("hidden bean");
}
@Bean(scope = DefaultScopes.PROTOTYPE)
private Object secretBean() {
List list = new ArrayList();
// hidden beans can access beans defined in the 'owning' context
list.add(hiddenBean());
System.out.println("creating secret bean");
return list;
}
}
Java 組態將使用方法可見性來確定某個 bean 是公共的(也就是說,如果它可以在其宣告組態之外使用)還是私有的(非公共的)。 因此,任何非公共的 @Bean 註解方法都會創建一個隱藏的 bean。 這使您可以提供 bean 定義封裝,禁止意外或非意外的訪問。 非常重要的是要注意,隱藏的 bean 不會轉換為 巢狀 bean - 它們是功能齊全的頂級 bean:它們具有自己的生命週期並支援自定義範圍,而不是依賴於父 bean 的內部 bean。
為了證明這一點,我將 hiddenBean 標記為單例,將 secretBean 標記為原型。
讓我們使用以下測試來測試行為
public class VisibilityTest extends TestCase {
private ConfigurableApplicationContext context;
@Override
protected void setUp() throws Exception {
context = new AnnotationApplicationContext("**/VisibilityConfiguration.class");
}
@Override
protected void tearDown() throws Exception {
context.close();
}
public void testApplicationContext() {
assertNotNull(context);
System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
// I don't belive you container! I know you are hidding something
context.getBean("hiddenBean");
}
}
測試應列印
[blog.javaconfig.VisibilityConfiguration, publicBean]
creating hidden bean
creating secret bean
creating public bean
creating secret bean
creating public bean
之後,應該出現類似
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'hiddenBean' is defined
...
控制台中的第一行顯示 secretBean 和 hiddenBean 未在我們持有的上下文中定義。 但是,以下行顯示隱藏的 bean 被創建一次(因為它是單例),而 secretBean 被創建兩次,對於每個 publicBean,因為它是一個原型。
那麼隱藏的 bean 在哪裡呢? 在子容器內。
父容器(在我們的例子中是context)完全不知道它,因此也不知道其中宣告的任何 bean。 然而,在子上下文中宣告的 bean 可以訪問在父上下文中宣告的任何 bean,反之則不然。 另一方面,公共 bean(例如 publicBean)由 Java 組態「推送」到父容器中,但由於它們與隱藏的 bean 在同一組態中宣告,因此它們可以在實例化期間引用「秘密」bean。
對於那些想要完全放棄 XML 的人,Spring Java 組態提供了 AnnotationApplicationContext,它使用類而不是 XML 檔案,正如您可以從上面的測試案例中看到的那樣。 雖然我的範例有效,但它並不理想,因為在沒有任何快取的情況下,應用程式上下文將為每個測試創建和銷毀。 一種替代方法是重用現有的 AbstractDependencyInjectionSpringContextTests 並適當地覆蓋上下文創建
public class NoXMLTest extends AbstractDependencyInjectionSpringContextTests {
@Override
protected ConfigurableApplicationContext createApplicationContext(String[] locations) {
GenericApplicationContext context = new GenericApplicationContext();
customizeBeanFactory(context.getDefaultListableBeanFactory());
// use Java Configuration annotation-based bean definition reader
new ConfigurationClassScanningBeanDefinitionReader(context).loadBeanDefinitions(locations);
context.refresh();
return context;
}
@Override
protected String[] getConfigLocations() {
return new String[] { "**/*.class" };
}
public void testAppCtx() {
assertNotNull(applicationContext);
}
}
(可以透過 SPR-3550 進一步簡化)。
你們中的一些人可能想知道哪種註解組態方法最好:註解驅動注入還是 Java 組態? 我的回答是:「這取決於」。
Java 組態忠於 IoC 原則,因為組態位於您的程式碼之外,這意味著您擁有真正的 POJO(即,您的程式碼中沒有組態註解)。
先前在本部落格中介紹的註解驅動注入允許物件對其組態有更多的了解。 他們可以要求依賴項、自動裝配,甚至可以指定其範圍。 注入仍然發生(也就是說,物件仍然由容器管理),但您組態的某些部分現在包含在您的物件中。
使用 JavaConfig,您可以配置您的物件,而不受任何限制,因為您使用的是純 Java。 您可以使用任意數量的參數、任何類型,並且可以調用任意數量的方法。 由於它是 Java,因此您的組態可以方便地重構,並且您可以從您的 IDE 自動完成中受益。 這非常靈活和強大!
另一方面,透過註解驅動注入,您可以對物件進行細粒度(類別、方法甚至欄位級別)控制,以及更多的上下文資訊。
考慮 @Autowire 方法
@Autowired
public void createTemplate(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
Spring 不僅使用註解來確定將發生自動裝配的方法,還確定所需的類型。 此外,可以使用多參數方法,這是「傳統」自動裝配不支援的功能,後者使用 JavaBeans 約定,因此使用 setter。
歸根結底,這兩種方法都達到一個目的:配置 Spring 容器。 您可以使用其中一種、兩種,以及一些 XML 和 屬性(如果願意)。 事實上,Java 組態發行版使用基於 XML、註解和 Groovy 的組態替換 Petclinic 的「傳統」XML 組態。 考慮到這篇部落格 文章,直到 JRuby 被包含在內之前,時間不會太長。
最重要的是,您可以選擇任何更適合您的開發風格的東西。
附註:如果您對這個主題感興趣,您可能想參加以下 SpringOne 會議,以進行深入討論 :)
乾杯,Costin