Spring + Jersey事务注释

时间:2014-01-14 01:29:14

标签: spring rest jpa transactions jersey

创建样板项目以向启用JPA的数据库公开RESTful API。它使用以下版本:
- 春季3.2.6
- Hibernate 4.3.0
- 泽西岛2.5.1
我终于能够让他们一起玩,但仍然有一些问题。这是最令人费解的事情之一(参见REST服务类的摘录)

@Service
@Path("resources")
@Produces({ MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_JSON })
@Transactional
public class ResourceServices extends AbstractServices<Resource> {
...
}

如果使用@Service注释类,则忽略@Transactional注释并且不启动方法的事务。但是,当更改为@Component时,一切正常。想不通,为什么。

可以看到整个项目here

3 个答案:

答案 0 :(得分:9)

我也对此感到困惑,但终于弄明白了。

jersey-spring模块只会从您的上下文中导入@Component个bean。确切地说是beanClass.isAnnotationPresent(Component.class)中的SpringComponentProvider支票。

否则它似乎只创建了半生不熟的请求范围的bean实例(我在服务构造函数中用Thread.dumpStack跟踪它)。他们似乎有依赖注入,但不是AOP。

泽西岛问题跟踪器中已有许多JIRA项目:JERSEY-2495JERSEY-2059JERSEY-2301

更新:我已经合并了这些请求,这应该在Jersey 2.11中修复。

答案 1 :(得分:2)

正如在另一个答案中提到的,SpringComponentProvider获取Jersey创建的bean并在Spring上下文中注册它,但在这种情况下你不会得到Spring AOP。

我设法让它以相反的方式使用AOP:bean是由Spring创建的(因此实际上它是AOP的代理)然后在Jersey中注册。

但我必须修复Jersey的ModelHelper类中的错误:https://github.com/jersey/jersey/pull/90

如果没有这个修复,Jersey无法在Spring代理中找到@Path注释。

这是基本结构:

public class MyApplication extends ResourceConfig {
    @Inject
    public MyApplication(ServletContext servletContext) {
        super(JSONController.class, XSSSecurityFilter.class, JacksonFeature.class);
        WebApplicationContext springFactory = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        // TODO: scan entire Spring factory for beans annotated with @Path and register them, so we don't need to do this manually.
        // Letting Jersey register the beans does not work because in this case Spring will not intercept the calls.
        register(springFactory.getBean(UserServiceFacade.class));
    }
}

答案 2 :(得分:0)

原因是你的Spring有一个不同的注释容器,而且jersey有一个不同的注释容器,为了在spring容器中访问你的bean你可以参考下面的代码;

我不同意你使用的版本,我没有尝试使用最新版本的球衣:

通过web.xml加载spring,如下所示为普通弹簧配置:

<servlet>
    <servlet-name>project-spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:project-spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>project-spring</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

现在通过应用程序加载你的球衣资源,如下所示:

@ApplicationPath("/rest")
public class ResourceLoader extends Application
{
    /* (non-Javadoc)
     * @see javax.ws.rs.core.Application#getClasses()
     */
    @Override
    public Set<Class<?>> getClasses()
    {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        loadResourceClasses(classes);
        return classes;
    }

    private void loadResourceClasses(Set<Class<?>> classes)
    {
        classes.add(StudentResource.class);
    }
}

然后在你的资源中:

@Path("student")
class StudentResource
{
    private StudentService studentService;

    StudentResource(@Context ServletContext servletContext)
    {
       ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        this.studentService= applicationContext.getBean(StudentService .class);
    }
}

你可以使用Spring的WebApplicationContextUtils传递servlet上下文来获取你的ApplicationContext,其中所有bean已经被初始化,并获取你的bean