I'm new about spring and my purpose is crea two object and one object print a string and the second object ( that has dipendency with first object take the value e prints an output). It is an exercise:
Helloworld.java is this:
#Component
public class HelloWorld {
private String message="";
public void setMessage(String message) {
this.message = message;
}
#Bean
public String getMessage() {
return message;
}
}
Person class does this:
#Component
public class Person {
private String person="Jessy";
public void setPerson(String person) {
this.person = person;
}
// I want receive hellowordbean and I want print the first message and join the two message
#Autowired
public String printMessage(HelloWorld message) {
return message.getMessage()+" - "+this.person;
}
}
this is main class:
#ComponentScan(value={"com.example.bean"})
public class TestApplication {
public static void main(String[] args) {
//SpringApplication.run(TestApplication.class, args);
ApplicationContext context = SpringApplication.run(HelloWorld.class, args);
HelloWorld word=context.getBean(HelloWorld.class);
word.setMessage("hi world");
ApplicationContext personContext = SpringApplication.run(Person.class, args);
Person person=personContext.getBean(Person.class);
person.setPerson("Jimbey");
System.out.println(person.printMessage(word));
}
}
I play run and I obtain
Description:
Parameter 0 of method printMessage in com.example.bean.Person required
a bean of type 'com.example.bean.HelloWorld' that could not be found.
Action:
Consider defining a bean of type 'com.example.bean.HelloWorld' in your
configur
I don't know how resolve this problem, anyone can help me?
There are many things wrong :( I recommend you read a tutorial about how to build a hello world app with Spring.
Remove #Autowired annotation from printMessage method in Person class. Here you can see how to use #Autowired annotation https://www.baeldung.com/spring-autowire.
Use #SpringBootApplication annotation on TestApplication class. It creates all beans annotated with #Component annotation.
Related
I run into a code that has some strange #Qualifier behavior. It is not what I would expect.
Let's consider the following code snippet:
#SpringBootApplication
class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
Foo foo1() {
return new Foo("foo1");
}
#Bean
Foo foo2() {
return new Foo("foo2");
}
static class Foo {
private final String name;
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
#Component
static class Bar {
private Foo fooOne;
private Foo fooTwo;
#Autowired
#Qualifier("foo3")
void setFoo(Foo foo1, Foo foo2) {
this.fooOne = foo1;
this.fooTwo = foo2;
}
#PostConstruct
void printFoo() {
System.out.println(fooOne.getName() + fooTwo.getName());
}
}
}
The above code doesn't work because there is no Foo bean named foo3. This is exactly what I expect. Now let's change Foo class definition to include a #Qualifier:
#Qualifier("foo3")
static class Foo {
// remaining code not changed..
With that change, the #Qualifier on Bar::setFoo method seems to be ignored and instances foo1 and foo2 are injected. No bean with id foo3 is present in the context. I have hard time understanding the mechanism applied here. Can anyone explain it?
By putting the qualifier on class Foo:
#Qualifier("foo3")
static class Foo {
...
}
Every bean instantiated as a class Foo will have the given qualifier. You are correct that are is not a single bean with id foo3, however there are two beans present with a qualifier foo3.
More information on the topic can be found here.
To inject both beans, remove the qualifier on the static class and add a qualifier on both parameters:
#Autowired
void setFoo(#Qualifier("foo1") Foo foo1, #Qualifier("foo2") Foo foo2) {
To play around with beans in an application context, I'd suggest injecting the application context and retrieving the beans to inspect them. For instance:
#SpringBootApplication
class DemoApplication {
#Autowired
ApplicationContext context
#PostConstruct
public void init() {
// Beans by qualifier
BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), YourClass.class, "foo3");
}
...
}
I have an interface like so:
public interface Animal {
void setName(String animal);
String getName();
}
and I have a Class that implements the interface:
#Component
public class Dog implements Animal {
private String name;
public void setName(String name) {
this.name= name;
}
public String getName() {
return this.name;
}
}
In another class (ProcessAnimal), I AutoWire the interface:
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
I only have one class that implements Animal so this should work, however, I get a NullPointerException when it hits the animal.setName("Fido"); line. IntelliJ is complaining that Autowired members must be defined in valid Spring bean (#Component|#Service...) which I have... I don't understand what I'm doing wrong. I've tried to add a #Qualifier, but still it didn't work and it shouldn't be necessary since I only have one implementation.
-java
-com.example.com.AnimalProcessing
-Animal
-Animal.java
-Dog.java
-ProcessAnimal.java
-AnimalProcessingApplication.java
AnimalProcessingApplication.java
#SpringBootApplication
public class AnimalProcessingApplication {
public static void main(String[] args) {
SpringApplication.run(AnimalProcessingApplication.class, args);
run();
}
public static void run() {
ProcessAnimal processAnimal = new ProcessAnimal();
processAnimal.processAnimals();
}
}
AnimalProcessingApplication class should be one level above all other classes.
Also you are using new for creation of object instead of using Dependency Injection (autowiring).
Replace below -
ProcessAnimal processAnimal = new ProcessAnimal();
with
#Autowired
ProcessAnimal processAnimal;
Also make sure that ProcessAnimal is a bean and Animal is injected in this class using autowiring.
Animal Processing Application.java must be on root folder of all classes.
Then all components in child folders are recognized automatically.
Update:
Create a config class with #Bean method to create an instance with a Dog. Also then you can get rid of the #Component annotation of the class.
The problem here is the constructor String name which cannot be injected.
Update 2:
Don't create the instances by yourself. Let spring container create them. Remove the run method.
Following are to be done to make this program work.
1.ProcessAnimal should be made a component . Annotating the class with #Component will mark the class to be autodetected during component scan.
#Component
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
Obtain the ProcessAnimal class from the application context. The spring will prepare the ProcessAnimal bean with all its dependencies set.
You may do this in multiple ways and following is one of those
#Component
public class CheckRedRunner implements ApplicationRunner {
#Autowired
ProcessAnimal process;
#Override
public void run(ApplicationArguments args) throws Exception {
process.processAnimals();
}
}
A bean implementing ApplicationRunner will be run when the application starts.
or else
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(AnimalProcessingApplication.class, args);
ProcessAnimal process = ctx.getBean(ProcessAnimal.class);
process.processAnimals();
}
Couple of observations
the package names by convention uses lower case letters
example : com.example.process.entity
Please go through the official documentation to learn the expected way of writing Spring boot application.
Could you please help me and clarify how should I deal with generics in Spring?
In the following example an Exception NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available is thrown?
public class Launcher {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
FooService<String> fooService = context.getBean(FooService.class);
fooService.printVal();
}
}
I would like to notice that if I change generic to String type it works correctly.
#Service
public final class FooService<V> {
private final V v;
#Autowired
public FooService(V v) {
this.v = v;
}
public void printVal() {
System.out.println(v);
}
}
And simple configuration class.
#Configuration
#ComponentScan("service")
public class AppConfig {
#Bean
public String string() {
return "simple string";
}
}
Could you please advice me what is the best way to force the snippet to work properly?
Since Spring 4.0 it will automatically consider generics as a form of #Qualifier, as below:
#Autowired
private Item<String> strItem; // Injects the stringItem bean
#Autowired
private Item<Integer> intItem; // Injects the integerItem bean
So you should indicate the name ( in your case "string") in your constructor method.
I do have ServiceImpl which looks like this:
#Service
#RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
#EnableSwagger2
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
But I receive the following error when running SpringBoot app:
Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Do you have any solution for that?
You annotated your class with #Service and defined it manually as a bean with the #Bean annotation. I do think the second is the way you planned to use it.
The #Service annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the #Service annotation and everything should work as expected.
Try this
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowire
public ServiceAImpl(#Value("${fieldA}") String fieldA){
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
and this
#EnableSwagger2
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use #Service and #Bean for the same class!
Spring is not so smart :)
You should annotate your bean like:
#RequiredArgsConstructor
public class ServiceAImpl {
#Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the #RequiredFieldsConstructor, it would be simpler for you write down the constructor annotated with #Autowired and using the #Value annotation for the String parameter:
#Autowired
public ServiceAImpl(#Value("${aProp}") String string) {
You're using two bean declaration mechanisms:
You're registering your bean using #Service
You're registering a bean using #Bean
This means that your service will be created twice. The one defined using #Bean works properly, since it uses the #Value annotation to inject the proper value in your service.
However, the service created due to #Service doesn't know about the #Value annotation and will try to find any bean of type String, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the #Bean configuration, you should remove the #Service annotation from ServiceAImpl and that will do the trick.
Alternatively, if you want to keep the #Service annotation, you should remove the #Bean declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the #Value annotation within the constructor:
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
/**
* This constructor works as well
*/
public ServiceAImpl(#Value("${fieldA}") String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
If you want to declare ServiceAImpl as a Spring bean in your Java Configuration file, you should remove the #Service annotation from the class declaration. These annotations doesn't work well together.
ServiceAImpl.java
import org.springframework.beans.factory.annotation.Autowired;
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowired
public ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
Application.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
}
Your application.properties
fieldA=value
The below implementation works well for me. You have two issues, first you have to choose between #Service and #Bean and the other issue I've seen in your code was the #Value annotation, you have to use only to inject a value from the properties.
#SpringBootApplication
public class TestedValueApplication {
#Autowired
void printServiceInstance(ServiceA service) {
System.out.println("Service instance: " + service);
System.out.println("value==value? " + service.isFieldA("value"));
}
public static void main(String[] args) {
SpringApplication.run(TestedValueApplication.class, args);
}
#Bean
public ServiceA serviceA(#Value("${fieldA}") String fieldA) {
return new ServiceAImpl(fieldA);
}
}
Service:
public class ServiceAImpl implements ServiceA {
private String fieldA;
ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
application.properties:
fieldA=value
When using a no args-constructor in Spring is it possible to make sure that some properties have been set before the bean is initialized? I want to use InitializingBean to validate settings after the bean has been created. E.g., what I want to do:
public class HelloWorld implements InitializingBean{
private String message;
public HelloWorld()
{
//Only no-args constructor must be used
//How do we make sure 'message' was ever set before the Bean is used?
}
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
public void afterPropertiesSet(){
//Validate object, requires message to be set!
}
}
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
//Bean is instantiated
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
//Bean is initialized and thus afterPropertiesSet() is called here. It will fail because it requires 'message' to be set.
//Right after the bean is instantiated we set the 'message', but it's already to late. afterPropertiesSet() was already called.
obj.setMessage("Hello World!");
}
}
To inject things determined at runtime, please consider using FactoryBean. It's basically the Factory pattern, but supported by Spring natively:
public class SomeWeirdMessageFactoryBean implements AbstractFactoryBean {
public Class<?> getObjectType() {
return String.class;
}
protected Object createInstance() throws Exception {
String message;
// magic:
[...]
return message;
}
}
The magic part is obviously what has to figure out what to return. You then have to inject it as property of your class, and Spring is going to figure out that it's a factory and do the dirty work for you.