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

    前面几节学习到的CDI内容,基本上都是hard-code,以硬编码的方式在代码里指定注入类型,这并非依赖注入的本意,依赖注入的优势之一在于“解耦”,这一节我们将学习如何利用配置来动态注入的类型及属性初始化。

    一、@Alternative/@Default/@Any

    当一个服务接口(也称契约)有多个实现时,可以在代码里指定一个缺省的实现类型(即:标注成@Default或@Any),其它实现类标注成@Alternative,以后如果需要动态切换实现类,只要在webapp/WEB-INF/beans.xml中配置即可。

    1.1 新建二个示例接口

    1 package contract;
    2 
    3 public interface Connection {
    4 
    5     String connect();
    6 
    7 }
    Connection

    该接口模拟db连接,里面有一个connect方法,用来连接db.

    1 package contract;
    2 
    3 public interface DriveService {
    4     
    5     String drive();
    6 
    7 }
    DriveService

    该接口模拟游戏应用中,有些人物具有驾驶技能。

    1.2 提供接口实现

    假设Connection有二个实现,一个用来连接到Oracle Database,另一个用来连接Microsoft Sql Server

     1 package contract.impl;
     2 
     3 import javax.enterprise.inject.Default;
     4 
     5 import contract.Connection;
     6 
     7 @Default
     8 public class OracleConnection implements Connection {
     9 
    10     @Override
    11     public String connect() {
    12 
    13         return "Oracle Database is connecting...";
    14     }
    15 
    16 }
    OracleConnection
     1 package contract.impl;
     2 
     3 import javax.enterprise.inject.Alternative;
     4 
     5 import contract.Connection;
     6 
     7 @Alternative
     8 public class SqlServerConnection implements Connection {
     9 
    10     @Override
    11     public String connect() {
    12 
    13         return "Microsoft SqlServer is connecting...";
    14     }
    15 
    16 }
    SqlServerConnection

    注:OracleConnection上应用了注解@Default,表示这是接口Connection的默认实现类(@Default实质上是系统的默认注解,其实也可以省略,系统会自动默认为@Default);SqlServerConnection上应用了注解@Alternative,表示它是候选项,俗称:备胎:),所有非@Default的实现类,都必须标识@Alternative,否则注入时,会提示“不明确的类型”

    再来看DriveService的实现,我们提供三种实现:驾驶汽车、摩托车、拖拉机

     1 package contract.impl;
     2 
     3 import contract.DriveService;
     4 
     5 public class CarDriveImpl implements DriveService {
     6 
     7     @Override
     8     public String drive() {
     9         String msg = "Drive a car...";
    10         System.out.println(msg);
    11         return msg;
    12     }
    13 
    14 }
    CarDriveImpl
     1 package contract.impl;
     2 
     3 import javax.enterprise.inject.Alternative;
     4 
     5 import contract.DriveService;
     6 
     7 @Alternative
     8 public class MotorcycleDriveImpl implements DriveService {
     9 
    10     @Override
    11     public String drive() {
    12         String msg = "Drive a motocycle...";
    13         System.out.println(msg);
    14         return msg;
    15     }
    16 
    17 }
    MotorcycleDriveImpl
     1 package contract.impl;
     2 
     3 import javax.enterprise.inject.Alternative;
     4 
     5 import contract.DriveService;
     6 
     7 @Alternative
     8 public class TractorDriveImpl implements DriveService {
     9 
    10     @Override
    11     public String drive() {
    12         String msg = "Drive a tractor...";
    13         System.out.println(msg);
    14         return msg;
    15     }
    16 
    17 }
    TractorDriveImpl

    注:MotocycleDriveImpl、TractorDriveImpl这二个类使用了@Alternative,即它们俩是候选,剩下的CarDriveImpl上未使用任何注解,即默认的@Default

    1.3 编写Controller类

     1 package controller;
     2 
     3 import javax.inject.Inject;
     4 import javax.inject.Named;
     5 
     6 import contract.Connection;
     7 
     8 @Named("Conn")
     9 public class ConnectionController {
    10 
    11     @Inject
    12     private Connection conn;
    13 
    14     public Connection getConn() {
    15         return conn;
    16     }
    17 
    18 }
    ConnectionController
     1 package controller;
     2 
     3 import javax.enterprise.inject.*;
     4 import javax.inject.Inject;
     5 import javax.inject.Named;
     6 
     7 import contract.DriveService;
     8 
     9 @Named("Drive")
    10 public class DriveController {
    11 
    12     @Inject
    13     private DriveService driveService;
    14 
    15     public DriveService getDriveService() {
    16         return driveService;
    17     }
    18 
    19     @Inject
    20     @Any
    21     private Instance<DriveService> anySerInstance;
    22 
    23     public DriveService getAnySerInstance() {
    24         return anySerInstance.get();
    25     }
    26 
    27 }
    DriveController

    注:DriveController中anySerInstance成员上使用了@Any,从本例最终使用的效果上看,它跟@Default一样,只不过细节要留意一下,需要使用Instance<T>接口,这点跟@Default有点不同。

    1.4 UI层

     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>CDI - Alternative/Default/Any</title>
     9 </h:head> 
    10 <body> 
    11     #{Drive.driveService.drive()}
    12     <br />
    13     <br />#{Drive.anySerInstance.drive()}
    14     <br />
    15     <br /> #{Conn.conn.connect()}
    16 </body> 
    17 </html>
    Index.xhtml

    运行结果:

    修改beans.xml的内容如下:

    1 <?xml version="1.0" encoding="UTF-8"?>
    2 
    3 <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4     xsi:schemaLocation="         http://java.sun.com/xml/ns/javaee          http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    5     <alternatives>
    6         <class>contract.impl.SqlServerConnection</class>
    7         <class>contract.impl.TractorDriveImpl</class>
    8     </alternatives>
    9 </beans>
    beans.xml

    重新在Jboss里部署、运行,结果如下:

    在不修改java源代码的前提下,仅通过配置文件beans.xml的修改,就动态切换了接口的实现类。

    二、Extension

    不仅注入的类型可以由配置文件来动态切换,也可以由配置文件来直接初始化注入对象的属性值(虽然我个人认为这种场景在实际开发中其实并不多见)

    2.1 先来定义几个类:

    BaseDto.java

     1 package dto;
     2 
     3 import java.io.Serializable;
     4 
     5 public class BaseDto implements Serializable {
     6 
     7     private static final long serialVersionUID = 804047416541420712L;
     8 
     9     public BaseDto() {
    10         System.out.println("BaseDto's constructor is called...");
    11 
    12     }
    13 
    14 }
    BaseDto
     1 package dto;
     2 
     3 @DtoType(ProductType.Product)
     4 public class Product extends BaseDto {
     5 
     6     public Product() {
     7         System.out.println("Product's constructor is called...");
     8 
     9     }
    10 
    11     private static final long serialVersionUID = 7364741422914624828L;
    12     private String productNo;
    13     private String productName;
    14 
    15     public String getProductName() {
    16         return productName;
    17     }
    18 
    19     public void setProductName(String productName) {
    20         this.productName = productName;
    21     }
    22 
    23     public String getProductNo() {
    24         return productNo;
    25     }
    26 
    27     public void setProductNo(String productNo) {
    28         this.productNo = productNo;
    29     }
    30 
    31     @Override
    32     public String toString() {
    33         return "productNo:" + productNo + " , productName: " + productName
    34                 + " , serialVersionUID:" + serialVersionUID;
    35     }
    36 }
    Product
     1 package dto;
     2 
     3 //@DtoType(ProductType.Computer)
     4 public class Computer extends Product {
     5 
     6     public Computer() {
     7         System.out.println("Computer's constructor is called...");
     8     }
     9 
    10     private static final long serialVersionUID = -5323881568748028893L;
    11 
    12     private String cpuType;
    13 
    14     private int hardDiskCapacity;
    15 
    16     public String getCpuType() {
    17         return cpuType;
    18     }
    19 
    20     public void setCpuType(String cpuType) {
    21         this.cpuType = cpuType;
    22     }
    23 
    24     public int getHardDiskCapacity() {
    25         return hardDiskCapacity;
    26     }
    27 
    28     public void setHardDiskCapacity(int hardDiskCapacity) {
    29         this.hardDiskCapacity = hardDiskCapacity;
    30     }
    31 
    32     @Override
    33     public String toString() {
    34         return "productNo:" + getProductNo() + " , productName: "
    35                 + getProductName() + " , cpuType:" + getCpuType()
    36                 + " , hardDiskCapacity: " + getHardDiskCapacity()
    37                 + " , serialVersionUID:" + serialVersionUID;
    38     }
    39 }
    Computer
     1 package dto;
     2 
     3 //@DtoType(ProductType.Cloth)
     4 public class Cloth extends Product {
     5 
     6     private static final long serialVersionUID = -8799705022666106476L;
     7     private String brand;
     8 
     9     public String getBrand() {
    10         return brand;
    11     }
    12 
    13     public void setBrand(String brand) {
    14         this.brand = brand;
    15     }
    16 
    17     @Override
    18     public String toString() {
    19         return "productNo:" + getProductNo() + " , productName: "
    20                 + getProductName() + " , brand:" + getBrand()
    21                 + " , serialVersionUID:" + serialVersionUID;
    22     }
    23 
    24 }
    Cloth

    Product上使用了一个自定义的注解:@DtoType

     1 package dto;
     2 
     3 import javax.inject.Qualifier;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 
     7 @Qualifier
     8 @Retention(RetentionPolicy.RUNTIME)
     9 public @interface DtoType {
    10 
    11     public ProductType value();
    12 
    13 }
    @DtoType

    以及枚举:

    1 package dto;
    2 
    3 public enum ProductType {
    4     Product,Computer,Cloth
    5 }
    ProductType

    2.2 BaseDtoExtension

    为了实现注入配置化,我们还需要对BaseDto写一个扩展类:

     1 package dto.extension;
     2 
     3 import java.io.IOException;
     4 import java.io.InputStream;
     5 import java.util.logging.Logger;
     6 
     7 import javax.enterprise.event.Observes;
     8 
     9 import javax.enterprise.inject.spi.*;
    10 import javax.xml.parsers.*;
    11 
    12 import dto.*;
    13 import org.w3c.dom.*;
    14 
    15 import org.xml.sax.SAXException;
    16 
    17 
    18 public class BaseDtoExtension implements Extension {
    19     private final Document document;
    20     private final Logger log = Logger.getLogger(BaseDtoExtension.class
    21             .getName());
    22 
    23     public BaseDtoExtension() {
    24         try {
    25             InputStream creatureDefs = BaseDtoExtension.class.getClassLoader()
    26                     .getResourceAsStream("inject-beans.xml");
    27             DocumentBuilderFactory factory = DocumentBuilderFactory
    28                     .newInstance();
    29             DocumentBuilder builder = factory.newDocumentBuilder();
    30             document = builder.parse(creatureDefs);
    31         } catch (ParserConfigurationException e) {
    32             throw new RuntimeException("Error building xml parser, aborting", e);
    33         } catch (SAXException e) {
    34             throw new RuntimeException("SAX exception while parsing xml file",
    35                     e);
    36         } catch (IOException e) {
    37             throw new RuntimeException("Error reading or parsing xml file", e);
    38         }
    39     }
    40 
    41 
    42     <X extends BaseDto> void processInjectionTarget(
    43             @Observes ProcessInjectionTarget<X> pit) {
    44         Class<? extends BaseDto> klass = pit.getAnnotatedType().getJavaClass();
    45         log.info("Setting up injection target for " + klass);
    46         final Element entry = (Element) document.getElementsByTagName(
    47                 klass.getSimpleName().toLowerCase()).item(0);
    48         pit.setInjectionTarget(new XmlWrappedInjection<X>(pit
    49                 .getInjectionTarget(), entry));
    50     }
    51 }
    BaseDtoExtension

    该扩展,将读取resources/inject-beans.xml文件的内容,并完成BaseDto以及所有子类的加载,包括Inject,该类还使用了另一个辅助类:

     1 package dto.extension;
     2 
     3 import java.lang.reflect.Field;
     4 import java.util.Set;
     5 
     6 import javax.enterprise.context.spi.CreationalContext;
     7 import javax.enterprise.inject.InjectionException;
     8 import javax.enterprise.inject.spi.*;
     9 
    10 import dto.*;
    11 import org.w3c.dom.Element;
    12 
    13 public class XmlWrappedInjection<X extends BaseDto> implements
    14         InjectionTarget<X> {
    15     private final InjectionTarget<X> wrapped;
    16     private final Element xmlBacking;
    17 
    18     public XmlWrappedInjection(InjectionTarget<X> it, Element xmlElement) {
    19         wrapped = it;
    20         xmlBacking = xmlElement;
    21     }
    22 
    23     @Override
    24     public void inject(X instance, CreationalContext<X> ctx) {
    25         wrapped.inject(instance, ctx);
    26 
    27         final Class<? extends BaseDto> klass = instance.getClass();
    28         //yjm注:出于演示目的,这里仅反射了本类中声明的field,所以注入时,父类中的field会被忽略,大家可以自行修改,逐层向上反射,直到BaseDto类为止
    29         for (Field field : klass.getDeclaredFields()) {
    30             field.setAccessible(true);
    31             final String fieldValueFromXml = xmlBacking.getAttribute(field
    32                     .getName());
    33             try {
    34                 //System.out.println("the filed name is :" + field.getName());
    35                 if (field.getName().toLowerCase().equals("serialversionuid")) {
    36                     continue;
    37                 }
    38                 //注:出于演示目的,这里只处理了int、long、String这三种类型,其它类型大家可自行扩展
    39                 if (field.getType().isAssignableFrom(Integer.TYPE)) {
    40                     field.set(instance, Integer.parseInt(fieldValueFromXml));
    41                 } else if (field.getType().isAssignableFrom(Long.TYPE)) {
    42                     field.set(instance, Long.parseLong(fieldValueFromXml));
    43                 } else if (field.getType().isAssignableFrom(String.class)) {
    44                     field.set(instance, fieldValueFromXml);
    45                 } else {
    46                     throw new InjectionException("Cannot convert to type "
    47                             + field.getType());
    48                 }
    49             } catch (IllegalAccessException e) {
    50                 throw new InjectionException("Cannot access field " + field);
    51             }
    52         }
    53     }
    54 
    55     @Override
    56     public void postConstruct(X instance) {
    57         wrapped.postConstruct(instance);
    58     }
    59 
    60     @Override
    61     public void preDestroy(X instance) {
    62         wrapped.preDestroy(instance);
    63     }
    64 
    65     @Override
    66     public X produce(CreationalContext<X> ctx) {
    67         return wrapped.produce(ctx);
    68     }
    69 
    70     @Override
    71     public void dispose(X instance) {
    72         wrapped.dispose(instance);
    73     }
    74 
    75     @Override
    76     public Set<InjectionPoint> getInjectionPoints() {
    77         return wrapped.getInjectionPoints();
    78     }
    79 }
    XmlWrappedInjection

    注:这里仅只是演示,所以处理得相对比较简单,如果一个类继承自父类,Inject时,上面的代码,只反射了子类本身声明的field,对于父类的属性,未逐层向上反射,大家可以自行改进。

    2.3 控制器

     1 package controller;
     2 
     3 import javax.inject.Inject;
     4 import javax.inject.Named;
     5 
     6 import dto.Cloth;
     7 import dto.Computer;
     8 import dto.DtoType;
     9 import dto.Product;
    10 import dto.ProductType;
    11 
    12 @Named("Ext")
    13 public class ExtensionController {
    14 
    15     @Inject
    16     //@DtoType(ProductType.Computer)
    17     private Computer computer;
    18 
    19     @Inject
    20     //@DtoType(ProductType.Cloth)
    21     private Cloth cloth;
    22 
    23     @Inject
    24     @DtoType(ProductType.Product)
    25     private Product product;
    26 
    27     public Computer getComputer() {
    28         return computer;
    29     }
    30 
    31     public Cloth getCloth() {
    32         return cloth;
    33     }
    34 
    35     public Product getProduct() {
    36         return product;
    37     }
    38 
    39 }
    ExtensionController

    注:这里思考一下,为什么Product上必须使用注解@DtoType(ProductType.Product),而其它二个Inject的field不需要?如果暂时没想明白的朋友,建议回到第一节 ,看下1.7节的内容,因为Computer、Cloth都继承自Product类,所以在实例Product类时,系统有3个选择:Computer、Cloth、Product,它不知道该选哪一个?所以运行时,系统会罢工,so,需要额外的注释给它一点提示。

    2.4 ext.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>Extension Test</title>
     9 </h:head>
    10 <body>
    11     #{Ext.product.toString()}
    12     <br /> #{Ext.computer.toString()}
    13     <br /> #{Ext.cloth.toString()}
    14 
    15 </body>
    16 </html>
    ext.xhtml

    2.5 inject-beans.xml

    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <basedtos>
    3     <product productNo="001" productName="A Unknown New Product" />
    4     <computer productNo="T60" productName="ThinkPad" cpuType="2-cores"
    5         hardDiskCapacity="320" />
    6     <cloth productNo="XX" productName="Underware" brand="JackJohns" />
    7 </basedtos>
    inject-beans.xml

    该文件设计时,要放在main/java/resources/目录下,部署时,会自动复制到webapp/resources/

    2.6 javax.enterprise.inject.spi.Extension

    /main/java/resources/META-INF/services目录下,新建一个文件:javax.enterprise.inject.spi.Extension,内容如下:

    dto.extension.BaseDtoExtension

    该文件的作用是在运行时,告诉系统根据BaseDtoExtension类的定义去找inject-beans.xml,它相当于入口。

    2.7 运行效果:浏览地址 http://localhost:8080/cdi-alternative-sample/ext.jsf

    跟预期结果完全一样,不过正如文中指出的一样,父类的属性被忽略了,如果父类成员也需要初始化,需要大家自行修改XmlWrappedInjection类

    最后附示例源代码:cdi-alternative-sample.zip

  • 相关阅读:
    CSS 中 Font-Family 中英文对照表
    0号首页,移动应用快捷门户
    身边的大数据和我们的着手点
    php单例模式
    原生JS实现ajax与ajax的跨域请求
    two.js之实现动画效果
    H5中的拖放
    简说chart2.4的应用,以及Uncaught ReferenceError : require is not defined的解决
    JS中的作用域以及全局变量的问题
    JavaScript 框架------------AngularJS(下)
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/javaee-cdi-alternative-extension.html
Copyright © 2011-2022 走看看