May I annotate a simple pojo by #Component ?
It is because IntelliJ says :
could not autowire, no beans found
Thanks
Of course you can annotate pojo with #Component, and then you can autowire it within another pojo also annotated with #Component e.g:
#Component
public class Computer {
#Autowired
Procesor procesor;
public void printName() {
System.out.println("486DX");
}
}
#Component
public class Procesor {
}
#SpringBootApplication
public class SpringOneApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringOneApplication.class, args);
Computer computer = (Computer) context.getBean("computer");
computer.printName();
}
}
Related
I have 2 spring boot microservice let's say core and persistence. where persistence has a dependency on core.
I have defined an interface in core whose implementation is inside persistence as below:
core
package com.mine.service;
public interface MyDaoService {
}
Persistence
package com.mine.service.impl;
#Service
public class MyDaoServiceImpl implements MyDaoService {
}
I am trying to inject MyDaoService in another service which is in core only:
core
package com.mine.service;
#Service
public class MyService {
private final MyDaoService myDaoService;
public MyService(MyDaoService myDaoService) {
this.myDaoService = myDaoService;
}
}
while doing this i am getting this weird error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.mine.service.MyService required a bean of type 'com.mine.service.MyDaoService' that could not be found.
Action:
Consider defining a bean of type 'com.mine.service.MyDaoService' in your configuration.
can anyone explain me why ?
NOTE: i have already included com.mine.service in componentscan of springbootapplication as below
package com.mine.restpi;
#SpringBootApplication
#EnableScheduling
#ComponentScan(basePackages = "com.mine")
public class MyRestApiApplication {
public static void main(String[] args) {
SpringApplication.run(MyRestApiApplication.class, args);
}
}
Try adding the #Service annotation to your impl classes and the #Autowired annotation to the constructor.
// Include the #Service annotation
#Service
public class MyServiceImpl implements MyService {
}
// Include the #Service annotation and #Autowired annotation on the constructor
#Service
public class MyDaoServiceImpl implements MyDaoService {
private final MyService myService ;
#Autowired
public MyDaoServiceImpl(MyService myService){
this.myService = myService;
}
}
As stated in the error message hint: Consider defining a bean of type 'com.mine.service.MyDaoService' in your configuration to solve this problem you can define in your package com.mine a configuration class named MyConfiguration annotated with #Configuration including a bean named myDaoService like below:
#Configuration
public class MyConfiguration {
#Bean
public MyDaoService myDaoService() {
return new MyDaoServiceImpl();
}
}
Try the following, move MyRestApiApplication class to com.mine package and remove #ComponentScan annotation.
package com.mine;
#SpringBootApplication
#EnableScheduling
public class MyRestApiApplication {
public static void main(String[] args) {
SpringApplication.run(MyRestApiApplication.class, args);
}
}
I have this scenario
team A is implementing an interface Vehicle as ClassAVehicle
team B is implementing a dashboard service in which it uses vehicle implementation
Now team A have new implementation of Vehicle as ClassBVehicle. And team B wants to use it. One way I know is that use of #Qualifier annotation. But for this I require to change team B's code.
So do I have tight coupling here? Can I have some XML based configuration so that team B's code resolves new ClassBVehicle instance automatically?
interface Vehicle{
int getNoTyre();
}
class ClassAVehicle{
int getNoTyre(){
return 1;
}
}
class ClassBVehicle{
int getNoTyre(){
return 2;
}
}
class Dashboard{
// Here everything is fine until classBVehicle is not there
// Now I want to use new classBVehicle.
// One way I see is that using #Qualifier but will it not be tight coupling?
#Autowired
Vehicle oldAInstance;
}
If you use xml to define bean, your way is good to decouple. Another way is that you can use ApplicationContext to get bean dynamically in annotation program. There are two way to getBean with beanName or beanClass. The below is sample:
#Service
public class BService {
private Vehicle vo;
#Autowired
ApplicationContext context;
public void getVehicle(String beanName){
this.vo = (Vehicle) context.getBean(beanName);
}
public void getVehicle(Class beanClz){
this.vo = (Vehicle) context.getBean(beanClz);
}
public void print(){
System.out.println("---class is "+vo.getClass());
}
}
public interface Vehicle {
}
#Component
public class OneVehicle implements Vehicle{
}
#Component
public class TwoVehicle implements Vehicle{
}
#SpringBootApplication
public class SpringDependenciesExampleApplication implements ApplicationRunner {
#Autowired
BService bService;
public static void main(String[] args) {
SpringApplication.run(SpringDependenciesExampleApplication.class, args);
}
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
bService.getVehicle("oneVehicle");
bService.print();
}
}
// output is ---class is class OneVehicle
This question already has answers here:
Injecting beans into a class outside the Spring managed context
(8 answers)
Closed 3 years ago.
Suppose I have
#Service
public class myService () {
public void sayHello() {
System.out.println("Hello");
}
}
public class myTestClass() {
#Autowired
private myService thisService;
public void transferHello() {
thisService.sayHello();
}
}
public class Application() {
public static void main(String[] args) {
SpringApplication.run(PilContainerApplication.class, args);
myTestClass thisTest = new myTestClass();
thisTest.transferHello();
}
}
Since myTestClass() is not a bean like service/controller, thisService would have a null reference when I use myTestClass thisTest = new myTestClass();.
I was wondering how to overcome this.. I tried using public static myService thisService and it said cannot use #Autowired on static fields.
Thank you
When a class cannot be changed to be annotated with a Spring stereotype, #Bean is a very good alternative :
#Configuration
public class MyBeansConfiguration {
#Bean
public MyTestClass getMyTestClass(MyService myService) {
return new MyTestClass(myService);
}
}
You can inject it now in your Application class :
public class PilContainerApplication {
#Autowired
MyTestClass myTestClass;
#PostConstruct
public void init(){
myTestClass.transferHello();
}
public static void main(String[] args) {
SpringApplication.run(PilContainerApplication.class, args);
}
}
Note that beans are instances of class and are injected in other instances of class that depend on them. So you don't have access to the injected beans in the static main() method. But you have access to it in an instance method annotated with #PostConstruct that will be executed when the dependencies were injected in the current bean.
Side note : classes have to start with an uppercase. I did it in the provided code.
if you want inject bean B without marking bean A via some annotation, or xml definition, you can use SpringBeanAutowiringSupport
public class A {
#Autowired
private class B b;
public A{
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
}
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
I wonder why the field injection works in the #SpringBootApplication class and the constructor injection does not.
My ApplicationTypeBean is working as expected but when I want to have a constructor injection of CustomTypeService I receive this exception:
Failed to instantiate [at.eurotours.ThirdPartyGlobalAndCustomTypesApplication$$EnhancerBySpringCGLIB$$2a56ce70]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.eurotours.ThirdPartyGlobalAndCustomTypesApplication$$EnhancerBySpringCGLIB$$2a56ce70.<init>()
Is there any reason why it does not work at #SpringBootApplication class?
My SpringBootApplication class:
#SpringBootApplication
public class ThirdPartyGlobalAndCustomTypesApplication implements CommandLineRunner{
#Autowired
ApplicationTypeBean applicationTypeBean;
private final CustomTypeService customTypeService;
#Autowired
public ThirdPartyGlobalAndCustomTypesApplication(CustomTypeService customTypeService) {
this.customTypeService = customTypeService;
}
#Override
public void run(String... args) throws Exception {
System.out.println(applicationTypeBean.getType());
customTypeService.process();
}
public static void main(String[] args) {
SpringApplication.run(ThirdPartyGlobalAndCustomTypesApplication.class, args);
}
public CustomTypeService getCustomTypeService() {
return customTypeService;
}
My #Service class:
#Service
public class CustomTypeService {
public void process(){
System.out.println("CustomType");
}
}
My #Component class:
#Component
#ConfigurationProperties("application.type")
public class ApplicationTypeBean {
private String type;
SpringBootApplication is a meta annotation that:
// Other annotations
#Configuration
#EnableAutoConfiguration
#ComponentScan
public #interface SpringBootApplication { ... }
So baiscally, your ThirdPartyGlobalAndCustomTypesApplication is also a Spring Configuration class. As Configuration's javadoc states:
#Configuration is meta-annotated with #Component, therefore
#Configuration classes are candidates for component scanning
(typically using Spring XML's element) and
therefore may also take advantage of #Autowired/#Inject at the field
and method level (but not at the constructor level).
So you can't use constructor injection for Configuration classes. Apparently it's going to be fixed in 4.3 release, based on this answer and this jira ticket.