如何使用Spring初始化Jersey应用程序(ResourceConfig)?

时间:2014-01-08 14:54:40

标签: spring jersey jersey-2.0

我正在使用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之前初始化。

2 个答案:

答案 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,并仔细查看初始化期间可能发生的任何线程问题。