如何在WSS4JOutInterceptor的CallbackHandler中注入@AppConfigProperty
问题解答
你的猜想完全正确。
原因分析
当你通过ConfigurationConstants.PW_CALLBACK_CLASS指定InternalServicePasswordCallback的类名时,WSS4J会直接通过反射自行实例化这个回调类,整个过程完全脱离JBoss/Weld CDI容器的管理。CDI的依赖注入仅对由容器创建和托管的Bean实例生效,因此你注入的username字段始终为null。
解决方案
要让CDI注入生效,需要让WSS4J使用CDI托管的回调实例,而不是让它自行创建。具体有两种简单可行的方式:
方式1:直接传入CDI托管的回调实例
- 首先确保
InternalServicePasswordCallback被CDI识别为托管Bean,给它添加CDI作用域注解,比如@Dependent(最适合回调类的作用域):
import jakarta.enterprise.context.Dependent; import jakarta.inject.Inject; @Dependent public class InternalServicePasswordCallback implements CallbackHandler { private static final String APP_PROPERTIES = "app.properties"; @Inject @AppConfigProperty("USERNAME") private String username; // 原构造方法和handle方法保持不变 }
- 在调用
setEndPoint的类中,注入这个回调实例:
@Inject private InternalServicePasswordCallback passwordCallback;
- 修改WSS4J的配置,替换
PW_CALLBACK_CLASS为PW_CALLBACK_REF,直接传入已注入好的实例:
private void setEndPoint(Object port, String endPoint) { BindingProvider bp = (BindingProvider) port; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPoint); Client client = ClientProxy.getClient(port); Map<String, Object> props = new HashMap<>(); props.put(ConfigurationConstants.ACTION, ConfigurationConstants.USERNAME_TOKEN); props.put(ConfigurationConstants.PASSWORD_TYPE, WSS4JConstants.PW_TEXT); // 替换为传入CDI托管的实例 props.put(ConfigurationConstants.PW_CALLBACK_REF, passwordCallback); props.put(ConfigurationConstants.USER, user); WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props); Endpoint endpoint = client.getEndpoint(); endpoint.getOutInterceptors().add(wssOut); client.getOutInterceptors().add(new LoggingOutInterceptor(-1)); client.getInInterceptors().add(new LoggingInInterceptor(-1)); }
方式2:自定义回调工厂(可选,适合复杂场景)
如果无法直接注入实例,可以自定义一个CallbackHandlerFactory,通过CDI的BeanManager获取回调实例,再配置给WSS4J:
public class CDICallbackHandlerFactory implements CallbackHandlerFactory { @Override public CallbackHandler createCallbackHandler(Map<String, Object> props) { BeanManager beanManager = CDI.current().getBeanManager(); return beanManager.createInstance().select(InternalServicePasswordCallback.class).get(); } }
然后在WSS4J配置中指定工厂类:
props.put(ConfigurationConstants.CALLBACK_HANDLER_FACTORY, CDICallbackHandlerFactory.class.getName());
注意事项
- 确保你的应用归档(WAR/JAR)包含
beans.xml文件(即使是空文件),让CDI能够扫描并识别托管Bean。 - 如果是在批处理作业环境中调用,需要确保当前上下文处于CDI活动范围内(JBoss EAP的批处理组件通常会自动处理,但如果遇到问题,可以通过
CDI.current()手动获取上下文)。
内容的提问来源于stack exchange,提问作者Cristian-Florin Pandele
相关产品推荐
相关产品推荐

