Here's my issue--I have a service that relies on an external library. I was trying to autowire the service so I can use it but was not able to
import org.keycloak.admin.client.token.TokenService;
public class SimpleService {
#Autowired
private TokenService keycloakTokenSvc; // Could not autowire, no beans of type 'TokenService' found
public void execute() {
keyCloakTokenSvc.doSomething();
}
}
I then added this to my SpringBootApplication and got it working:
#SpringBootApplication
#ComponentScan({"org.keycloak.admin.client.token"})
public MyApp {}
Sweet -- all good now, right? Nope. It seems like this overrides some of my auto configuraitons like my security config, so I was no longer to make RESTful requests to my application while it was running. I then did this next:
#SpringBootApplication
#ComponentScan({"org.keycloak.admin.client.token", "com.project.pkg"})
public MyApp {}
Still nothing. I get the same error as before:
Field keycloakTokenSvc in com.mark43.jms.services.TokenRefreshService required a bean of type 'org.keycloak.admin.client.token.TokenService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.keycloak.admin.client.token.TokenService' in your configuration.
I'm new to Spring Boot so not sure what to do here. Is there a way to use the TokenService without Autowiring? Is there a way to scan both packages?
It seems to me that you need to create a TokenService bean as follows:
#Configuration
public class TokenConfig {
#Bean
public TokenService tokenService() {
return new TokenService(); // Or whatever you need to instantiate it
}
}
This will register a TokenService object as a Spring-managed bean so that it can be autowired into SimpleService.
Related
I'm trying to inject an optional bean into a service. The bean is successfully created but consistently after the dependent service. Any thoughts why this goes wrong?
Spring Boot 2.5.0 is used. Without using the Optional, everything works fine.
Application.java
#SpringBootApplication
#Configuration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public Optional<SomeClass> someClass() {
System.out.println("creating bean");
return Optional.of(new SomeClass("someName"));
}
}
SomeClass.java
#Value
public class SomeClass {
String name;
}
SomeService.java
#Service
public class SomeService {
public SomeService(Optional<SomeClass> some) {
System.out.println(
some.map(SomeClass::getName).orElse("empty")
);
}
}
Output:
empty
creating bean
When autowiring dependencies the Optional should be used at the injection point, to indicate that a bean might or might not be available. The bean cannot be available for different reasons (conditional configuration, or just a setup error leading to null for a bean).
Creating an Optional bean is something different then using Optional for the injection point.
Ideally, you would just create the bean, which would be created eagerly, if processing can fail, handle it and return null. Now when using Optional at the injection point it will see null and Optional.empty() will automatically be injected.
I've switched a portion of a Spring app to use functional bean registrations. The motivation for the switch is due to requiring multiple instances of some beans under certain conditions. It also turns out to be much more concise (which won't be at all apparent with the simplistic examples below).
The code used to look like this (simple example):
#Configuration
public class ConfigA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
#Service
public class Service1 {
#Autowired BeanA beanA;
...
}
#Service
public class Service2 {
#Autowired BeanA beanA;
...
}
I've switched the configuration class to look like this:
#Configuration
public class ConfigA implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
context.registerBean("beanA", BeanA.class, () -> new BeanA());
}
}
The issue I'm now encountering is that Spring is complaining about autowired beans not being found. With the original code, Spring could determine that a BeanA bean was declared via ConfigA and would create that bean before initializing the services.
With the new code, I guess there is no way for Spring to determine where the BeanA bean(s) are being declared, and so it tries to init the services before the BeanA is initialized (which causes the app to not start).
I was hoping that Spring would prioritize #Configuration classes over #Service or #Controller classes, but that doesn't seem to be the case.
I could annotate all the services with #DependsOn("configA"), but there are many services that autowire BeanA (some in other code bases), so the #DependsOn option isn't really realistic.
Question: When using functional bean registration, is there a way to inform Spring that ConfigA is responsible for creating an instance of BeanA?
In order to use the functional style of bean registration and enable autowiring mechanism in other beans you can do the following:
Remove #Configuration annotation from your ConfigA class;
Create directory named META-INF under main/java/resources and create a file named spring.factories under the newly created directory;
Fill the newly created file with the line org.springframework.context.ApplicationContextInitializer=(package-name-to-configA-class).ConfigA
Now Spring should be able to successfully autowire bean named BeanA where requested.
I have the following code:
This is my first class that will to call the
#Component
class DefaultBridge #Autowired constructor(
private val clientConfig: Config,
private val client: world.example.Client
) : Bridge {
override fun doEpicStuff(): String {
val request = createRequest(...)
val response = client.makeCall(request)
return response.responseBody
}
}
The Client being used (world.example.Client) then has the following piece of code:
public class Client {
#Autowired
private RestTemplate restTemplate;
public Response makeCall(Request request) {
restTemplate.exchange(....)
}
}
When running the code. I get the following Error:
NoSuchBeanDefinitionException: No qualifying bean of type
'org.springframework.web.client.RestTemplate' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Where should I declare the bean when the constructor is autowired?
why won't spring automatically create the bean?
I need help understanding the problem so please don't just post the solution.
With Spring dependency injection you can inject a instance of a bean.
In Spring it's called Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI).
You can define depencenies for your bean with member variable or constuctor variables. There are differnt possibilities, for example #Autowire as annotation for member variables like your usage.
The container then injects those dependencies when it creates your bean.
Attention, however, it is necessary that the spring container knows how the dependency to be injected. There are many ways to teach the container this. The simplest variant is to provide a #Bean producer method in a #Configuration class. Spring container "scans" all #Configurations at the start of the container and "registers" the #Bean producer.
Nobody has done a producer for RestTemplate. Not Spring self and also no other lib. With many other beans you use, this has already been done, not with RestTemplate. You have to provide a #Bean producer for RestTemplate.
Do this in a new #Configuration or in one of your existing #Configuration.
A #Configuration indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
// New instance or more complex config
return new RestTemplate();
}
}
Define RestTemplate as a bean in your configuration class.
Do we have a spring annotation that provides an option to initialize a bean (not a component) if not available through default constructor while autowiring?
If yes, that will be awesome. I am tired of initializing beans in some configuration class using default constructor and it occupies space.
I am doing this currently
#Bean
public Test test() {
return new Test();
}
Expecting:
Sometime like:
#Autowire(initMethodType=constructor)
private Test test:
If no, was there no real need of such annotation or any technical limitation?
You have to use #Bean annotation inside an #Configuration class.
Check the following link
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You could annotate your class with #Component, like this:
#Component
public class Test {
...
}
Whereever you need that class, you could then simply autowire it:
#Autowired
private Test test;
You would just have to make sure, that you use #ComponentScan to pick up that bean. You can find more information on that here.
I have some java objects coming from external library which I need to inject in my spring project. Problem is the classes from library is not aware of any spring api's
If I inject the beans from library to Service using #Autowired I am getting org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
Following is my service class
#Path("/test")
public class TestService {
#Autowired
SomeOtherClass service;
#GET
public Response get(){
return Response.ok(service.someMethod()).build();
}
}
and following is my class from library which is not aware of spring
public class SomeOtherClass {
public String someMethod(){
return "Data from library";
}
}
When I invoke my service I get exception as
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.SomeOtherClass' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is there are way in spring to inject a plain Java Object similar to that of injection in **CDI**?
There is one option to define applicationcontext.xml and define SomeOtherClass in xml and use getBean, but I don't want to do that. Is there any other option?
Note:
Following options cannot be considered because I have100's of classes coming from library
Cannot use applicationcontext.xml
Cannot #Configuration #Bean to produce beans.
You could use the #Configuration and #Bean annotations as follows -
Create a new class:
#Configuration
public class AppConfig {
#Bean
SomeOtherClass someOtherClassBean(){ return new SomeOtherClass();}
}
Now the auto wiring shall work.
What it does, is actually creating a bean and letting Spring know about it.
Maybe try adding the beans programatically to the IoC container:
Add Bean Programmatically to Spring Web App Context
You need to find all the classes you want to instantiate and use one of the methods in the linked question.
You can use reflection to add Bean definitions programatically.
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Reflections ref = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forPackage(PACKAGE_NAME))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(PACKAGE_NAME))));
ref.getSubTypesOf(Object.class).stream()
.forEach(clazz -> {
logger.info("Defining pojo bean: {} -> {}", Introspector.decapitalize(clazz.getSimpleName()), clazz.getCanonicalName());
registry.registerBeanDefinition(Introspector.decapitalize(clazz.getSimpleName()),
BeanDefinitionBuilder.genericBeanDefinition(clazz).getBeanDefinition());
});
}
Subsequently, these beans can be #Autowired elsewhere. See Gist: https://gist.github.com/ftahmed/a7dcdbadb8bb7dba31ade463746afd04