In a real project, I found out that #Component may be omitted in the following code:
// no #Component !!!!!
public class MovieRecommender {
private final CustomerPreference customerPreference;
#Autowired
public MovieRecommender(CustomerPreference customerPreference) {
this.customerPreference = customerPreference;
}
// ...
}
#Component
public class CustomerPreference {...}
(The example is taken from the official Spring docs https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation , and the docs show no #Component at all, which may mean either that it is not needed, or that it is just not shown.)
The project where I work does not use any XML bean declarations, but it uses frameworks other than just Spring, so it is possible that something declares the class as a bean. Or it may be a feature of the version of Spring that we use, and if that feature is not documented, it may be dropped later.
Question:
Must the class that uses #Autowired be annotated with #Component (well, be a bean)? Is there any official documentation about that?
UPD Folks, there is no #Configuration and no XML configs in the project, I know that such declarations make a bean from a class, but the question is not about them. I even wrote "(well, be a bean)" in the question above to cover that. Does #Autowired work in a class that is not a bean? Or maybe it declares the class that uses it as a bean?
there are several ways to instantiate a bean in Spring.
One of them indeed is with the #Component annotations, with that, Spring will scan all the packages defined for component-scan and initialize all annotated classes (either with #Component or one of the annotations that uses it - Controller, Service, etc.).
Other way to initialise beans is using a configuration class (annotated with #Configuration) that includes methods annotated with #Bean. each of these methods will create a bean.
There's also an option to create the beans using xml configurations, but this is becoming less and less common, as the annotation-based approach is more convinient
According to https://stackoverflow.com/a/3813725/755804 , with autowireBean() it is possible to autowire a bean from a class not declared as a bean.
#Autowired
private AutowireCapableBeanFactory beanFactory;
public void sayHello(){
System.out.println("Hello World");
Bar bar = new Bar();
beanFactory.autowireBean(bar);
bar.sayHello();
}
and
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
public class Bar {
#Autowired
private Foo foo;
public void sayHello(){
System.out.println("Bar: Hello World! foo="+foo);
}
}
On the other hand, by default the latest Spring does not assume that classes that use #Autowire are #Component-s.
UPD
As to the mentioned real project, the stack trace shows that the constructor is called from createBean(). That is, the framework creates beans from classes declared in the framework's configs.
Related
Edit Fixed by changing package.
I have this configuration file for spring framework
#Configuration
public class AppConfig {
#Bean(initMethod = "populateCache")
public AccountRepository accountRepository(){
return new JdbcAccountRepository();
}
}
JdbcAccountRepository looks like this.
#Repository
public class JdbcAccountRepository implements AccountRepository {
#Override
public Account findByAccountId(long
return new SavingAccount();
}
public void populateCache() {
System.out.println("Populating Cache");
}
public void clearCache(){
System.out.println("Clearing Cache");
}
}
I'm new to spring framework and trying to use initMethod or destroyMethod. Both of these method are showing following errors.
Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Could not find an init method named 'populateCache' on bean with name 'accountRepository'
Here is my main method.
public class BeanLifeCycleDemo {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new
AnnotationConfigApplicationContext(AppConfig.class);
AccountRepository bean = applicationContext.getBean(AccountRepository.class);
applicationContext.close();
}
}
Edit
I was practicing from a book and had created many packages for different chapters. Error was it was importing different JdbcAccountRepository from different package that did not have that method. I fixed it and it works now. I got hinted at this from answers.
Like you said, if you are mixing configurations types, it can be confusing. Besides, even if you created a Bean of type AccountRepository, because Spring does a lot of things at runtime, it can call your initMethod, even if the compiler couldn't.
So yes, if you have many beans with the same type, Spring can be confused an know which one to call, hence your exception.
Oh and by the way, having a Configuration creating the accountRepoisitory Bean, you can remove the #Repository from your JdbcAccountRepository... It is either #Configuration + #Bean or #Component/Repository/Service + #ComponentScan.
TL;DR
Here is more information and how Spring creates your bean : What object are injected by Spring ?
#Bean(initMethod = "populateCache")
public AccountRepository accountRepository(){
return new JdbcAccountRepository();
}
With this code, Spring will :
Detect that you want to add a Bean in the application Context
The bean information are retrieved from the method signature. In your case, it will create a bean of type AccountRepository named accountRepository... That's all Spring knows, it won't look inside your method body.
Once Spring is done analysing your classpath, or scanning the bean definitions, it will start instanciating your object.
It will therefor creates your bean accountRepository of type AccountRepository.
But Spring is "clever" and nice with us. Even if you couldn't write this code without your compiler yelling at you, Spring can still call your method.
To make sure, try writing this code :
AccountRepository accountRepository = new JdbcAccountRepository();
accountRepository.populateCache(); // Compiler error => the method is not found.
But it works for Spring... Magic.
My recommandation, but you might thinking the same now: If you have classes across many packages to answer different business case, then rely on #Configuration classes. #ComponentScan is great to kickstart your development, but reach its limit when your application grows...
You mix two different ways of spring bean declaration:
Using #Configuration classes. Spring finds all beans annotated with #Configuration and uses them as a reference to what beans should be created.
So if you follow this path of configuration - don't use #Repository on beans. Spring will detect it anyway
Using #Repository - other way around - you don't need to use #Configuration in this case. If you decide to use #Repository put #PostConstruct annotation on the method and spring will call it, in this case remove #Configuration altogether (or at least remove #Bean method that creates JdbcAccountRepository)
Annotate populateCache method with #PostConstruct and remove initMethod from #Bean. It will work.
I've seen other questions related to this issue but none of them actually helps with my issue.
I have these two properties defined in my test class.
#Autowired
private MessagingProperties messagingProperties;
#Autowired
private MessagingPropertiesRefactor messagingPropertiesRefactor;
I am trying to create a new version of MessagingProperties.java. I basically copied this class and create a new file MessagingPropertiesRefactor.java (same package/dir) and pasted the same code. I changed the class definition, etc., but for the most part are the same.
I get an IntelliJ design time compiler error stating that "Could not autowire. No beans of 'MessagingPropertiesRefactor' type not found."
I then searched for every single Usage of the original class to double check if it has been declared somewhere but I have found nothing.
I am "new" to Java (and Spring). Has anyone run into the same issue before?
If your new class MessagingPropertiesRefactor is not annotated with #Component you have got to declare it either in beans.xml like this:
<beans>
<bean name="messagingPropertiesRefactor" class="com.package.path.MessagingPropertiesRefactor"/>
</beans>
or in your AppConfig.java like this:
#Configuration
public class AppConfig {
#Bean
public MessagingPropertiesRefactor messagingPropertiesRefactor() {
return new MessagingPropertiesRefactor();
}
}
Source Reference
If you want to apply Spring dependency injection in tests, you have 2 basic options:
If you want to have the beans declared in XML, declare both MessagingProperties and MessagingPropertiesRefactor in XML file and annotate the test class the following way:
#RunWith(SpringRunner.class)
#ContextConfiguration("your.xml")
The beans should be successfully autowired.
To have the beans declared in JavaConfig, do the following
2.1. Annotate you test class like this:
#RunWith(SpringRunner.class)
#ContextConfiguration
2.2. Inside the test class, define context configuration class that declates the beans that you need:
#Configuration
static class Config {
#Bean
public MessagingProperties messagingProperties() {
// Assuming MessagingProperties has default ctor.
return new MessagingProperties();
}
// Same for MessagingPropertiesRefactor
}
Enjoy the autowired beans :)
*The config class does not have to be nested into the test class.
For more info regarding the topic, please consult Spring documentation: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-javaconfig, Chapter 3.5.4. Context management.
Ensure that MessagingPropertiesRefactor has all annotations applied to the class that MessagingProperties has. If it has, than look for the bean definition of MessagingProperties in xml configurations for your project and make a copy.
So, I found the "magic" line of code that makes it a "bean".
#EnableConfigurationProperties({MessagingProperties.class, MessagingPropertiesRefactor.class})
public class MessagingConfiguration {
#Autowired
private MessagingProperties messagingProperties;
#Autowired
private MessagingPropertiesRefactor messagingProperties;
So, apparently all I needed to do is add this class to the #EnableConfigurationProperties attribute. Then I can use it anywere by using the #Autowired attribute.
Now... why or how, I don't know (like I said, I am "new" to java) and I would love for someone to elaborate on this.
Good day, guys. I have a question about autowiring services into my classes when using Springboot. All of the examples I have seen on the Internet as well as in the Springboot specification do something of the like (taking an excerpt from the Springboot version 1.5.7 specification):
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
#Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
This is a class that injects a property through its constructor, by means of #Autowiring the constructor. Another form is to #Autowire the property like this:
#Autowired
private final RiskAssessor riskAssessor
But, where I work, for these two methods to work, I have been told that I need to use this method:
applicationContext.getAutowireCapableBeanFactory().autowireBean(Object.class)
They have told me that I need this in order for the #Autowired annotation to work.
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly? (Something like #AutowiredClass). The above method is too verbose and hard to remember, so surely there must be a better way to make #Autowired work on classes in order to inject services, just like we do in Grails where we just say def someService and it is automatically injected.
If you want properly use #Autowired in your spring-boot application, you must do next steps:
Add #SpringBootApplicationto your main class
Add #Service or #Component annotation to class you want inject
Use one of two ways that you describe in question, to autowire
If you don't have any wiered package structure and the main class package includes all other classes you want spring to instantiate (directly or in the subpackages) a simple annotation #ComponentScan on your main class will help you save all those boiler plate code. Then spring will do the magic, it will go and scan the package(and subpackages) and look for classes annotated with #Service, #Component etc and instantiate it.
Even better, use #SpringBootApplication in your main class, this will cover #Configuration as well. If it is a green field project , I would encourage to start from start.spring.io - a template generation/scaffolding tool for spring
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly?
There is: #SpringBootApplication
If you put this at the root of your application (file that contains the main class) and as long as your services are at the same package or a sub-package, Spring will auto-discover, instantiate, and inject the proper classes.
There's an example in this walk-through: REST Service with Spring Boot
As described in that page:
#SpringBootApplication is a convenience annotation that adds all of the following:
#Configuration tags the class as a source of bean definitions for the application context.
#EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
#ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.
You need to annotate the implementation of RestService as a #Service or #Component so Spring would pick it up.
#Service
public class MyRiskAssessorImpl implements RiskAssessor {
///
}
#Autowired almost works out of the box. Just do your component scanning of the class you want to autowire and you are done. Just make sure your main class (or main configuration class) uses #ComponentScan("{com.example.app}") or #SpringBootApplication (main class). The docs explain this stuff pretty good
I have a class as such.
public class Foo {
#Autowired
private Bar bar;
}
The class Bar lives in a jar that comes from a dependency pulled in from Maven. How can I add that class to the Spring context so that it will be properly autowired? I can't add the #Component to the source code. Is there a way to do it without XML? I'm currently trying to stick with annotation driven injection as much as possible.
I'm using Spring 3.1.4-RELEASE
It appears from your description that you know exactly which class is being instantiated. If that's the case, then in one of your #Configuration classes, you can simply create the appropriate bean, and Spring will configure it before deployment into the context:
#Bean public Foo foo() {
return new Foo();
}
Spring documentation defines #Component annotation in the following way: "Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning. "
This is concise, but it does not say a lot. I understand that #Component is used to indicate that a class lifecycle (creation/destruction) will be managed by Spring. The question I have: I need to use it only in classes that will be autowired somewhere (1) or do I also need to use it in classes that have autowired attributes (2)?
(1)
#Component
class B {
}
class A {
// #Autowired
B b;
}
(2)
#Component
class B {
}
#Component
class A {
// #Autowired
B b;
}
Well, strictly speaking you don't have to use anywhere, you can define beans in XML like in the old days. Also you can use #Service or #Repository like in the old days. But back to your question:
If your bean A is not annotated with #Component or otherwise known to the Spring context, it will never be created and managed by Spring. So you either have to use an annotation or define A in XML.
This is true for B as well. If you want it to be a subject for autowiring, it must be known to Spring - either by annotation scanning or by XML.
At the end of the day it doesn't really matter whether you use XML, annotation or Java configuration. It's important that both beans are known to application context.