8

我有一个同时使用 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 ......这是有道理的,因为我猜测资源现在正在被加载Jersey 并且不是 Spring 管理的 bean,因此没有自动装配。

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

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

显然,如果有人对我有明确的答案,那就太好了,但我也很感激任何关于我可以在哪里寻找或尝试的东西的指针或提示。

谢谢,马特

4

6 回答 6

3

下面是我的应用程序的一部分,它使用 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");
    }
}

}

于 2014-03-06T12:57:34.427 回答
1

我想到了两个选项(没有双关语)。

  1. 也许您可以SpringServlet使用自己的类进行扩展并为其添加适当的 servlet 3.0 注释。
  2. 随着您从 类切换SpringServletApplication类的方法,您可以通过启用 Spring 构建时或加载时字节码编织来解决无自动装配问题。这使 Spring 能够注入由任何地方实例化的对象,而不仅仅是由 Spring 创建的对象。请参阅“使用 AspectJ 通过 Spring 依赖注入域对象”
于 2013-02-19T02:05:32.927 回答
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>
于 2013-10-16T07:33:57.563 回答
1

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

所需的 web.xml 更改在 init 参数中(过滤器映射未显示,但仍是必需的):

<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 中引入更多配置,这对我来说很重要,因为我试图隐藏这个细节。

于 2013-02-19T01:04:47.357 回答
0

我将 Jersey 与我之前使用 SpringMVC 制作的项目一起使用。我的代码基于Spring 的官方文档

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;
    }

}

休眠配置

// 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;
    }
}
于 2016-01-25T23:08:48.477 回答
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
        );
    }
}

春季网络配置。

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

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

    @Autowired
    private ApplicationContext applicationContext;

    public WebConfig() {
    }
}

弹簧初始化器

//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

于 2020-11-17T13:00:01.597 回答