Spring Boot: scanning components into an injectable list or set - spring-boot

Spring Boot here. I have dozens and dozens of classes that are all subclass/implementations of the same interface:
public interface AnimalService {
void eat();
// etc.
}
#Component
public class DogService implements AnimalService {
#Override
public void eat() { return ... }
// etc.
}
// many many many of these
I have a #Service-annotated class (AnimalService) that needs to be injected with each one of these subclasses:
#Service
public class AnimalProcessingService {
#Autowired
private List<AnimalService> allAnimalServices; // DogService, SharkService, etc.
// the rest of the class definition is omitted intentionally
}
Now I could do this injection the hard way:
#Configuration
public class AnimalConfig {
private DogService dogService;
private SharkService sharkService;
// ...etc.
#Bean
public List<AnimalService> allAnimalServices() {
return Arrays.asList(dogService, sharkService /*, ...etc. */);
}
}
But there's gotta be an easier way to do this, right?
How can inject this List<AnimalService> without having to manually create the list? Does this method allow you to filter out certain candidates (like if I just want a subset of the animal services)? Ideally something annotation-based (even if I have to define my own annotations) that works with Spring autowiring/DI/component scanning. Thanks for any and all help here!

Essentially you're looking for #ComponentScan, which is a Spring (not Boot) annotation. It allows you to define a list of packages for Spring to scan for #Components (or "sub-annotations" such as #Service), to automatically instantiate beans of those classes and add them to the Spring context. So you can consider it a more automated way of the more manual #Bean method declaration.
Since you're using Spring Boot, however, you might want to look into #SpringBootApplication which, when used, enables component scan automatically. All you have to do is making sure your #Component classes are defined in the same package as, or sub-packages of, the #SpringBootApplication-annotated class.
Once you've enabled component scanning, you can just inject a List<AnimalService> where you need it, like in a constructor, and do your processing (filtering?) there.

Related

Spring Proxy Creation of Classes annotated with #Configuration or #Component

Spring uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. If a class is annotated with #Configuration, then CGLIB is used.
However, one limitation of Spring AOP is that once the call has finally reached the target object, any method calls that it may make on itself are going to be invoked against the this reference, and not the proxy. This piece of information is important to remember when using #Transactional and in other places as well.
So having that knowledge, in the code below, is Spring injecting the actual instance or the proxy of SimpleBean?
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean()); //<---
}
}
And what is the behavior if a class is annotation with #Component?
Let me give you another perspective.
Say there is an another bean AnotherBeanConsumer that also needs a simpleBean. Simple Bean has a Singleton scope:
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
#Bean
public AnotherBeanConsumer anotherBeanConsumer() {
return new AnotherBeanConsumer(simpleBean());
}
}
Now the question is, how its possible that two calls to simpleBean() made from different methods simpleBeanConsumer and anotherBeanConsumer return the same instance of the simple bean (since its a singleton obviously)?
IMO (and disclaimer, I'm not affiliated with spring or something), This is the main reason of creating proxies that wrap Configurations.
Now indeed Spring AOP has a limitation of calling methods just as you've stated, however who said that spring under-the-hood uses spring AOP? The bytecode instrumentation done on much lower levels doesn't have a limitation like this. After all creating a proxy means: "create a proxy object that will have the same interface but will alter the behavior", right?
For example if you use CGLIB that uses inheritance you could create a proxy out of configuration that looks like this (schematically):
class CGLIB_GENERATED_PROXY extends Config {
private Map<String, Object> singletonBeans;
public SimpleBean simpleBean() {
String name = getNameFromMethodNameMaybePrecached();
if(singletonBeans.get(name) != null) {
return singletonBeans.get(name);
}
else {
SimpleBean bean = super.simpleBean();
singletonBeans.put(name, bean);
return bean;
}
}
....
}
Of course its only a schematic picture, in real life there is an application context that basically provides the access to the map like this, but you get the point.
If its not enough, then there are some even more sophisticated frameworks that spring must make use of in order to load a configuration (like ASM)...
Here is an example:
If you use #ConditionalOnClass(A.class) and the class doesn't really exist in runtime, how spring can load the bytecode of the configuration that uses this configuration and not fail on something like NoClassDefFoundException?
My point is that it goes far beyond the spring AOP, and has its quirks :)
Having said that, nothing that I've describe above requires the real components to be always wrapped in Proxies of any kind. So in the most trivial case, when SimpleBean does not by itself have some annotations that require proxy generation (stuff like #Cached, #Transactional and so forth), Spring won't wrap the object of that type and you'll get a plain SimpleBean object.

