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