zoukankan      html  css  js  c++  java
  • JAVA CDI 学习(3)

    上一节学习了注入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的生命周期在加入了这二个注解后,其执行顺序如下:

    所以,当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 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
     2 <html xmlns="http://www.w3.org/1999/xhtml"
     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 
     7 <h:head>
     8     <title>Connection Test</title>
     9 </h:head> 
    10 <body> 
    11     #{Conn.connection.doSomething()}
    12 </body> 
    13 </html>
    conn.xhtml

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

    跟预想的完全一样! 条条道路通罗马,解决问题的途径往往不止一条,或许有些人不喜欢在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 }
    ConnectionFactory

    注:关注一下@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、修改ConnectionController

    1     @Inject    
    2     @MyConnection
    3     private Connection connection;
    View Code

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

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

    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 }
    Product

    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 }
    Resouces

    8.3 然后在页面上就可以直接使用了

     1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     2 <html xmlns="http://www.w3.org/1999/xhtml"
     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 
     8 <h:head>
     9     <title>CDI Sample Test</title>
    10 </h:head>
    11 <body>#{newProduct.toString()}
    12 </body>
    13 </html>
    index.xhtml

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

  • 相关阅读:
    由PhysicalFileProvider构建的物理文件系统
    Net Core WebApi单元测试
    多个项目使用NET Core
    ReactNative
    定制样式插入到ueditor
    ES6的Class
    Redis存储Session
    二叉 查找树 排序树 搜索树
    SignalR实现实时日志监控
    KNN(k-nearest neighbor的缩写)又叫最近邻算法
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/javaee-cdi-produces-disposes.html
Copyright © 2011-2022 走看看