[weld] 芸芸众类中,谁能成为bean?
wuhaixing
2010-03-27
我们已经见过两种bean了:Java Bean和EJB session beans。但在bean的家族里并不止他们两个,接下来让我们一起认识下CDI界中的诸神吧。
他们是(排名不分先后):
|
|
wuhaixing
2010-03-27
Managed beans
managed bean是一个java类,其语义及生命周期在Managed Beans规范中有明确的定义。你可以用注解 @ManagedBean给一个java 类加上managed bean称号,但在CDI中,你没必要这么搞。依照规范,CDI容器会将符合下属条件的类作为managed bean对待:
注意
没有进行bean type限定的managed bean所具有的bean type包括他的class,所有父类及其直接或间接实现的接口。
|
|
wuhaixing
2010-03-27
Session beans
Session beans是在EJB规范中出现的,他们有特殊的生命周期、状态管理及并发性模型,这使得他们与众不同。但在CDI中,session bean的参与方式与其他managed bean和非managed的java对象没什么两样。你可以在一个session bean注入其他session bean,注入managed bean,也可以在managed bean中注入session bean,让managed bean监测由session bean触发的事件,等等此类。 注意 Message-driven bean 和 entity beans 天生就不是会出现在context中的对象,自然也不能注入到其他的对象中。然而,message-driven bean还是可以使用某些CDI提供的功能,比如依赖注入、拦截器和decorators。实际上,CDI会为任何session或message-driven bean执行依赖注入,即使他们不是contextual实例。 未限定bean类型的session bean所具有的bean类型包括所有本地接口及其父接口。如果有bean类的local view,那么该类及其父类也是该bean的bean类型。当然也少不了java.lang.Object。但remote interfaces 不包括在bean类型里。 没必要为stateless session bean 和 singleton session bean声明作用域。EJB容器会控制好他们的生命周期,@Stateless和@Singleton已经能说明问题了。而对于stateful session bean,可能是任何作用域。 可以为Stateful session beans 定义一个remove方法,用@Remove进行标注,应用程序用此来标记这个实例应该被销毁了。然而,对于一个contextual的bean实例,这个方法可能仅仅在bean是@Dependent的作用域时才会由应用程序调用。对于在其他作用域中的bean,只能由container来销毁。 那什么时候才需要session bean出场呢?简简单单的managed bean不是挺好吗?EJB还是很强大的,你有时候会需要这些服务,比如:
当确实不需要这些服务的时候,一个普通的managed bean就足够了。 很多beans(包括任何@SessionScoped 或 @ApplicationScoped 的bean) 都会受到并发访问。因此,由EJB 3.1提供的并发性控制特别有用。大多数session 和 application 作用域的 beans 都应该是 EJBs. 使用了重量级资源的beans,或者内部状态非常丰富的bean,都能从EJB定义的 stateless/stateful/singleton模型中获益,优秀的container-managed生命周期及对于钝化和实例池的支持会给你很多帮助。 最后,你什么时候需要method-level 事务管理, method-level 安全, timers, 远程方法或异步方法,情况会非常明显。 在这里我们想要说明的是:当你需要他们提供的服务时才使用session bean,而不是在仅仅需要依赖注入、生命周期管理或拦截器的时候。Java EE 6提供了一个层次化的编程模型,通常来说,你很容易从一个普通的managed bean开始,当你需要的时候,只要加上注解 @Stateless, @Stateful 或 @Singleton就可以将其变成EJB。 还有,别被你的朋友所谓的“重量级”session bean吓到。如果仅仅因为要部署在Java EE容器中,就认为他们“更重”,那你和相信太子是龙他妈生的封建愚民差不多,由民间的bean容器或依赖注入框架进行管理的bean就轻快了? 作为技术人员,你不能轻信那些随意的使用“重量级”这种含糊不清的术语的家伙。 |
|
wuhaixing
2010-03-28
Producer methods
由container new一个bean类的实例再交给你用并不能解决所有问题,有时候你想获得更多的控制权。比如你想在运行的时候再决定创建bean 类型的哪个实现注入给使用者,或者你要注入的对象是通过服务或事务资源获得的,比如JPA查询返回的结果。 producer 方法也可以承担创建bean实例的任务。该方法定义其能提供的bean,当容器在指定的context中找不到bean实例的时候,就调用该方法来创建bean实例。利用 producer 方法,你的应用程序就可以完全控制bean的实例化过程了。 在bean类的方法前标注上 @Produces,你就得到了一个producer 方法。 @ApplicationScoped public class RandomNumberGenerator { private Random random = new Random(System.currentTimeMillis()); @Produces @Named @Random int getRandomNumber() { return random.nextInt(100); } 很显然,我们写不出一个随机数的类来,但肯定能写一个产生随机数的方法。让这个方法成为producer方法,其返回值就可以被注入到其他bean中了。上面的例子中还指明了一个 qualifier-- @Random,默认的作用域 @Dependent,还有EL名称—默认的 randomNumber ,接下来就可以在任何需要的地方获取随机数了。 @Inject @Random int randomNumber; 或者 <p>Your raffle number is #{randomNumber}.</p> 只有 managed bean 或者 session bean 中的非抽象方法才能作为producer方法,是不是静态方法无所谓。哦,如果是session bean中的方法,那必须是业务方法,否则必须为静态。 producer 方法的bean类型取决于其返回值的类型:
注意 Producer方法和属性可以使用 primitive bean type。这些primitive类型与java.lang包中对应的wrapper类型是等同的。 producer方法中有参数时,容器会根据参数的类型和qualifier自动为这些参数赋值(还记得吧,producer方法是由容器调用的,所以参数的赋值也要由他解决),这也算是依赖注入的一种形式: @Produces Set<Roles> getRoles(User user) { return user.getRoles(); } |
|
wuhaixing
2010-03-28
Producer fields
producer属性相对producer方法更简单,用@Produces标注 bean类中的属性,就得到了一个producer 属性。 public class Shop { @Produces PaymentProcessor paymentProcessor = ....; @Produces @Catalog List<Product> products = ....; } producer 属性的bean类型规则与producer方法一致。 producer 属性仅仅是一种快捷方式,使你不必写个毫无用处的getter方法。除了更方便之外,producer属性还可以成为Java EE组件环境注入的适配器。 |