Spring+Jersey transactional annotation

拈花ヽ惹草 提交于 2019-12-18 04:47:11

问题


Created boilerplate project to expose RESTful API to JPA enabled database. It's using the following versions:
- Spring 3.2.6
- Hibernate 4.3.0
- Jersey 2.5.1
I finally was able to get them playing together, but still some question remains. Here's one of the most puzzling things (see excerpt from REST service class)

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

if class is annotated with @Service, @Transactional annotation is ignored and transaction for the methods is not started. However, when changed to @Component, everything works fine. Couldn't figure out, why.

The entire project can be seen here


回答1:


I got puzzled by this as well, but finally figured this out.

The jersey-spring module will only import @Component beans from your context. There literally is a beanClass.isAnnotationPresent(Component.class) check in SpringComponentProvider.

Otherwise it appears to only create half-baked request-scoped instances of the bean (I traced this with Thread.dumpStack in service constructor). They seem to have dependency injection, but not AOP.

There's a number of JIRA items already in Jersey's issue tracker: JERSEY-2495, JERSEY-2059, JERSEY-2301

UPDATE: My pull request for these has been merged, this should be fixed in Jersey 2.11.




回答2:


As mentioned in another answer SpringComponentProvider gets a bean created by Jersey and registers it in the Spring context, but in this case you don't get Spring AOP.

I managed to get it working with AOP the other way around: the bean is created by Spring (so in fact it is a proxy because of AOP) and then is registered in Jersey.

But I had to fix a bug in Jersey's ModelHelper class: https://github.com/jersey/jersey/pull/90

Without this fix Jersey was not able to find the @Path annotation in the Spring proxy.

This is the basic structure:

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



回答3:


The reason is your Spring has a different container for its annotations and jersey has a different container for its annotations , in order to access your beans in spring container you can refer to the below code ;

I beg to differ from the version your using, I haven't tried with the latest version of jersey:

load spring through web.xml like shown below as normal spring confifuration:

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

Now load your jersey Resources through Application as shown below:

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

Then in your resource:

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

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

You can get your ApplicationContext where all the beans have been initailized using WebApplicationContextUtils of spring pass the servlet context, and get your bean



来源:https://stackoverflow.com/questions/21104567/springjersey-transactional-annotation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!