通过 CDI 在运行时自动注入实现

Injectind Implementation automatically on runtime over CDI

我正在尝试找出 CDI 和最适合我需要的方法。 我有一个与纯 tcp 通信交互的服务 (TcpServiceImpl)。现在这个服务有一些地方需要通知某人发生了什么事。对于此信息,我有接口 TcpConnection 需要将 CDI 注入到正确的实现中。另一个问题是服务TcpServiceImpl本身被注入到一个定期执行的作业(TcpConnectionJob)中并调用服务来做事。 这意味着服务 TcpServiceImpl 将存在多次。每个都有它处理的另一个 tcp 连接,并且有另一个设备需要另一个 driver/protocol 注入接口 TcpConnection

让我展示一下参与这个场景的三个元素:

这是将获得多个实现的接口:

public interface TcpConnection
{

  /**
   * Connected.
   *
   * @throws NGException the NG exception
   */
  public void connected() throws NGException;

  /**
   * This method will send the received data from the InputStream of the connection.
   *
   * @param data the received data
   * @throws NGException the  NG exception
   */
  public void received( byte[] data ) throws NGException;

  /**
   * Usable for the protocol to send data to the device.
   *
   * @param data the data to send to the device ( Will be converted to byte[] with getBytes() )
   * @throws NGException the  NG exception
   */
  public void send( String data ) throws NGException;

  /**
   * Usable for the protocol to send data to the device.
   *
   * @param data the data to send to the device ( Will be send as is )
   * @throws NGException the NG exception
   */
  public void send( byte[] data ) throws NGException;

  /**
   * This method will inform the protocol that the connection got closed.
   *
   * @throws NGException the NG exception
   */
  public void closed() throws NGException;
}

这里还有一个示例片段,说明何时会在我现有的服务中调用它:

public class TCPServiceImpl implements TCPService, Runnable
{
/** The callback. */
private TcpConnection callback;
private void disconnect()
{
  connection.disconnect();
  if ( !getStatus( jndiName ).equals( ConnectionStatus.FAILURE ) )
  {
     setStatus( ConnectionStatus.CLOSED );
  }
  /* TODO: Tell driver connection is closed! */
  callback.closed();
}
}

下面是调用服务的class,然后需要为接口动态注入正确的实现。

public class TcpConnectionJob implements JobRunnable
{
  /** The service. */
  private TCPService service;

  public void execute()
  {
    service.checkConnection( connection );
  }
}

服务注入 callback 必须链接到正确的 "protocol" 或 "driver" 的实现,这将转换数据或处理设备的逻辑。将有多个接口的驱动程序实现不同,我需要注入正确的一个。该决定的限定符可以是设备的名称。现在我查看了以下链接:

Understanding the necessity of type Safety in CDI

How to programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class

How to use CDI qualifiers with multiple class implementations?

问题:

但我仍然不确定使用哪个way/method以及正确的方法。任何帮助将不胜感激。

我的第一个想法是将我的界面复制到一个限定符界面,然后附加这个可以进入限定符的界面。这是个好主意吗?

使用 CDI 事件,不要打扰回调。部分资源:

https://docs.oracle.com/javaee/7/tutorial/cdi-adv005.htm

http://www.adam-bien.com/roller/abien/entry/java_ee_6_observer_with

http://www.next-presso.com/2014/06/you-think-you-know-everything-about-cdi-events-think-again/

所以这是我想出的解决方案。现在唯一的问题是让回调工作,但这是不同的。这是对我有用的解决方案:

/**
 * The Qualifier interface TcpDriver. The value of this annotation is the name the implementation
 * is found under. Please only enter values that are configured in the wildfly config as the name of
 * the device.
 */
@Documented
@Qualifier
@Retention( RUNTIME )
@Target( { TYPE, FIELD, METHOD, PARAMETER } )
public @interface TcpDriver
{

  /**
   * Value.
   *
   * @return the string
   */
  String value();
}

Qualifier 接口的默认实现:

/**
 * The Class TcpDriverImpl.
 */
public class TcpDriverImpl extends AnnotationLiteral<TcpDriver> implements TcpDriver
{

  /** The Constant serialVersionUID. */
  private static final long serialVersionUID = 1L;

  /** The name. */
  private final String name;

  /**
   * Instantiates a new tcp driver impl.
   *
   * @param name the name
   */
  public TcpDriverImpl( final String name )
  {
    this.name = name;
  }

  /** {@inheritDoc} */
  @Override
  public String value()
  {
    return name;
  }

}

现在一个测试实现来测试它:

@TcpDriver( "terminal1" )
@Dependent
public class TestDriverImpl implements TcpConnection
{

  /** The log. */
  private Log log;

  @Inject
  public void init( Log log )
  {
    this.log = log;
  }

  @Override
  public void connected() throws NGException
  {
    // TODO Auto-generated method stub
    log.info( "IT WORKS!!" );
  }

  @Override
  public void received( byte[] data ) throws NGException
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void send( String data ) throws NGException
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void send( byte[] data ) throws NGException
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void closed() throws NGException
  {
    // TODO Auto-generated method stub
    log.info( "BYE BYE" );
  }

}

最后但同样重要的是,我在服务中注入所有这些的方式:

  /** The callback Instance for the driver to find. */
  @Inject
  @Any
  private Instance<TcpConnection> callback;

  private TcpConnection driver;
  /**
  * Inject driver.
  */
  private void injectDriver()
  {
    final TcpDriver driver = new TcpDriverImpl( name );
    this.driver = callback.select( driver ).get();
  }

我希望这对有相同要求的人有所帮助。

PS:如果您检查测试实现中的日志输出然后查看日志,则显示它有效的小日志:)

2017-02-28 08:37:00,011 INFO  starting TCPConnection: TcpDevice1 with status: NOT_CONNECTED
2017-02-28 08:37:00,018 INFO  initializing terminal1
2017-02-28 08:37:00,019 INFO  Creating socket for: terminal1 with port: XXXXX
2017-02-28 08:37:00,023 INFO  Updated Status to CONNECTED for connection TcpDevice1
2017-02-28 08:37:00,024 INFO  opened connection to terminal1
2017-02-28 08:37:00,026 INFO  (terminal1) IT WORKS!!
2017-02-28 08:37:00,038 INFO  (terminal1) terminal1: In threaded method run
2017-02-28 08:37:00,039 INFO  (terminal1) waiting for data...
2017-02-28 08:39:00,045 INFO  (terminal1) Socket closed!
2017-02-28 08:39:00,045 INFO  (terminal1) BYE BYE