[weld] 芸芸众类中,谁能成为bean?

wuhaixing 2010-03-27
我们已经见过两种bean了:Java Bean和EJB session beans。但在bean的家族里并不止他们两个,接下来让我们一起认识下CDI界中的诸神吧。
他们是(排名不分先后):
  • Managed beans
  • Session beans
  • Producer methods
  • Producer fields

wuhaixing 2010-03-27

Managed beans

 

managed bean是一个java类,其语义及生命周期在Managed Beans规范中有明确的定义。你可以用注解 @ManagedBean给一个java 类加上managed bean称号,但在CDI中,你没必要这么搞。依照规范,CDI容器会将符合下属条件的类作为managed bean对待:

  • 不是非静态的inner类
  • 不是抽象类(除非带着@Decorator注解)
  • 没有EJB compoent-defining注解,当然也不能在ejb-jar.xml中声明为EJB bean。
  • 没有实现javax.enterprise.inject.spi.Extension接口
  • 构造方法适当,满足下面任一条件:
    • 不带参数    
    • 带有@Inject注解 

注意
    根据上面的定义,JPA entities从技术角度来讲也是 managed beans。但是 entities的生命周期、状态及标识模型都有其独特性,并且通常用new或者JPA来创建他们的实例。因此,我们建议不要直接injecting一个entity类,尤其不要给entity 类赋予@Dependent 之外的任何作用域,因为JPA并不能persist被注入的CDI proxies(TODO:链接)。

 

没有进行bean type限定的managed bean所具有的bean type包括他的class,所有父类及其直接或间接实现的接口。

如果managed bean 有public 属性,那他的scope 必须是默认的@Dependent。

managed bean支持@PostConstruct 和 @PreDestroy生命周期回调方法。

从技术角度来看,Session beans也是managed beans。但因为他们独特的生命周期,以及所用到的其他企业服务,CDI将他们作为另一种不同的bean来对待。

 

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还是很强大的,你有时候会需要这些服务,比如:
  • 方法一级的事务管理及安全
  • 并发性管理
  • statefule session bean实例的钝化和stateless session bean的实例池
  • 远程或web服务调用
  • timers 和 asynchronous 方法

当确实不需要这些服务的时候,一个普通的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类型取决于其返回值的类型:
  • 如果返回类型是接口,bean类型包括返回类型及其扩展的接口和java.lang.Object.
  • 如果返回类型是primitive或者array类型,bean类型仅包括返回类型和java.lang.Object。
  • 如果返回类型是类,bean类型包括返回类型,其父类及其直接或间接实现的接口,少不了的java.lang.Object。

注意
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组件环境注入的适配器。
Global site tag (gtag.js) - Google Analytics