假设我们有一个外部资源,比如数据库,我们希望它能根据部署环境进行更改。在CDI中,我们使用生产者字段声明来声明资源。
class CustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/CustomerDatasource")
@Produces @CustomerDatabase
Datasource customerDatabase;
}
该字段声明的目的是关联数据源和绑定类型@CustomerDatabase与具有全局JNDI名称的Java EE资源java:global/env/jdbc/CustomerDatasource.
现在我们可以轻松地将我们的数据源注入到任何需要它的bean中
public class Bean {
private Datasource customerDatabase;
@Inject public Bean(@CustomerDatabase Datasource ds) {
customerDatabase = ds;
}
...
}
现在,这个注入的资源引用已经非常可配置了。JNDI名称只是一个逻辑名称。实际指向的数据源可以很容易地根据部署进行更改。我们当然没有硬编码JDBC URL、用户名和密码!但是,让我们假设这还不够。CDI允许你定义bean的替代实现。
class TestCustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/TestDatasource")
@Produces @Alternative @CustomerDatabase
Datasource testCustomerDatabase;
}
@Alternative @CustomerDatabase
class MockCustomerDatabase implements Datasource {
//operations of Datasource go here!
...
}
这两个类都提供了@CustomerDatabase 数据源的替代实现。默认情况下,CDI会忽略这些bean,并继续使用原始实现,因为它是唯一没有声明的实现@Alternative.
然而,如果我们声明其中一个替代方案在beans.xml中,CDI将使用该实现
<beans>
<alternatives>
<class>org.mycompany.resources.TestCustomerDatabase</class>
</alternatives>
</beans>
这很好,很简单,当我们只有一两个bean根据部署场景而变化时。嗯,我通常认为大多数应用程序都是这种情况。然而,那些被Spring框架过度影响的开发者认为他们有数百个bean可能会根据部署而变化。这就是他们为什么要编写大量的XML来显式列出他们应用程序中的所有类。我们当然不想跟随他们走那条路!
因此,CDI提供了定义表示部署场景的注解的能力。这些注解被称为替代 stereotype。例如
@Alternative @Stereotype
@Target(TYPE) @Retention(RUNTIME)
public @interface Mock {}
@Alternative @Stereotype
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface Test {}
现在我们可以使用这些注解来指明我们的哪些Bean属于哪个部署场景。
class TestCustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/TestDatasource")
@Produces @Test @CustomerDatabase
Datasource testCustomerDatabase;
}
@Mock @CustomerDatabase
class MockCustomerDatabase implements Datasource {
//operations of Datasource go here!
...
}
现在,只需要一行XML,我们就可以一次性启用所有@TestBean,或者所有@MockBean。
<beans>
<alternatives>
<stereotype>org.mycompany.deployment.Test</stereotype>
</alternatives>
</beans>
您不觉得这是一个更好的方法吗?