Spring dependency injection via setter method and Configuration class - java

I wanna switch from xml based spring configuration into java-based. For constructor type injection it is easy. Example I am using:
#Configuration
public class BeanConfiguration {
#Bean
public GreetingService greetingService(GreetingDao greetingDao) {
return new GreetingService(greetingDao);
}
#Bean
public GreetingDao greetingDao() {
return new GreetingDao();
}
}
However, when I want to inject through setter method I am doing something like this:
#Configuration
public class BeanConfiguration {
#Bean
public MeetingDao meetingDao() {
return new MeetingDao();
}
#Bean
public MeetingService meetingService(MeetingDao meetingDao) {
MeetingService meetingService = new MeetingService();
meetingService.setMeetingDao(meetingDao);
return meetingService;
}
}
I am not sure if this is a possible way to inject with Java-based Configuration and setter method. Unfortunately I cannot find any example (for constructor dependencies a lot of examples exists). Also I am not sure in case of Java-based configuration when should I use constructor and when setter. In Spring docs it is described that I should use constructor for required and setter for optional dependency. However, I see it as the same result for this kind of approach.
Thanks for any clarification.
Adam

Under the assumption that you cannot change the classes themselves, you could always just not return the bean until you have injected all the values.
#Configuration
public class YourConfig{
#Value("${some.value.from.properties}")
private String someValue;
#Bean
#Autowired
public YourBean yourBean(TheDependency theDependency){
YourBean bean = new YourBean();
bean.setTheDependency(theDependency);
bean.setSomeValue(someValue);
return bean;
}
}
Constructor injection is always preferred with class dependencies, but not always possible.
If you have access to changing the source for these services and beans you are creating, then I suggest using constructor injection and placing "#Service" on the class and "#Autowired" on the constructors.
Properties are an example of "optional" dependencies; it is common to default values and behavior if not provided. I prefer just placing the "#Value" directly on that field instead of my constructor because otherwise you might end up with WAY too many parameters. This is "setter" injection, but you don't want an actual setter method (again, not normally anyways); you only want Spring to change the value, not any of your other code.
Using "#Value" is fine if you only have one or two properties(or 3? It's not an exact science); however, if you find you have a lot of fields with "#Value" then you should use a configuration bean instead. An "#ConfigurationProperties" bean can also be treated the same way with "setter" injection, but I prefer constructor injection to be sure that at least the bean is never null, even though it's values might be.

Related

Why is #Bean(initMethod="") not detecting given method in spring?

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.

Wicket #SpringBean and Spring #Autowired with injection via constructor

I have a Wicket panel in which I want to inject bean using #SpringBean
public class SomePanel extends Panel {
#SpringBean
private BlogSummaryMailGenerator blogSummaryMailGenerator;
}
But this BlogSummaryMailGenerator has injection via constructor defined like this:
#Component
public class BlogSummaryMailGenerator {
private BlogRepository blogRepository;
private BlogPostRepository blogPostRepository;
#Autowired
public BlogSummaryMailGenerator(BlogRepository blogRepository,
BlogPostRepository blogPostRepository) {
this.blogRepository = blogRepository;
this.blogPostRepository = blogPostRepository;
}
}
And when SomePanel is instantiated I am getting an exception
Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) ~[cglib-3.1.jar:na]
at org.apache.wicket.proxy.LazyInitProxyFactory.createProxy(LazyInitProxyFactory.java:191) ~[wicket-ioc-7.2.0.jar:7.2.0]
Adding empty no-args constructor to the BlogSummaryMailGenerator solves this issue but adding such code only to make injection work is wrong and I would like to avoid it.
Any suggestions how to make #SpringBean work with beans using injection via constructor?
The real problem is in CGLIB. It requires a default constructor to be able to create the proxy instance. The real Spring bean is created separately by Spring and has no such restrictions. The default constructor needed by CGLIB could be even private as far as I remember.
Update: Since Wicket 9.5.0 Wicket could also use ByteBuddy instead of CGLib.
Another solution is to use an interface for this bean. Then Wicket will use JDK Proxy instead of CGLIB and in this case there is no need of default constructor in the implementation.
Solution
To be able to take advantage of constructor injection for #SpringBean in Wicket components you just have to add Objenesis to your compile time dependencies.
Explanation
Objenesis is a little and less known byte code manipulation library which (in opposite to CGLIB provided together with Wicket) is able to create a proxy object for a class which has no default (no args) constructor. If you add it as a compile dependency to your project then Wicket will switch it's internal lazily initializable proxy creation logic to take advantage of Objenesis (instead CGLIB which requires no args constructor) while instantiating a proxy. Unfortunately this feature is not documented but I can confirm it works in my case.
The error message is pretty clear. Wicked trying to create instance of proxy class for BlogSummaryMailGenerator with CGLIB library. Since you didn't (or you can't) provide arguments to constructor, it looking for contstructor with no arguments. But it can't, and you get error.
Just replace constructor injection with property injection, and create no argument constructor:
#Component
public class BlogSummaryMailGenerator {
#Autowired
private BlogRepository blogRepository;
#Autowired
private BlogPostRepository blogPostRepository;
public BlogSummaryMailGenerator() {}
}
Actually, you do not need to declare an empty constructor. I did it just for clarity. Note, that BlogRepository and BlogPostRepository should be declared as beans (marked with #Component annotation, or created as #Bean in Spring configuration).
UPDATE:
When you add SpringComponentInjector in your WebApplication.init(), you can specify false for third paramter, which means 'wrapInProxies'. Wicket will never wrap Spring beans in porxy, and you can use #Autowired for constructors.
#Override
public void init()
{
super.init();
AnnotationConfigApplicationContext springContext =
new AnnotationConfigApplicationContext();
springContext.register(SpringConfig.class);
springContext.refresh();
getComponentInstantiationListeners().add(new SpringComponentInjector(this,
springContext, false));
}
The correct way to solve this is not to add Objenesis to your project, but to inject interfaces instead of concrete implementations, as #martin-g already explained (of course, we do not always have the privilege to be able to do the right thing, but when we do, we should do it).
I have a project that started to give the exact same error and stack after a library update I still don't exactly understand (complete Maven dependency hell, I inherited it, go easy on me). The reason was that I was creating a Spring request-scoped bean from a concrete subclass of ListModel<MyClass> and Wicket was hell bent on wrapping that class into a lazy loaded proxy, which it couldn't do because there was no zero-args-constructor.
I fixed it by changing the configuration class to create a named instance of IModel<List<MyClass>> and by defining the injected dependency using the name.
In the configuration class:
#Bean(name = "namedModel")
#RequestScope
public IModel<List<MyClass>> myObjectList() {
return new MyClass(parameters);
}
In the component:
#Inject
#Named("namedModel")
private IModel<List<MyClass>> myModel;

How can I test only one bean using existing application's spring configuration class?

In my code, I don't want to load all the beans defined in the XXApplicationConfig class.
XXApplicationConfig is a #Configuration annotated file which has bunch of spring beans defined.
So, I want to load only AppBean from XXApplicationConfig class while testing to reduce loading test time and also differentiate what I am testing. I also want to load the class using XXApplicationConfig class to make sure the bean configuration defined is correct as well.
This is my Test class ( modified ) to test AppBean class.
Could you let me know if this is the right approach and suggest how to make it better? Currently, this approach seems to be working. But, not sure if it is correct way of approaching it.
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationTest {
#Configuration
#PropertySources(value = {#PropertySource("classpath:test.properties")})
static class MyTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public XXApplicationConfig xxAppConfig() {
return new XXApplicationConfig();
}
#Bean
public CustomTestService customTestService() {
return new CustomTestService();
}
#Bean
public AppBean appBean() throws Exception {
return XXApplicationConfig().appBean();
}
}
#Autowired
private AppBean appBean;
#Test
public void testAppBean() {
test appBean.doSomething();
}
}
If you want to test just one object, just create one object of that class, using the constructor of that class. Spring beans are designed to be POJOs. The Spring context is just a convenient way of creating and connecting objects. Nothing stops you creating and connecting them yourself.
If you can instantiated the class you want to test and manually inject all the dependencies it required via constructor and/or setter getters, then you don't need to use Spring in your test.
However, if your bean:
uses private fields annotated with #Autowired or #Value without corresponding getters/setters.
depends on many other beans.
the behavior you want to test depends on Spring AOP/Proxies (you use #Transactional or #Cacheable for example).
Then you will need Spring to wired the bean. I personally prefer to define a a minimal #Configuration class for these cases.
Then again, if your bean meets the conditions on the list you should consider refactoring the bean to minimize its dependencies and facilitate testing.

Placement of #Autowired annotation

I've seen the #Autowired annotation placed just before the constructor of a POJO used as controller.
#Controller
public class LoginController{
private UsuarioService usuarioService;
#Autowired
public void LoginController(UsuarioService usuarioService){
this.usuarioService = usuarioService;
}
// More code
}
This constructor takes as argument the reference to the object we want Spring to inject.However if I place this annotation just before the property declaration the application works just the same.
#Controller
public class LoginController{
#Autowired
private UsuarioService usuarioService;
// More code
}
My question is what is the difference between this two approaches in terms of pros and cons.
My advice to you would be to never use #Autowired on fields (except in Spring #Configuration classes).
The reason is very simple: TESTING!!!!
When you use #Autowired on fields of a class, then that class becomes harder to unit test because you cannot easily use your own (possible mocked) dependencies for the class under test.
When you use constructor injection then is becomes immediately evident what the dependencies of the class are, and creating that class becomes straight forward (simple constructor call).
Some points that need to made:
1) Some might argue that even when #Autowired is used the class can still be unit tested with the use of Mockito's #InjectMocks, or Spring's ReflectionTestUtils.setField, but my opinion is that the creation of a unit under test should be as dead simple as possible.
2) Another point that could be mentioned is that there might be many arguments in the constructor making the manual invocation of the constructor (either in the test or elsewhere) difficult. This however is not a problem regarding the creation of the class, but a problem in the design. When a class has to many dependencies, in most cases it is trying to do too much and needs to broken into smaller classes with fewer dependencies.
#Autowired annotation on setter methods is to get rid of the element in XML configuration file. When Spring finds an #Autowired annotation used with setter methods, it tries to perform byType autowiring on the method
#Autowired annotation on properties is to get rid of the setter methods. When you will pass values of autowired properties using Spring will automatically assign those properties with the passed values or references.
Here is an example of both the usage:
http://www.tutorialspoint.com/spring/spring_autowired_annotation.htm
This Springsource blog post mentions that constructor injection makes it easy to validate required dependencies, if used in combination with contructors assertions that are good practice anyway and would also work if the class is instantiated outside Spring with the new operator:
#Controller
public class LoginController{
private UsuarioService usuarioService;
#Autowired
public void LoginController(UsuarioService usuarioService){
if (usuarioService == null) {
throw new IllegalArgumentException("usuarioService cannot be null.");
}
this.usuarioService = usuarioService;
}
}
This type of assertions are general best practice that is advised to do independently of the class being a Spring bean or not.
For setter or property injection there is also the #Required annotation for validating missing dependencies or #Autowired(required = true). According to the blog post, constructor injection provides this advantage, but for historical reasons setter injection is more frequently used in Spring.

