java produces_JAVA CDI 学习- @Produces及@Disposes

上一节学习了注入Bean的生命周期,今天再来看看另一个话题: Bean的生产(@Produces)及销毁(@Disposes),这有点象设计模式中的工厂模式。在正式学习这个之前,先来看一个场景:

基于web的db应用开发中,经常要在一个页面上连接db,然后干点啥,最后关闭连接。下面用之前二节前到的CDI技能来演练一下:

1、先建一个Connection的接口1 package conn;

2

3 public interface Connection {

4

5     void connect();

6

7     void closeConnection();

8

9     String doSomething();

10

11 }

Connection Interface

2、再来一个实现1 package conn;

2

3 import javax.annotation.PostConstruct;

4 import javax.annotation.PreDestroy;

5

6 public class ConnectionImpl implements Connection {

7     /**

8      * Servlet构造函数调用后,会自动执行带有@PostConstruct的方法

9      */

10     @PostConstruct

11     private void initConn(){

12         System.out.println("initConn is called...");

13         connect();

14     }

15

16     /**

17      * Servlet卸载前,会自动执行带有@PreDestroy的方法

18      */

19     @PreDestroy

20     private void destroyConn(){

21         System.out.println("destroyConn is called...");

22         closeConnection();

23     }

24

25     @Override

26     public void connect() {

27         System.out.println("Connecting...");

28

29     }

30

31     @Override

32     public void closeConnection() {

33         System.out.println("Closing connection...");

34

35     }

36

37     @Override

38     public String doSomething() {

39         String msg = "do something...";

40         System.out.println(msg);

41         return msg;

42

43     }

44

45 }

ConnectionImpl

注:留意一下@PostConstruct与@PreDestroy这二个特殊的注解。我们知道所有jsf/jsp页面,最终运行时,实际上执行的是背后对应的Servlet,整个Servlet的生命周期在加入了这二个注解后,其执行顺序如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

所以,当ConnectionImpl最终被注入到Controller中时,会自动先调用initConn方法建立连接,在整个Request结束前,自动调用destroyConn关闭连接。

3、创建Controller类1 package controller;

2

3 import javax.inject.Inject;

4 import javax.inject.Named;

5

6 import conn.Connection;

7 import conn.TestConnection;

8

9 @Named("Conn")

10 public class ConnectionController {

11

12     @Inject

13     private Connection connection;

14

15     public Connection getConnection() {

16         return connection;

17     }

18

19 }

ConnectionController

4、新建conn.xhtml用于显示1 html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

3       xmlns:h="http://java.sun.com/jsf/html"

4       xmlns:f="http://java.sun.com/jsf/core"

5       xmlns:ui="http://java.sun.com/jsf/facelets">

6

8     

Connection Test

10 

11     #{Conn.connection.doSomething()}

12 

13 

conn.xhtml

eclipse里部署到jboss下,浏览http://localhost:8080/cdi-scope-sample/conn.jsf,观察console的输出:

AAffA0nNPuCLAAAAAElFTkSuQmCC

跟预想的完全一样! 条条道路通罗马,解决问题的途径往往不止一条,或许有些人不喜欢在ConnectionImpl里参杂太多其它的职责(比如:自动打开连接、自动关闭连接),可以考虑用CDI的produces及disposes.

5、创建ConnectionFactory

回想一下设计模式中的工厂模式,对象的创建(销毁)通常放在一个单独的工厂类来处理(单一职责原则),我们也来建一个工厂:1 package conn;

2

3 import javax.enterprise.context.RequestScoped;

4 import javax.enterprise.inject.*;

5

6 public class ConnectionFactory {

7

8     @Produces

9     @RequestScoped

10     @MyConnection

11     public Connection getConn() {

12         System.out.println("ConnectionFactory.getConn is called...");

13         Connection conn = new ConnectionImpl();

14         conn.connect();

15         return conn;

16

17     }

18

19

20     public void closeConn(@Disposes @MyConnection Connection conn) {

21         System.out.println("ConnectionFactory.closeConn is called...");

22         conn.closeConnection();

23     }

24

25 }

