我正在使用Jersey 2和Spring,我正在尝试使用Spring上下文中的参数初始化我的Jersey应用程序(即从ResourceConfig派生的类)。
背景:我构建了一个Jersey应用程序(即单个WAR),并将其部署在服务器集群中,在不同服务器上具有不同的Spring配置,以启用或禁用服务器的不同部分,例如:一些服务器启用了/search
个资源,等等。这在泽西岛1.0中非常简单:我只是说,
<context:component-scan base-package="com.mycompany.resources.search"/>
在Spring配置中让Jersey扫描该特定包,并在其中启用JAX-RS资源提供程序。
现在在Jersey 2.0中,Spring <context:component-scan ... />
不起作用,因此必须在从ResourceConfig
派生的启动类中以编程方式注册资源:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.resources.search");
}
}
到目前为止一切顺利,但我需要有条件地扫描该软件包,我无法弄清楚如何将任何Spring配置放入MyApplication
类。我认为构造函数注入可能有效:
public class MyApplication extends ResourceConfig {
@Autowired
public MyApplication(@Qualifier("my-config") MyConfiguration myConfiguration) {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
然而HK2抱怨它找不到默认的构造函数......所以这向我表明DI在构造这个类时起作用,但是DI没有使用Spring。
同样,使用Spring bean生命周期不起作用:
public class MyApplication extends ResourceConfig implements InitializingBean {
@Autowired
private MyConfiguration myConfiguration;
public MyApplication() {
}
@Override
public void afterPropertiesSet() throws Exception {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
(未调用afterPropertiesSet
方法。)
所以现在我陷入困境:有没有办法使用Spring配置Jersey ResourceConfig
应用程序对象?
更新
我在下面接受了@ JohnR的答案,但我还要包括我认为更清洁的最终解决方案。 @ JohnR的答案是将对象初始化两次:首先是Spring,然后是Jersey / HK2。当Spring初始化对象时,您将依赖项缓存在静态成员中,然后当Jersey / HK2稍后初始化它时,您可以检索依赖项。
我最终这样做了:
public class MyApplication extends ResourceConfig {
public MyApplication() {
ApplicationContext rootCtx = ContextLoader.getCurrentWebApplicationContext();
MyConfiguration myConfiguration = rootCtx.getBean(MyConfiguration.class);
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.whatever");
}
}
}
我们让Jersey / HK2初始化它,而不是让对象初始化两次,然后我们从Spring中检索依赖项。
两种解决方案都容易受到时间限制:他们都认为Spring在Jersey / HK2之前初始化。
答案 0 :(得分:1)
扩展我之前的评论:
如果您不知道自己在做什么,尝试扩展ResourceConfig是危险的。 Jersey变得不可预测,如果您尝试将其子类化为Abstract类,Jersey将会崩溃。
相反,JAX-RS规范为我们提供了一个非常有用的界面,该界面称为功能:它允许您注册所需的任何类,就像在配置自己的应用程序一样。此外,您无需使用笨拙的AbstractBinder,只需指定与您的类进行注册的合同即可。
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
// Don't use @Component here, we need to inject the Spring context manually.
public class MySpringFeature implements Feature {
@Context
private ServletContext servletContext;
private ApplicationContext applicationContext;
@Autowired
private MySecurityDAO mySecurityDAO;
@Autowired
private MySpringResponseFilter myResponseFilter;
@Override
public boolean configure(FeatureContext context) {
if(this.servletContext == null) {
return false; // ERROR!
}
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(this.applicationContext == null) {
return false; // ERROR!
}
// This is where the magic happens!
AutowireCapableBeanFactory bf = applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
// From here you can get all the beans you need
// Now we take a Spring bean instance,
// and register it with its appropriate JAX-RS contract
context.register(myResponseFilter, ContainerResponseFilter.class);
// Or, we could do this instead:
SomeSecurityFilter mySecurityFilter = new SomeSecurityFilter();
mySecurityFilter.setSecurityDAO(mySecurityDAO);
context.register(mySegurityFilter, ContainerRequestFilter.class);
// Or even this:
SomeOtherSpringBean someOtherBean = applicationContext.getBean(SomeOtherSpringBean.class);
context.register(someOtherBean, SomeOtherJerseyContract.class);
// Success!
return true;
}
}
在您的ResourceConfig中:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
Ta-da!
答案 1 :(得分:0)
所以现在我卡住了:有没有办法配置泽西岛 使用Spring的ResourceConfig应用程序对象?
我认为您不能将Jersey配置为从Spring获取您的ResourceConfig作为Spring托管bean。这有点hackish,但你可以做这样的事情。请注意,最终会得到两个ResourceConfig实例:一个由Spring管理,另一个由Jersey管理:
public class MyApplication extends ResourceConfig {
// static, available to all instances
private static MyConfiguration myConfiguration;
public MyApplication() {
// when Spring creates the first instance of MyApplication, myConfiguration
// will be null because the setter wasn't called yet
if (myConfiguration != null)
{
// second instance created by jersey... Spring will have autowired
// the first instance, and myConfiguration is static
if (myConfiguration.isEnabled())
packages("com.mycompany.resources.search");
}
}
@Autowired
public void setMyConfiguration(MyConfiguration config)
{
// instance level setter saves to a static variable to make it available for
// future instances (i.e. the one created by jersey)
MyApplication.myConfiguration = config;
}
}
同样,这是相当hackish。您需要确保在Jersey之前初始化Spring,并仔细查看初始化期间可能发生的任何线程问题。