Create a bean and autowire it

I currently have numerous classes with a field like
private MyStuff myStuff = new MyStuff();
It would be preferable if there was a MyStuff singleton which all the classes used. Our project has a MyConfiguration class with some Beans in it, but they don't seem to get used, at least not directly, so I can't use them for examples. I have been tasked to create a MyStuff bean in the MyConfiguration class, and then inject it into my other classes.
What I have so far:
#Configuration
public class MyConfiguration
{
#Bean
public MyStuff myStuff()
{
return new MyStuff();
}
}
public SomeClass
{
public void dealWithStuff()
{
someStuff.myMethod();
}
#Autowired
private MyStuff someStuff;
}
This compiles but does not run. someStuff is null when it tries to call myMethod(). Apparently it does not see the bean or make the connection. What am I missing?
I'm going to make an assumption, so correct me if I'm wrong: you are creating instances of SomeClass yourself. Something like
SomeClass someInstance = new SomeClass();
outside of any Spring component.
In this case, how do you expect Spring to inject anything since it's not even process it.
You need to let Spring create objects (beans) that need to have other beans injected.
The problem is naming. Refer to this for a complete description but briefly it says
If you intend to express annotation-driven injection by name, do not primarily use #Autowired, even if is technically capable of referring to a bean name through #Qualifier values. Instead, use the JSR-250 #Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process.
It means that you should be doing something like this:
#Resource(name="myStuff")
public void setSomeStuff(MyStuff someStuff){
this.someStuff = someStuff;
}
The reason is that you have defined your bean as myStuff but referring it as someStuff.
Also to have a singleton instance, all you need is to define its scope as singleton in your Spring Configuration as following:
#Configuration
public class MyConfiguration
{
#Bean #Scope(BeanDefinition.SCOPE_SINGLETON)
public MyStuff myStuff()
{
return new MyStuff();
}
}
As I see in your code, your object reference name is "someStuff" but you are referring to myStuff you should use someStuff.myMethod();

Categories