注:关注一下@Produces这个注解,这表示应用该注解的方法,是一个Bean的生成器(或理解成工厂的某些产品生产流水线),在需要Inject的时候,会自动通过该方法产生对象实例;而@Disposes注解,正好与@Produces对应,用于人道毁灭@Produces产生的对象,此消彼涨,这样世界才会遵从守恒定律!

@RequestScoped不用多解释了,表示工厂里产生的Bean其生命周期仅存在于单次Http请求中。

but,just wait a minute,@MyConnection ? what is this ? why we need it ?

让我们将思维方式,从人类大脑切换成计算机电脑的模式,ConnectionImpl继承自Connection,对于系统来讲,这二个是都是兼容Connection类型的,在产生对象时,这还好说,因为目前Connection只有一个实现类ConnectionImpl,计算机可以足够智能的推断出应该用ConnectionImpl来创建对象实例,但是对象销毁的时候呢?这时传入的参数类型是Connection接口类型,这时它并不知道该对象具体是何种实现?

所以,我们自己创建了一个@MyConnection注解,在@Produces与@Disposes上都应用该注解,这样对象销毁时,就能根据该注解精确的知道是要销毁何种类型的哪个对象.

6、@MyConnection代码如下:1 package conn;

2

3 import java.lang.annotation.Retention;

4 import java.lang.annotation.Target;

5

6 import static java.lang.annotation.ElementType.FIELD;

7 import static java.lang.annotation.ElementType.TYPE;

8 import static java.lang.annotation.ElementType.METHOD;

9 import static java.lang.annotation.ElementType.PARAMETER;

10 import static java.lang.annotation.RetentionPolicy.RUNTIME;

11

12 import javax.inject.Qualifier;

13

14 @Qualifier

15 @Retention(RUNTIME)

16 @Target({ FIELD, TYPE, METHOD, PARAMETER })

17 public @interface MyConnection {

18

19 }

@MyConnection

7、修改ConnectionController1     @Inject

2     @MyConnection

3     private Connection connection;

在原来的@Inject下,增加@MyConnection,否则Controller感受不到Factory的存在(系统将只是简单的注入一个ConnectionImpl实例而已,不会自动创建连接/关闭连接),感兴趣的同学可以先不加这个注释,然后运行试试,体会一下

编译、部署、运行,观察Console的输出:

AAffA0nNPuCLAAAAAElFTkSuQmCC

Perfect!

8、@Produces当成资源池使用

@Produces还有一个用途,可以把一些其它地方需要用到的注入对象,统一放在一起先“生产”好,形成一个"资源池",在需要使用的地方,直接从池里拿来用即可.

下面演示了这种用法:

8.1 先定义一个Product POJO类:

1 package model;

2

3 public class Product {

4

5     private String productName;

6

7     private String productNo;

8

9     public String getProductName() {

10         return productName;

11     }

12

13     public void setProductName(String productName) {

14         this.productName = productName;

15     }

16

17     public String getProductNo() {

18         return productNo;

19     }

20

21     public void setProductNo(String productNo) {

22         this.productNo = productNo;

23     }

24

25     @Override

26     public String toString() {

27         return productNo + "," + productName;

28     }

29

30 }

8.2 再创建一个Resocues.java,用来统一管理需要用到的资源1 package resource;

2

3 import javax.enterprise.inject.Produces;

4 import javax.inject.Named;

5

6 import model.Product;

7

8 public class Resouces {

9

10     @Produces

11     @Named

12     public Product getNewProduct() {

13         Product product = new Product();

14         product.setProductName("new product");

15         product.setProductNo("000000");

16         return product;

17     }

18

19 }

8.3 然后在页面上就可以直接使用了1 html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

3     xmlns:h="http://java.sun.com/jsf/html"

4     xmlns:f="http://java.sun.com/jsf/core"

5     xmlns:ui="http://java.sun.com/jsf/facelets"

6     xmlns:c="http://java.sun.com/jsp/jstl/core">

7

9     

CDI Sample Test

10 

11 

#{newProduct.toString()}

12 

13 

注意:这里我们并没有任何的Controller,Resouces类本身也没有使用@Named之类的注解,只是在方法getNewProduct上使用了 @Produces、 @Named,页面上就可以直接使用资源池中的对象了.

AAffA0nNPuCLAAAAAElFTkSuQmCC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值