如何使用Spring初始化Jersey应用程序(ResourceConfig)?
我使用的是Jersey 2和Spring,我试图用Spring上下文中的参数初始化Jersey应用程序(即从ResourceConfig派生的类) 背景:我构建了一个Jersey应用程序(即一个WAR),并在不同服务器上使用不同的Spring配置跨服务器集群部署它,以启用或禁用服务器的不同部分,例如,一些服务器启用了如何使用Spring初始化Jersey应用程序(ResourceConfig)?,spring,jersey,jersey-2.0,Spring,Jersey,Jersey 2.0,我使用的是Jersey 2和Spring,我试图用Spring上下文中的参数初始化Jersey应用程序(即从ResourceConfig派生的类) 背景:我构建了一个Jersey应用程序(即一个WAR),并在不同服务器上使用不同的Spring配置跨服务器集群部署它,以启用或禁用服务器的不同部分,例如,一些服务器启用了/search资源,等等,这在泽西1.0中真的很容易:我刚才说 <context:component-scan base-package="com.mycompany.reso
/search
资源,等等,这在泽西1.0中真的很容易:我刚才说
<context:component-scan base-package="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
类似地,在SpringBean生命周期中使用
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");
}
}
}
(未调用afterPropertieSet
方法。)
所以现在我被卡住了:有没有办法使用Spring配置JerseyResourceConfig
应用程序对象?
更新:
我在下面接受了@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之前初始化
所以现在我陷入了困境:有没有办法配置球衣
使用Spring的ResourceConfig应用程序对象
我认为您不能将Jersey配置为从Spring获取ResourceConfig作为Spring管理的bean。这有点粗俗,但你可以这样做。注意,您将得到两个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;
}
}
再说一次,这是相当骇人听闻的。您需要确保Spring在Jersey之前已初始化,并仔细查看初始化过程中可能出现的任何线程问题。展开我之前的评论: 如果您不知道自己在做什么,那么尝试扩展ResourceConfig是危险的。Jersey变得不可预测,如果您试图将其子类化为抽象类,Jersey将崩溃 相反,JAX-RS规范为我们提供了一个非常有用的接口,名为Feature:它允许您注册所需的任何类,就像您正在配置自己的应用程序一样。此外,您不需要使用笨拙的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;
}
}
在您的资源配置中:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
塔达 我在泽西2+春季遇到了类似的问题。最后,我完成了自己的集成。帮助?这是一个很好的答案。非常不幸的是,你不得不推出自己的球衣/春桥。可悲的是,我的经历也很相似:在泽西2和春天浪费了很多时间。我现在已经开始跑步了,但jersey2 spring bridge似乎有一条狭窄的“快乐之路”,如果你迷路了,会有很多陷阱。我对其他从事泽西2号和斯普林号的人的建议是:遵循规则,不要尝试做任何不同的事情,因为它不会起作用。我已经设法解决了这个问题。如果您确实需要使用Spring类配置应用程序,请不要扩展ResourceConfig,这是一项专有功能。相反,实现javax.ws.rs.core.Feature,从中可以使用@Context ServletContext字段获取Spring的ApplicationContext。这会帮你省去很多麻烦。然后您只需从ResourceConfig类注册您的功能,然后ta da!(您可以使用Jersey-Spring4依赖项,但在我的情况下,我不能使用,因为我被迫使用Jersey 2.21,但没有这样的包。)
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}