通过 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
我正在尝试找出 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