Spring conditional component scan configuration

I have a configuration class which registers beans based on a very simple condition (checking a property value in application.properties). The configuration class and the condition are the following:
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
#ComponentScan(basePackageClasses = DBConfigComponents.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
and
public class DatabaseConfigurationCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("configuration.type").contains("db");
}
}
In addition of the beans registered in this configuration class I have component scan which scans for other components. When the condition is not met, I expect the beans which are defined in the configuration class not to be registered (which happens to be a case), but also I expect other classes which are annotated with #Component (or #Repository, #Service, etc.. ) and are in same folder as DBConfigComponents.class marker interface not to be registered, which does not happen. Beans which are scanned are always registered, no matter if the condition is fulfilled or not.
When I put the #Conditional(DatabaseConfigurationCondition.class) on each #Component annotated class, than it's working correctly, but I don't want to put it on each class separately.
Any suggestion?
Fortunately, I managed to fix this. The problem in my case was that I had another #ComponentScan annotation placed in other configuration class in other Maven module - not conditional on any property. The components which are in same package as DBConfigComponents marker interface were actually scanned by the other configuration class.
The way #ComponentScan works is on package level. Although, in different Maven modules, both configuration classes were in same package. #ComponentScan works perfectly fine with #Conditional. No need #Conditional to be placed on each component separately.
The best way to achieve this is not to annotate these beans using #Component / #Service and #Repository annotations. Instead you should return these as part of the configuration you have setup which would be DatabaseConfigurationLoader. See sample below.
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
#Bean
public SomeService someService() {
return new SomeService();
}
#Bean
public SomeComponent someComponent() {
return new SomeComponent();
}
}
Note: Typically #Configuration with #Conditional are used in libraries that you want to include in your spring boot application. Such libraries should not share the same package as your spring boot application. Thus they should not be picked up by #ComponentScan annotation. Beans from libraries should not be annotated with #Component / #Service / #Repository annotations. Spring suggests using AutoConfiguration for that. See https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html & https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
No need to implement Condition interface, you need to use '#ConditionalOnProperty' annotation:
#Configuration
#ComponentScan(basePackageClasses = DBConfigComponents.class)
#ConditionalOnProperty(name = "configuration.type", havingValue = "db")
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
you can use 'prefix' instead of 'havingValue' depending on your needs.

Load service for interface only where there is no other service

In my application I have interface with default implementation that is service:
public interface MessageGenerator {
Message getMessage();
}
#Service
public class PropertiesMessageGenerator implements MessageGenerator {
#Override
public Message getMessage() {
return doSmth();
}
}
This service is loaded by spring boots #ComponentScan and everything works fine until I've added new implementation with #Profile
#Service
#Profile("p1")
public class ProfileMessageGenerator implements MessageGenerator {
#Override
public Message getMessage() {
return doSmthWithProfile();
}
}
How can I stop loading into DI service PropertiesMessageGenerator when ProfileMessageGenerator is in context?
PS
I cannot use #Profile("default") on MultipleMessages, because I've got more profiles that I load at one time.
A bit late to the party, but might help future visitors:
I've got it working in Spring Boot 2.5.1 by configuring my service like this:
#Service
#ConditionalOnMissingBean(value = BaseService.class, ignored = DefaultService.class)
public class DefaultService implements BaseService {
...
}
Without the ignored field in the annotation, the condition will match DefaultService and thus not instantiate it. That behavior is a bit strange, but excluding itself from the condition makes it work. With this, I can create an additional implementation of BaseService that is just annotated with #Service and instead it will not instantiate DefaultService but the other implementation.
If you don't use component scanning you could define and pick the bean using #Qualifier
If you want to maintain using component scanning use the #Conditional annotation.
Create your own implementation of 'org.springframework.context.annotation.Condition.class' and pass this to the annotation.
This could read a property to define when it returns true which could be set when what you want is in the context.
#ConditionalOnMissingBean(MessageGenerator.class) can be used if you do not want to implement your own Condition.
Or just annotate ProfileMessageGenerator with #Primary.

Autowire specific implementations of persistence layer in Spring with Java based configuration

