如何仅使用注释配置Jersey with Spring

时间:2013-02-06 05:10:25

标签: spring jersey

我有一个使用Spring和Jersey的Servlet 3.0 Web应用程序。我目前使用在web.xml中配置为过滤器的SpringServlet以及使用@Path@Component注释的资源类进行设置。这是web.xml片段:

<filter>
    <filter-name>jersey-serlvet</filter-name>
    <filter-class>
        com.sun.jersey.spi.spring.container.servlet.SpringServlet
    </filter-class>
    <init-param>
        <param-name>
            com.sun.jersey.config.property.packages
        </param-name>
        <param-value>com.foo;com.bar</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.feature.FilterForwardOn404</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>jersey-serlvet</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

此设置有效,但我真的希望仅使用注释设置此设置 - 没有web.xml配置。我的第一次尝试是删除上面的SpringServlet配置并创建一个扩展Application的类。这是一个片段:

@ApplicationPath("/*")
public class MyApplication extends PackagesResourceConfig {

    public MyApplication() {
        super("com.foo;com.bar");

        HashMap<String, Object> settings = new HashMap<String, Object>(1);
        settings.put(ServletContainer.FEATURE_FILTER_FORWARD_ON_404, true);
        this.setPropertiesAndFeatures(settings);
    }
}

这是因为JAX-RS资源已经注册,我可以在他们的URL上点击它们,但是当他们尝试使用他们的自动装配属性时会抛出NullPointerExceptions ...这是有道理的,因为我猜测资源现在由泽西岛加载并且不是Spring托管bean,因此没有自动装配。

尽管有一些搜索,我找不到任何方式将Jersey资源加载为仅带注释的Spring bean。 有没有这样的方式?我真的不想为资源编写一堆代码来手动获取Spring上下文并在我可以帮助时调用DI。

如果仅注释不起作用,那么如果我可以指定要加载的Application类而不是要扫描的包列表,那么我可以使用web.xml中的过滤器配置。如果我可以删除那里的包列表并只指定一个Application类实例,那么我就会满足。

显然如果有人对我有明确的答案会很好,但我也会感激任何我可以看到的东西或尝试的东西。

谢谢, 马特

6 个答案:

答案 0 :(得分:2)

以下是我的应用程序的一部分,它使用Servlet 3.0,Spring,Jersey 1.8,它没有web.xml:

public class WebAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.setConfigLocation("com.myapp.config");

    final FilterRegistration.Dynamic characterEncodingFilter = servletContext.addFilter("characterEncodingFilter", new CharacterEncodingFilter());
    characterEncodingFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
    characterEncodingFilter.setInitParameter("encoding", "UTF-8");
    characterEncodingFilter.setInitParameter("forceEncoding", "true");

    final FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
    springSecurityFilterChain.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");

    servletContext.addListener(new ContextLoaderListener(context));
    servletContext.setInitParameter("spring.profiles.default", "production");

    final SpringServlet servlet = new SpringServlet();

    final ServletRegistration.Dynamic appServlet = servletContext.addServlet("appServlet", servlet);
    appServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.myapp.api");
    appServlet.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", "com.myapp.api.SizeLimitFilter");
    appServlet.setLoadOnStartup(1);

    final Set<String> mappingConflicts = appServlet.addMapping("/api/*");

    if (!mappingConflicts.isEmpty()) {
        throw new IllegalStateException("'appServlet' cannot be mapped to '/' under Tomcat versions <= 7.0.14");
    }
}

}

答案 1 :(得分:1)

我无法获得理想的结果,但我已经取得了一些进展,所以我会在这里发布,以防它帮助其他人。我能够使用Spring Servlet来指定我的应用程序类,从而从web.xml中删除包列表。

所需的web.xml更改位于init params中(过滤器映射未显示但仍需要):

<filter>
    <filter-name>jersey-serlvet</filter-name>
    <filter-class>
        com.sun.jersey.spi.spring.container.servlet.SpringServlet
    </filter-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name> <!-- Specify application class here -->
        <param-value>com.foo.MyApplication</param-value>
    </init-param>
</filter>

然后在应用程序类中,我不得不改变我稍微调用超级构造函数的方式:

public MyApplication() {
    super("com.foo", "com.bar"); // Pass in packages as separate params

    HashMap<String, Object> settings = new HashMap<String, Object>(1);
    settings.put(ServletContainer.FEATURE_FILTER_FORWARD_ON_404, true);
    this.setPropertiesAndFeatures(settings);
}

仍然不完全是我追求的,但至少这会将更多的配置转换为Java代码和web.xml,这对我来说很重要,因为我试图隐藏这些细节。

答案 2 :(得分:1)

有两种选择可供选择(没有双关语)。

  1. 也许您可以使用自己的类扩展SpringServlet并为其添加适当的servlet 3.0注释。
  2. 继续从SpringServlet切换到Application类的方法,您可以通过启用Spring构建时或加载时字节码编织来解决无自动装配问题。这使Spring能够注入由任何地方实例化的对象,而不仅仅是Spring创建的对象。见"Using AspectJ to dependency inject domain objects with Spring"

答案 3 :(得分:1)

首先,在servlet 3.0容器中,您并不需要web.xml

