I have the following classes:
com.foo.pkgx:
#Component public class A {}
#Component public class B {
#Autowired A a;
}
com.foo.pkgy:
#Component public class C {
#Autowired B b;
}
I.E. dependency-wise: C -> B -> A
When executing the following spock Specification:
#Configuration
#ComponentScan(basePackages = ["com.foo.pkgy"])
class Config {
def mockFactory = new DetachedMockFactory()
#Bean
B b() {mockFactory.Mock(B)};
}
#ContextConfiguration(classes = Config)
class CSpec extends Specification {
#Autowired
C c;
def "sanity"() {
expect: c
}
}
The test fails initialization:
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'c':
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: com.foo.pkgx.B com.foo.pkgy.C.b;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'b':
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: com.foo.pkgx.A com.foo.pkgx.B.a;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.foo.pkgx.A] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I read this as "tried to wire A into B, but failed". I wasn't expecting B to undergo autowiring, as it is mocked.
Could someone shed some light?
Thanks!
Mock of a class is a subclass generated by code-generation library, byte-buddy or cglib-nodep to name a few. It's a regular bean from Spring perspective. And once any bean is instantiated then at some point, Spring starts "introspection" of the bean by means of various ...BeanPostProcessors. E.g. Autowiring is handled by AutowiredAnnotationBeanPostProcessor. This postprocessor works in such a way that each bean is inspected through the entire class hierarchy. So your class B is there with #Autowired field A and Spring tries to resolve this dependency with no luck.
The good news, however, is that there is a demand for such a feature of disabling autowiring on a per-bean basis. And looks like eventually, Spring team will introduce it soon.
Meanwhile you can just mock A so that B's dependency is resolved. Or you can just use interfaces for your components, instead of using just classes. I believe the latter is the best practice
Related
Using Spring 2.7.0 to create an API gateway, that was working fine until I tried to replace the RestTemplate with OpenFeign.
Here is the relevant build.gradle contents:
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'org.liquibase.gradle' version '2.1.0'
id 'groovy'
}
ext {
set('springCloudVersion', "2021.0.3")
}
// implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
My application class:
#SpringBootApplication
#EnableDiscoveryClient
#EnableFeignClients
public class ApiGatewayApplication {
...
}
My FeignClient:
#FeignClient(name="identity-service")
public interface IdentityServiceClient {
#GetMapping("/api/all")
public List<ApiKey> getAllApiKeys();
}
when I try to start the application now I get:
o.s.c.openfeign.FeignClientFactoryBean : For 'identity-service' URL not provided. Will try picking an instance via load-balancing.
DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate
....
onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'apiGatewayApplication': Invocation of init method failed; nested exception is feign.codec.DecodeException: No qualifying bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
com.netflix.discovery.DiscoveryClient : Shutting down DiscoveryClient ...
com.netflix.discovery.DiscoveryClient : Unregistering ...
com.netflix.discovery.DiscoveryClient : DiscoveryClient_API-GATEWAY/192.168.1.72:api-gateway:8081 - deregister status: 404
com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' in your configuration.
But none of the online examples seem to show the need for adding custom HttpMessageConverters since they are included in spring-web and spring-boot-starter-webflux
UPDATE -
this appears to be related to #Autowired. I put both my service and feign service in a regular class and it starts up fine. But when I autowire those two services, I get this error again.
I am having issues with dependency injection in a batchlet.
#Named
public class SimpleBatchlet extends AbstractBatchlet {
#Inject
protected StorageService storageService;
...
public String process() throws Exception {
storageService.doSomething(); // this throws a null pointer exception
}
}
#Named
public class LocalFileStorageService implements StorageService {
public void doSomething() {
}
}
I have tried putting beans.xml in both META-INF and WEB-INF and removing it, all to no avail. I also tried changing the scopes of the beans to singletons, etc. I am invoking / starting the batch job through the use of an #Schedule annotation on a method that uses BatchRuntime to start the job.
I must be missing something simple as I know this should work. The actual scope of the beans I will use may need to vary, but the point I am trying to make is that I don't believe bean scope is a problem, but some other configuration issue.
I should also note that I only have 1 implementation of StorageService.
Not clear what really is your problem (NPE on injected CDI bean?), but annotating your Batchlet #Dependent should solve the problem :
#Named
#Dependent
public class SimpleBatchlet extends AbstractBatchlet {
#Inject
protected StorageService storageService;
}
Batchlet need to be #Named and #Dependent for integration with CDI.
I'm using OpenEJB as application server and I want to deploy a Jax-RS resource that requires some named service to be injected in its constructor.
My resource looks like :
#Singleton
#Path("/")
public class Resource {
private Service service;
#Inject
public Resource(#Named("service") Service service) {
this.service = service;
}
}
Unfortunately, OpenEJB complains because it doesn't consider my constructor to be valid because of the #Named annotation decorating the Service argument.
java.lang.RuntimeException: Resource class class test.Resource has no valid constructor
at org.apache.openejb.server.cxf.rs.CdiResourceProvider.validateConstructorExists(CdiResourceProvider.java:138)
at org.apache.openejb.server.cxf.rs.CdiResourceProvider.<init>(CdiResourceProvider.java:100)
at org.apache.openejb.server.cxf.rs.OpenEJBPerRequestPojoResourceProvider.<init>(OpenEJBPerRequestPojoResourceProvider.java:28)
at org.apache.openejb.server.cxf.rs.CxfRsHttpListener.deployApplication(CxfRsHttpListener.java:522)
The implementation of the OpenEJB CdiResourceProvider clearly doesn't want something else than the Jax-RS #Context annotation for constructor arguments...
Is it supported by OpenEJB?
JAX-RS has a specific requirement for a no-args constructor. This will be true of any JAX-RS impl, its not specific to the OpenEJB integration.
I have problem with Spring Data Elasticsearch. I configure elasticsearch like this:
#Configuration
#EnableJpaRepositories(basePackages = {"org.project.repositories"})
#EnableElasticsearchRepositories(basePackages = "org.project.repositorieselastic")
#EnableTransactionManagement
public class PersistenceContext {
#Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchTemplate(client());
}
#Bean
public Client client(){
Settings settings = ImmutableSettings.settingsBuilder()
// Setting "transport.type" enables this module:
.put("cluster.name", "elasticsearch")
.put("client.transport.ignore_cluster_name", false)
.build();
TransportClient client= new TransportClient(settings);
TransportAddress address = new InetSocketTransportAddress("127.0.0.1", 9300);
client.addTransportAddress(address);
return client;
}
}
My repo looks like.
#Repository()
public interface UserFavoriteElasticRepo extends ElasticsearchRepository<UserFavorite, Long> {
}
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.project.repositorieselastic.UserFavoriteElasticRepo org.project.services.elastic.FavoriteIndexerService.elasticRepo; nested exception
is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userFavoriteElasticRepo': Invocation of init method failed; nested exception is java.lang.AbstractMethodError
It's look like the implementation is not generated. But I don't know where to investigate. I tried to use one package and use this - https://github.com/izeye/spring-boot-throwaway-branches/commit/874ccba09189d6ef897bc430c43b6e3705404399 but with no success.
I solved the problem adding this in pom file
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.12.2.RELEASE</version>
</dependency>
I've got same exception using spring-data-elasticsearch.
But exception was thrown when I declared new methods in repository:
ex.:
public interface UserFavoriteElasticRepo extends ElasticsearchRepository<UserFavorite, Long> {
Page<UserFavorite> findBySomeProperty(String propertyValue, Pageable pageable);
}
It was casued due to spring-data-elasticsearch, spring-data-commons versions. Function declarations have changed: org.springframework.data.repository.query.QueryLookupStrategy.resolveQuery - it caused the exception.
For spring-data-elasticsearch version 2.0.0.RELEASE you have to use spring-data-commons with version 1.12.0.
If you have spring-data-jpa in your project it also uses spring-data-commons. For spring-data-jpa v1.9.0.RELEASE, spring-data-commons is v1.11.0.RELEASE
Could you provide what frameworks and versions of spring you are using? Also If you could put whole stacktrace, it will be helpfull?
I have deployed a war-file, with actionclasses and a facade, and a jar-file with ejb-components (a stateless bean, a couple of entities and a persistence.xml) on glassfish3. My problem is that i cant find my remote interface to the stateless bean from my facade.
My bean and interface looks like:
#Remote
public interface RecordService {...
#Stateless(name="RecordServiceBean", mappedName="ejb/RecordServiceJNDI")
public class RecordServiceImpl implements RecordService {
#PersistenceContext(unitName="record_persistence_ctx")
private EntityManager em;...
and if i look in the server.log the portable jndi looks like:
Portable JNDI names for EJB RecordServiceBean : [java:global/recordEjb/RecordServiceBean, java:global/recordEjb/RecordServiceBean!domain.service.RecordService]|#]
and my facade:
...InitialContext ctx= new InitialContext();
try{
recordService = (RecordService) ctx.lookup("java:global/recordEjb/RecordServiceBean!domain.service.RecordService");
}
catch(Throwable t){
System.out.println("ooops");
try{
recordService = (RecordService)ctx.lookup("java:global/recordEjb/RecordServiceImpl");
}
catch(Throwable t2){
System.out.println("noooo!");
}...
}
and when the facade makes the first call this exception occur:
javax.naming.NamingException: Lookup failed for 'java:global/recordEjb/RecordServiceBean!domain.service.RecordService' in SerialContext [Root exception is javax.naming.NamingException: ejb ref resolution error for remote business interfacedomain.service.RecordService [Root exception is java.lang.ClassNotFoundException: domain.service.RecordService]]
and the second call:
javax.naming.NamingException: Lookup failed for 'java:global/recordEjb/RecordServiceBean' in SerialContext [Root exception is javax.naming.NamingException: ejb ref resolution error for remote business interfacedomain.service.RecordService [Root exception is java.lang.ClassNotFoundException: domain.service.RecordService]]
I have also tested to inject the bean with the #EJB-annotation:
#EJB(name="RecordServiceBean")
private RecordService recordService;
But that doesnt work either. What have i missed? I tried with an ejb-jar.xml but that shouldnt be nessesary. Is there anyone who can tell me how to fix this problem?
I found a sollution to this problem. If i pack the interfacefile in a jarfile and drop that jar in to WEB-INF/lib in the warfile the problem is solved.
When server gets started, the global jndi names of the interfaces are displayed in console.
Use those to lookup interfaces instead of trying to call them by giving names explicitly.
Have you tried commenting out all the ejb related sections of your xml files and then using just
#Stateless
public class RecordServiceImpl implements RecordService {
and then
#EJB
private RecordService recordService;
They've tried to make the process fairly brain dead in EJB 3.1
Also, while JNDI browsing didn't make it into gf 3.0 you can dig up some info with
asadmin list-jndi-entries --context java:global/yourAppName