Java Spring Framework. Declare classes as bean only - java

I am learning Java Spring in parallel with tackling a problem.
My question is:
Can I have a pool of classes defined as beans only (without them declared in the JAVA source)?
When the application runs it will do computations only on the declared classes available in the beans.
So for instance I have beans for the classes A, B ,C; But I have no declaration in the java code for this classes. I may group them together under an Interface say DocumentationVersionSpecificTag
Is this approach feasible ?

You are trying to inject objects that are not in your source code using #Autowired, you can achieve this using a configuration class, a class annotated with #Configuration, and configure these classes as beans in it, using #Bean:
#Configuration
public class ProjectConfiguration{
#Bean
public A configureA(){
return new A(); // use the suitable constructor you want
}
#Bean
public B configureB(){
return new B(); // use the suitable constructor you want
}
#Bean
public C configureC(){
return new C(); // use the suitable constructor you want
}
}
After you can inject these classes with #Autowired:
#Autowired
A a;
For more details, see this link.

Related

Spring: #Autowired without #Component

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.

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.

How to create bean using #Bean in spring boot for abstract class

I have requirement to migrate old style spring project to Spring boot.
Assume below code snippet I have to migrate to Spring boot style.
Here my ask , how to convert below abstract bean to #Bean ?
<bean id="sample" class="com.test.core.common.AbstractClass" abstract="true">
<property name="sample1" ref="sample1" />
<property name="sample2" ref="sample2" />
</bean>
Write your abstract base class in plain Java (without any Spring coupling) :
public abstract class AbstractClass{
private Sample1 sample1;
private Sample2 sample2;
public AbstractClass(Sample1 sample1, Sample1 sample2){
this.sample1 = sample1;
this.sample2 = sample2;
}
...
}
Note that adding a constructor with parameters (both for the abstract class and the concrete class) makes injection easier and dependencies clearer.
Then you have two ways :
1) Annotate the concrete class(es) with #Component.
Such as :
#Component
public class MyClass extends AbstractClass{
public MyClass (Sample1 sample1, Sample1 sample2){
super(sample1, sample2);
}
}
This first way has the advantage to be short : just an annotation to add.
But it makes de facto the subclass as a bean that may potentially be loaded by the Spring context.
2) Alternatively, declare the bean in a Configuration class.
Such as :
#Configuration
public class MyConfig{
#Bean
public MyClass myClass(Sample1 sample1, Sample1 sample2){
return new MyClass(sample1, sample1);
}
}
This second way is more verbose but has the advantage to not modify the subclass code and also let clients of the class to decide whether the class should be a bean.
Each approach has its advantages and its drawbacks.
So to use according to the concrete requirement.
There is no need in converting this code. You only need to make the classes that extend com.test.core.common.AbstractClass declared as spring managed beans by either annotating them with #Component or #Service or declaring a method annotated with #Bean in your configuration class.
Generally "abstract bean" is not needed in Java Configuration, there is even no equivalent. It was needed in xml configuration for parameter inheritance which is now achievable with plain java methods. Find example from Stephane Nicoll who is Spring Core developer.
Since Java has it's own mechanism of abstract classes and inheritance in place, you don't need to do the coupling of following code in your spring coupling.
<bean id="sample" class="com.test.core.common.AbstractClass" abstract="true">
<property name="sample1" ref="sample1" />
<property name="sample2" ref="sample2" />
</bean>
In XML config, you needed to do this to specify the template for inheritance of child beans. But since Springboot uses Java configuration, this part is handled directly with Java inheritance.
What it means is that you can declare this abstract class as a normal Java abstract class and treat only the child classes as beans without worrying about the abstract parent class.
When we want to instantiate an abstract class or an interface as a #Bean (with spring-java-config), we can generally:
Instantiate an anonymous inner class.
Override all abstract methods! ;(
So for a Method injection, it would look like:
#Configuration
class FreakyConfig {
#Bean
#RequestScope // !! (stateful)
public MyFooBarDelegate delegate() {
return MyFooBarDelegate.of(...);
}
#Bean // singleton, stateless, abstract (no spring deps.)! ;)
public MyFooBarAbstractSingletonBean() {
return new MyFooBarAbstractSingletonBean() { // anonymous inner class!
#Override
protected MyFooBarDelegate fooBarDelegate() { // ...and override the (injected)/prescribed methods.
return delegate();
}
};
}
} // (stress) tested ;)
Another good(?) question: Where is that latest ("current") "spring-javaconfig/docs"???
see also:
1.4.6. Method Injection
Method Injection Article
1.5.3. Singleton Beans with Prototype-bean Dependencies

Using packages written by the other in Spring

So the package I'm trying to use is here:
https://github.com/spring-projects/spring-batch-extensions/tree/master/spring-batch-excel
and on that page is the spring bean configuration for that package. I'm new to spring, and I don't understand how to actually write code that uses the bean.
The config looks like this:
#Bean
public PoiItemReader excelReader() {
PoiItemReader reader = new PoiItemReader();
reader.setResource(new ClassPathResource("/path/to/your/excel/file"));
reader.setRowMapper(rowMapper());
return reader;
}
#Bean
public RowMapper rowMapper() {
return new PassThroughRowMapper();
}
So if I have another class called reader somewhere, how do I use this bean configuration to get the lines from the excel file that PoiItemReader gets from .setRowMapper()?
The row mapper has a list of arrays that split the values in the excel rows, but I don't know how to get that list.
Do I call the excelReader() method?
PoiItemReader doesn't have any useful methods associated with it, so I don't think I'm supposed to do that.
From your code snippet, it looks like you're using #Configuration on the class that declares your beans.
Using Java Configuration is one of the ways to define your beans so that spring will understand them and inject property at runtime.
An alternative way is using annotations like #Service, #Component, #Controller and so forth (an answer provided by maneesh)
So the first thing to understand in Spring is that there are many ways of configuration (there is also an old-way, using XML and Groovy based configuration).
Now when you write a spring application, usually you use Spring Beans from other Spring Beans.
So, if you configure bean A to have a reference on Bean B, Spring will inject it for you. Example:
public class A {
private B b;
public A(B b) {
this.b = b;
}
public void doSomething() {
b.foo();
}
}
public class B {
public void foo() {...}
}
The "java config" way for defining these beans can look like this:
#Configuration
public class MyConfiguration {
#Bean
public A a(B b) {
return new A(b);
}
#Bean
public B b() {
return new B();
}
}
In your example, you've used an alternative syntax for Configurations (line reader.setRowMapper(rowMapper());:
#Configuration
public class MyConfiguration {
#Bean
public A a() {
return new A(b());
}
#Bean
public B B() {
return new B();
}
}
It looks like from A you just call a method that creates B. But it is not quite like that, in fact, spring is supposed to wrap your configuration in some kind of runtime proxy so that, for example, if you call b() many times (for many beans), it will always return the same instance, because B has a singleton scope. All-in-all Configuration classes should be considered like a Java DSL to create beans and not a regular code.
Now the last question to consider is where all this configuration gets started. The answer to this really depends on the environment you're running it. Usually, spring is already integrated into existing projects that run on-top of tomcat or other servers, or if it's a Spring Boot application it already provides "well known" integration ways. So you might just ask someone from your project (I assume it's not a homework or something) how does your application is integrated with spring, it's beyond the scope of this question.
you should declare #Service or #Component over the class where you want to read file, and use #Autowired annotation to use PoiItemReader
#Service
public class SomeclassName {
#Autowired
private PoiItemReader excelReader;
public void somemethod() {
//do some reading stuff here using excelReader
}
}

Spring - detect dependencies not annotated with #Component

With Guice, getting an instance of A will automatically resolve B even if I have not defined anything about B.
public class A {
#Inject B b;
}
public class B {
...
}
However, Spring seems to require that components are marked with #Component or another stereotype.
I have some common libraries that need to work with different CDI implementations. How can I get Spring to automatically construct instances without annotating with #Component or defining every single class in my #Configuration?

Categories