但是使用Jersey 2.0,您可以设置一个标志来扫描整个Web应用程序以获取带注释的资源:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>jersey</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.servlet.provider.webapp</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

如果包含此jar,将自动启用Spring:

    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>2.3.1</version>
    </dependency>

答案 4 :(得分:0)

我使用Jersey和我之前使用SpringMVC制作的项目。我的代码基于Spring's official documentation

public class WebAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) {
    // Don't create the Listener that Jersey uses to create. 
    // There can only be one linstener
    servletContext.setInitParameter("contextConfigLocation", "<NONE>");
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();

    // Add app config packages
    context.setConfigLocation("config.package");

    // Add listener to the context
    servletContext.addListener(new ContextLoaderListener(context));

    // Replacing:
    //      <servlet-name>ServletName</servlet-name>
    //      <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    //      <init-param>
    //          <param-name>com.sun.jersey.config.property.packages</param-name>
    //          <param-value>webservices.packages</param-value>
    //      </init-param>
    //      <load-on-startup>1</load-on-startup>
    AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();

    ServletRegistration.Dynamic appServlet = servletContext.addServlet("ServletName", new DispatcherServlet(dispatcherContext));
    appServlet.setInitParameter("com.sun.jersey.config.property.packages", "org.sunnycake.aton.controller");
    appServlet.setLoadOnStartup(1);
    appServlet.addMapping("/RootApp");

}
}

config.package中的配置类是:

// Specifies that there will be bean methods annotated with @Bean tag
// and will be managed by Spring
@Configuration

// Equivalent to context:component-scan base-package="..." in the xml, states
// where to find the beans controlled by Spring
@ComponentScan(basePackages = "config.package")
public class AppConfig {

    /**
     * Where will the project views be.
     *
     * @return ViewResolver como el XML
     */
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        return viewResolver;
    }

}

Hibernate配置

// Specifies that there will be bean methods annotated with @Bean tag
// and will be managed by Spring
@Configuration
// Equivalent to Spring's tx in the xml
@EnableTransactionManagement

// Equivalent to context:component-scan base-package="..." in the xml, states
// where to find the beans controlled by Spring
@ComponentScan({"config.package"})

// Here it can be stated some Spring properties with a properties file
@PropertySource(value = {"classpath:aplicacion.properties"})
public class HibernateConfig {

    /**
     * Inyected by Spring based on the .properties file in the
     * \@PropertySource tag.
     */
    @Autowired
    private Environment environment;

    /**
     * Here it's created a Session Factory, equivalent to the Spring's config file one.
     *
     * @return Spring Session factory
     */
    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();

        // Uses the datasource
        sessionFactory.setDataSource(dataSource());

        // Indicates where are the POJOs (DTO)
        sessionFactory.setPackagesToScan(new String[]{"dto.package"});
        // Se asignan las propiedades de Hibernate
        sessionFactory.setHibernateProperties(hibernateProperties());

        return sessionFactory;
    }

    /**
     * Propiedades de la base de datos (Según environment)
     *
     * @return Nuevo DataSource (Configuración de la base de datos)
     */
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
        dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
        return dataSource;
    }

    /**
     * Hibernate properties
     *
     * @return Properties set with the configuration
     */
    private Properties hibernateProperties() {
        Properties properties = new Properties();
        // Dialect (Mysql, postgresql, ...)
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        // Show SQL query
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        return properties;
    }

    /**
     * Inyected by sessionFactory
     */
    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory s) {
        HibernateTransactionManager txManager = new HibernateTransactionManager();
        txManager.setSessionFactory(s);
        return txManager;
    }
}

答案 5 :(得分:0)

这是一个完整的示例。首先-不要使用任何web.xml。仅使用下面的代码。

其他资源:

@Path("hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response hello() {
        String output = "Hello World!";
        return Response.status(200).entity(output).build();
    }
}

其他应用程序(在程序包名称中注明“核心”)

@ApplicationPath("rest")
public class RestApplication extends javax.ws.rs.core.Application {

    public RestApplication() {
    }

    @Override public Set<Class<?>> getClasses() {
        return Set.of(
            HelloResource.class
        );
    }
}

Spring Web配置。

@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = {
    
})
public class WebConfig implements WebMvcConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(WebConfig.class);

    @Autowired
    private ApplicationContext applicationContext;

    public WebConfig() {
    }
}

Spring初始化器

//This @Order is required!!!
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyWebInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        var ctx = new AnnotationConfigWebApplicationContext();
        //spring WebMvcConfigurer
        ctx.register(WebConfig.class);
        ctx.setServletContext(servletContext);

        //Spring servlet
        var servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");

        // Register Jersey 2.0 servlet
        ServletRegistration.Dynamic jerseyServlet = servletContext.addServlet("jerseyServlet",
            "org.glassfish.jersey.servlet.ServletContainer");
        //note "javax.ws.rs.Application" doesn't have "core"
        jerseyServlet.setInitParameter("javax.ws.rs.Application", RestApplication.class.getName());
        jerseyServlet.addMapping("/rest/*");
        jerseyServlet.setLoadOnStartup(1);
    }
}

它必须可以在http://127.0.0.1:8080/rest/hello

上运行