What will happen when I add multiple #ComponentScan in different #Configuration class - spring

When using spring, I want to have a configuration structure like:
//package com.test
//main configuration A
#Configuration
#ComponentScan({"com.pakcage.A", "com.common"})
public class AppA{
...
}
//package com.test
//main configuration B
#Configuration
#ComponentScan({"com.pakcage.B", "com.common"})
public class AppB{
...
}
//package com.common
//sub configuration for common use
#Configuration
#ComponentScan({"com.pakcage.common1", "com.package.common2"})
public class CommonConfig{
...
}
I can launch my Application by useing Configuration AppA or Configuration AppB, and all of them contains some common packages to scan like
"com.pakcage.common1"/"com.package.common2"
, I want to put it into a single configuration.
I want to ask
What will happen when I put multiply #ComponentScan, there will be a combination of all of these #ComponenScan?
Is there some source code reference to read about how this happen?

Yes, all the packages defined by any #ComponentScan will be scanned.
Yes, spring framework is opensource. You can access the sources here.

Related

Does #SpringBootApplication scans test folder for configurations?

I've a #SpringBootApplication annotation in main class of my Spring Boot Application with ordinary folders structure (and #SpringBootApplication is one level package upper then beans in other packages)
I defined some #Configuration classes in some packages but under the test folder.
Will #SpringBootApplication autoconfigure it when start application?
Will #SpringBootApplication autoconfigure it when it will be finded by #SpringBootTest (it's also one level upper but in test folder) when test started?
I am not completely sure, but I would say no, #SpringBootApplication does not scan #Configuration classes in your test folder. What you should use instead is #TestConfiguration and then in your #SpringBootTest add #Import(YourTestConfiguration.class). Find an example below:
#TestConfiguration
public class YourTestConfiguration {
#Bean
(...)
}
#SpringBootTest
#Import(YourTestConfiguration.class)
class AppTests {
(...)
}
You can read more about this and check complete examples in the following online resources:
https://reflectoring.io/spring-boot-testconfiguration/
https://howtodoinjava.com/spring-boot2/testing/springboot-test-configuration/

How to set up a spring based java library?

I am trying to create a java library that uses spring. But I am not able to find any resources that shows you how to set up component scan and load all beans when you do not have a main entry point into the application.
A common approach used by spring is to define a #EnableXXXX and let the client of your library to enable it by annotating it on their configuration class. Something like :
#Target(ElementType.TYPE)
#Import(FooLibarayConfiguration.class)
#Documented
public #interface EnableFooLibaray{
}
#ComponentScan("xxxxx")
#Configuration
public class FooLibarayConfiguration{
}
And client enables your library by :
#EnableFooLibaray
#Configuration
public class Application{
}
The #Import actually supports a more dynamic way to include the bean settings for your library. You can refer to many existing #EnableXXX provided by spring such as #EnableAsync , #EnableWebSecurity , #EnableTransactionManagement , #EnableCaching etc. for many examples.

Spring how to (de)activate complete packages by profile

I have a Spring Boot application. For a specific package of services i need to provide 2 different implementations (one using db one using HTTP API).
One set should be active by default and the other by setting a profile.
the package structure is:
otherstuff
service
dbimpl
httpimpl
<serviceinterfaces>
dbimpl (all classes in there) should be active by default and httpimpl should be activate by profile (that should deactivate dbimpl classes)
What is the easiest way to implement such an "switch"?
I thought about using #ComponentScan but i don't know how to switch between different Classes with component scan - i don't know how to have multiple classes with #ComponentScan and switch between them by profile.
I also don't want (at the moment worst option) to annotate each class with Profile.
So how to (de)activate complete packages by profile?
Assuming you have something like
package org.example;
#SpringBootApplication
#ComponentScan("org.example")
public class BootApplication {
}
1) Split your configuration into multiple classes and put them into separate nested package, say org.example.config
package org.example.config;
#Profile("default")
#Configuration
#ComponentScan("service.dbimpl")
public class DefaultConfig {
}
and
package org.example.config;
#Profile("!default")
#Configuration
#ComponentScan("service.httpimpl")
public class CustomConfig {
}
Assuming default is the name of default profile. It could be set up in application.properties file
spring.profiles.active=default
2) Replace global component scan in SpringBoot application with a less global one
#SpringBootApplication
#ComponentScan("org.example.config") // instead of "org.example"
public class BootApplication {
}
This should do the trick

