问题
I am trying to have a @Bean
implemented by a class from a 3rd party library (OWL API).
This implementation uses an @Inject
annotation. Spring tries to interpret it, interfering with the injection mechanism of the 3rd party library and avoiding it to work as intended.
Is there a way to instruct Spring to ignore the @Inject
annotations of the bean implementation, when instantiating the bean?
I found few questions about this subject but none of them provided a solution usable in my context.
I actually managed to resolve the issue myself, by wrapping the 3rd party object in an anonymous class, apparently creating a barrier for Spring and preventing it to look into this object (see the point 3. below), but I consider it to be an ugly workaround.
Details:
According to the OWL API documentation, the OWLOntologyManager
is to be created like this:
OWLOntologyManagerFactory ontologyManagerFactory = new OWLManager();
OWLOntologyManager owlOntologyManager = ontologyManagerFactory.get();
//... use owlOntologyManager
Indeed, in my Spring application that was working. However, I need to have the OWLOntologyManagerFactory
with an application
scope and OWLOntologyManager
with a Session
scope.
So I declared each of these two objects as a Spring @Bean
, with an appropriate scope and started to receive an error:
Error creating bean with name 'scopedTarget.sessionOWLOntologyManager': Unsatisfied dependency expressed through method 'setIRIMappers' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.Set' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
See below samples of the code.
- Functional first test of the code, not meeting the application needs:
@RestController
public class OntologiesController {
@RequestMapping("ontologies")
public String manager_loadOntology(
@RequestParam(value="ontologyIriString") String ontologyIriString
) throws OWLOntologyCreationException
{
OWLOntologyManagerFactory ontologyManagerFactory = new OWLManager();
OWLOntologyManager owlOntologyManager = ontologyManagerFactory.get();
OWLOntology ontology = owlOntologyManager.loadOntology(IRI.create(ontologyIriString));
return ontology.toString();
}
}
- Not functional code failing to create OWLOntologyManager with the error quoted above.
@Configuration
public class ApplicationScopeConfig {
@Bean
@ApplicationScope
public OWLOntologyManagerFactory applicationOWLOntologyManagerFactory() {
return new OWLManager();
}
}
@Configuration
public class SessionScopeConfig {
@Autowired
OWLOntologyManagerFactory applicationOWLOntologyManagerFactory;
@Bean
@SessionScope
public OWLOntologyManager sessionOWLOntologyManager() {
return applicationOWLOntologyManagerFactory.get();
}
}
@RestController
public class OntologiesController {
@Autowired
private OWLOntologyManager sessionOWLOntologyManager;
@RequestMapping("ontologies")
public String manager_loadOntology(
@RequestParam(value="ontologyIriString") String ontologyIriString
) throws OWLOntologyCreationException
{
OWLOntology ontology = sessionOWLOntologyManager.loadOntology(IRI.create(ontologyIriString));
return ontology.toString();
}
}
- Functional code, working as needed, but ugly, is there a way to improve it?
In the code from the point 2 I modified the sessionOWLOntologyManager() as follows, wrapping it to an anonymous class that prevents Spring to look into the real owlOntologyManager.
@Bean
@SessionScope
public OWLOntologyManager sessionOWLOntologyManager() {
final OWLOntologyManager owlOntologyManager = applicationOWLOntologyManagerFactory.get();
return new OWLOntologyManager() {
public void clearOntologies() {
owlOntologyManager.clearOntologies();
}
//additional 400 lines implementing all methods by delegating to owlOntologyManager
//Apparently that creates a barrier for Spring so it does not conflict with the
//@Inject annotation in the implementation of the original owlOntologyManager,
//but in spite of having IDE support to generate this delegation, I consider it
//as an workaround.
}
}
回答1:
As it eventually turns out that this is related to Configuring Spring to ignore dependencies annotated with @Inject, so I assume that a chance to get better answers is low and I post my additional findings in a reply to myself.
The class responsible for recognizing the @Inject is org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor. With my Spring knowledge I was however not able to intercept its behavior. The class SpringBeanAutowiringInterceptor, mentioned in the related question, does not exist in Spring 5.
For now I go with an improvement to my solution in the point 3. Instead of generating dozens of delegating methods (it can be done automatically by IDE), I go with the code below, using the reflection. The advantage is that it will be more resistant to changes if I should upgrade the OWLAPI library one day. Also it has significantly less lines. I assume that the reflection might be less performant.
@Bean
@SessionScope
public OWLOntologyManager sessionOWLOntologyManager() {
final OWLOntologyManager owlOntologyManager = applicationOWLOntologyManagerFactory.get();
//Instead of returning owlOntologyManager directly,
//the delegating proxy prevents Spring to resolve @Inject annotations
//in the implementation of owlOntologyManager.
return (OWLOntologyManager)Proxy.newProxyInstance(
owlOntologyManager.getClass().getClassLoader(),
owlOntologyManager.getClass().getInterfaces(),
(o, method, args) ->
method.invoke(owlOntologyManager, args)
);
}
来源:https://stackoverflow.com/questions/59582720/spring-bean-implemented-by-a-3rd-party-library-that-uses-inject-conflicting-wit