In a Spring MVC proyect I'm using Spring Data in the persistence layer so I have a bunch of repositories to access the data. I also have a layer for services so I have things like UserService or AuthorityService that use that repositories.
The problem is that I've been asked to create an interface to be able to change the implementation of the persistence layer (using DAOs for example) without have to touch a single line in the services. How can I specify in an Autowired of that interface what implementation to use? I´m using Java based config and I don't see how to inject it.
I also have a problem with the name of these new interfaces. Normally I would use a name like UserService but Spring use Service for the service layer so, What name is suitable for this type of interface?
You could mark the new implementation of the DAO as #Primary. Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
#Component
public class FooService {
private FooRepository fooRepository;
#Autowired
public FooService(FooRepository fooRepository) {
this.fooRepository = fooRepository;
}
}
#Component
public class JdbcFooRepository {
public JdbcFooService(DataSource dataSource) {
// ...
}
}
#Primary
#Component
public class HibernateFooRepository {
public HibernateFooService(SessionFactory sessionFactory) {
// ...
}
}
Because HibernateFooRepository is marked with #Primary, it will be injected preferentially over the jdbc-based variant assuming both are present as beans within the same Spring application context, which is often the case when component-scanning is applied liberally.
This annotation is semantically equivalent to the element's primary attribute in Spring XML.
I didn't completely follow your second question.

Where should #Service annotation be kept? Interface or Implementation?