Can I get another #Configuration only through #ComponentScan

I am using spring-boot 2.0.4; I have a bunch of services and they have a common configuration class marked with #Configuration.
I want to move this to a common dependency which will have this #Configuration, and based on the need, any micro-service can use #ComponentScan to activate this configuration from dependency.
I have done this for #Component classes, and it's working fine. I activate any particular component I need by adding it into #ComponentScan. How can I activate the configuration in a similar manner(based on need).
Here are the code examples:
Common Configuration:
package abc.department.common.configs.mongo
#Component
public class AbcMongo {
#Bean
public MongoTemplate mongoTemplate() {
// ... create MongoTemplate.
return createdMongoTemplate;
}
}
Here is a class which uses the above dependency:
#Configuration
#ComponentScan("abc.department.common.configs.mongo")
public class MyServiceConfigs {
}
Similarly, I want to do something like this:
package abc.department.common.configs.security.web
#Configuration
#EnableWebSecurity
public class AbcWebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ... do common configs;
}
}
and now, if a service would need web-security config, it could get like:
#Configuration
#ComponentScan({"abc.department.common.configs.mongo","abc.department.common.configs.security.web"})
public class MyServiceConfigs {
}
#Configuration is meant to specify the beans, for example:
#Configuration
public class MyMongoConfiguration {
#Bean
public MongoTemplate mongoTemplate() {
return new ...
}
#Bean
public MySampleBean mySampleBean(MongoTemplate tpl) {
return new MySampleBean(tpl);
}
}
But if so why do you need to work with #Component at all (at least for the beans you create)?
Configuration is a special bean used by Spring framework to load other beans and it can be viewed as a "substitution"/alternative technique to component scanning.
I believe that, if you have some infrastructure configuration that loads a bunch of "infrastructure beans" (shared jar if I get you right), then the services that use this jar should only say "Hey, I want to load this configuration" and not to scan inside the packaging structure of that jar. Why do I think so?
What if you decide to add new beans into a new package in the infra, should external services change their code and define an additional folder to scan? - Probably no.
What if you decide to move the infra to another package?
Now in Spring there are two simple ways to do this that come to mind:
Way 1: Use #Import Annotation
#Configuration // this is from "shared artifact"
class MyInfraConfiguration {
}
#Configuration // this is from an "applicative service" that uses the infra jar in dependencies
#Import(MyInfraConfiguration.class)
class ServiceAConfiguration {
}
Way 2: Use Spring Factories mechanism
The first way has a drawback: You need to know in a Service what infra configuration exactly is. If you see it as a drawback, consider using spring factories.
Spring factories allow registering the infra configuration in some file so that spring boot will load it in service one automatically, you won't even need to mention MyInfraConfiguration in the Service Configuration, just add a dependency to the infra jar and it will work.
In the infra component create:
META-INF/spring.factories
And add there:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycompany.myinfra.whatever.InfraConfiguration
That's it.
Now if you want to customize the loading of beans in the infra configuration, like, a creation of Mongo related templates only if some properties are available, you might want to use #Conditional. Now, although this is kind of out of scope for this question, I mention this because in conjunction with spring factories this can create a very flexible way to manage your configurations

Is there a way to use Spring #Profile annotation at a package level?

I'm trying to put all my bean definitions for a specific profiles together, and would rather not push them all into one giant AppConfig.java class. I was wondering if there was a way to annotate at a package level using package-info.java and have all configuration files within that package inherit the profile.
I've tried the following in package-info.java:
#Profile("test")
package com.system.configuration.test;
import org.springframework.context.annotation.Profile;
But the #Configuration classes within the package seem to be used whether it is the "test" profile or not.
Is the only choice to annotate each class individually?
You can do it in different way by creating separate #Configuration classes for different profiles:
#Configuration
#Profile("test")
#ComponentScan("com.system.configuration.test")
public class TestProfile {
}
And then on your main configuration class you need to do imports:
#Configuration
#Import(TestProfile.class)
public class MainConfiguration {
}

Resources