I'm developing an application using Spring. I need to use the #Service annotation. I have ServiceI and ServiceImpl such that ServiceImpl implements ServiceI. I'm confused here as to where should I keep the #Service annotation.
Should I annotate the interface or the implementation with #Service? What are the differences between these two approaches?
I never put #Component (or #Service, ...) at an interface, because this make the interface useless. Let me explain why.
claim 1: If you have an interface then you want to use that interface for the injection point type.
claim 2: The purpose of an interface is that it define a contract that can been implemented by several implementations. On the other side you have your injection point (#Autowired). Having just one interface and only one class that implement it, is (IMHO) useless, and violates YAGNI.
fact: When you put:
#Component (or #Service, ...) at an interface,
have multiple classes that implements it,
at least two classes become Spring Beans, and
have an injection point that use the interface for type based injection,
then you will get and NoUniqueBeanDefinitionException
(or you have a very special configurations setup, with Environment, Profiles or Qualifiers ...)
Conclusion: If you use #Component (or #Service, ...) at an interface then you must violate at least one of the two clains. Therefore I think it is not useful (except some rare scenarios) to put #Component at interface level.
Spring-Data-JPA Repository interfaces are something complete different
Basically annotations like #Service, #Repository, #Component, etc. they all serve the same purpose:
auto-detection when using annotation-based configuration and classpath
scanning.
From my experience I am always using #Service annotation on the interfaces or abstract classes and annotations like #Component and #Repository for their implementation. #Component annotation I am using on those classes which serves basic purposes, simple Spring beans, nothing more. #Repository annotation I am using in the DAO layer, for e.g. if I have to communicate to the database, have some transactions, etc.
So I would suggest to annotate your interface with the #Service and other layers depending on the functionality.
I used #Component, #Service, #Controller and #Repository annotations only on the implementation classes and not on the interface. But #Autowired annotation with Interfaces still worked for me. If there's only one implementation of your interface Spring component scan automatically finds it with just #Autowired annotation. In case you have multiple implementations, you will need to use the #Qualifier annotation along with #Autowired to inject the correct implementation at the injection point.
1. #Service on Interfaces
#Service
public interface AuthenticationService {
boolean authenticate(String username, String password);
}
Normally, that's fine, but there's a drawback. By putting Spring's #Service on interfaces, we create an extra dependency and couple our interfaces with an outside library.
Next, to test the autodetection of our new service beans, let's create an implementation of our AuthenticationService:
public class InMemoryAuthenticationService implements AuthenticationService {
#Override
public boolean authenticate(String username, String password) {
//...
}
}
We should pay attention that our new implementation, InMemoryAuthenticationService, doesn't have the #Service annotation on it. We left #Service only on the interface, AuthenticationService.
So, let's run our Spring context with the help of a basic Spring Boot setup:
#SpringBootApplication
public class AuthApplication {
#Autowired
private AuthenticationService authService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
When we run our app, we may get the infamous NoSuchBeanDefinitionException, and the Spring context fails to start.
Therefore, placing #Service on interfaces isn't enough for the auto-detection of Spring components.
2. #Service on Abstract Classes
Using the #Service annotation on abstract classes isn't common.
We'll start by defining an abstract class from scratch and putting the #Service annotation on it:
#Service
public abstract class AbstractAuthenticationService {
public boolean authenticate(String username, String password) {
return false;
}
}
Next, we extend AbstractAuthenticationService to create a concrete implementation without annotating it:
public class LdapAuthenticationService extends AbstractAuthenticationService {
#Override
public boolean authenticate(String username, String password) {
//...
}
}
Accordingly, we also update our AuthApplication, to inject the new service class:
#SpringBootApplication
public class AuthApplication {
#Autowired
private AbstractAuthenticationService authService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
After we run our AuthApplication, the Spring context doesn't start. It ends up with the same NoSuchBeanDefinitionException exception again.
So, using #Service annotation on abstract classes doesn't have any effect in Spring.
3. #Service on Concrete Classes
Contrary to what we've seen above, it's quite a common practice to annotate the implementation classes instead of abstract classes or interfaces.
In this way, our goal is mostly to tell Spring this class is going to be a #Component and mark it with a special stereotype, which is #Service in our case.
Therefore, Spring will autodetect those classes from the classpath and automatically define them as managed beans.
So, let's put #Service on our concrete service classes this time around. We'll have one class that implements our interface and a second that extends the abstract class that we defined previously:
#Service
public class InMemoryAuthenticationService implements AuthenticationService {
#Override
public boolean authenticate(String username, String password) {
//...
}
}
#Service
public class LdapAuthenticationService extends AbstractAuthenticationService {
#Override
public boolean authenticate(String username, String password) {
//...
}
}
We should take notice here that our AbstractAuthenticationService doesn't implement the AuthenticationService here. Hence, we can test them independently.
Finally, we add both of our service classes into the AuthApplication and give it a try:
#SpringBootApplication
public class AuthApplication {
#Autowired
private AuthenticationService inMemoryAuthService;
#Autowired
private AbstractAuthenticationService ldapAuthService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
Our final test gives us a successful result, and the Spring context boots up with no exceptions. Both of the services are automatically registered as beans.
You might have a look at this page for the other explanations.
Pros of putting annotation on #Service is that it gives a hint that it is a service. I don't know if any implementing class will by default inherit this annoation.
Con side is that you are coupling your interface with a specific framework i.e. Spring, by using spring specific annotation.
As interfaces are supposed to be decoupled from implementation, I would not suggest using any framework specific Annotations or object part of your interface.
I would put #Service on your class but put the name of the interface as a parameter to the annotation e.g.
interface ServiceOne {}
#Service("ServiceOne")
class ServiceOneImpl implements ServiceOne{}
By doing that you get all the benefits and can still inject the interface but get the class
#Autowired
private ServiceOne serviceOne;
So your interface is not tied to spring framework and you can change the class at any time and not have to update all your injection points.
So if I wanted to change the implementation class I could just annotate the new class and remove from the first but that's all that is required to be changed. If you inject the class you could have a lot of work when ever you want to change the impl class.
One benefit of spring is to easily switch Service (or other) implementation.
For this, you need to annotate on the interface and declare variable like this :
#Autowired
private MyInterface myVariable;
and not :
#Autowired
private MyClassImplementationWhichImplementsMyInterface myVariable;
Like the first case, you can activate which implementation to inject from the moment it is unique (only one class implements the interface).
In the second case, you need to refactor all your code (the new class implementation has another name).
As a consequence, the annotation needs to be on the interface as much as possible. Furthermore, JDK proxies are well suited for this : they are created and instantiated at application startup because runtime type is known by advance, contrary to CGlib proxies.
interface MyService {}
#Service
class MyServiceImpl implements MyService{}
#Autowired
private MyService myService;
My testing result on spring-boot 2.7.4 is:
Adding #Service ONLY to interface doesn't create spring bean named MyService. It will error on Autowired.
#Service will need to be added to implementation class to create bean com.*.service.impl.MyServiceImpl $$EnhancerBySpringCGLIB$$9140ae19 Spring will wire it to private MyService myService;
There are 5 annotations which could be used for making spring beans. List in below of answers.
Do you really need an interface? If you are going to have one implementation for each service interface, just avoid it, use only class. Of course, if you don't have RMI or when interface proxy is required.
#Repository - use for injecting your dao layer classes.
#Service - use for injecting your service layer classes. In service layer also you might need to use #Transactional annotation for db transaction management.
#Controller - use for your frontend layer controllers, such as JSF managed beans injecting as spring beans.
#RestController - use for spring rest controllers, this would help you to avoid every time to put #ResponseBody and #RequestBody annotations in your rest methods.
#Component - use it in any other case when you need to Inject spring bean which is not controller, service, or dao class
To put it simply:
#Service is a Stereotype annotation for the service layer.
#Repos­itory is a Stereotype annotation for the persis­tence layer.
#Component is a generic stereotype annotation used to tell Spring to create an instance of the object in the Appl­ication Context. It's possible to
define any name for the instance, the default is the class name as camel case.

Resources