is that ok to have #Autowire or #Inject in #Configuration class - spring

Spring 5, Java 8
I have multiple configuration files, one of the configuration file has #Autowire dependency. it does not complain on run time and works fine but intellij warns can't find those beans.
wondering if thats ok to have #Autowire or #Inject in configuration class.
why i have it is b/c its my websocket configuration and my handlers need dependencies.

It's OK.
#Configuration indicates that a class declares #Beans which might require dependencies. #Configuration itself is meta-annotated with #Component and "therefore may also take advantage of #Autowired/#Inject like any regular #Component".
I would recommend that you pass dependencies as method parameters rather than inject them into fields. It keeps the configuration class clear and emphasises the required dependencies for each #Bean method.
I prefer
class C {
#Bean
public A a(B b) { new A(b); }
}
to
class C {
private final B b;
#Bean
public A a() { new A(b); }
}

Related

How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application

I have a Configuration Class, which creates the bean for RedissonClient and also Creates the CacheManager. How to create the Unit Test case for this Configuration classes.
Can we write unit test case for #Configuration Class?
If we can, How we need to develop.
I prefer to write the test case in Spock Framework, with Groovy. If not, using Junit or Mockito Framework. How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application
#Configuration
public class CacheConfiguration {
private static final String CONFIG= "Configuration";
#Value("${redis.server.url}")
private String redisUrl;
#Value("${redis.server.password}")
private String password;
#Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress(redisUrl).setPassword(password);
RedissonClient client = Redisson.create(config);
return client;
}
#Bean
public CacheManager redissonCacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
config.put(CONFIG, new CacheConfig(24*60*1000, 12*60*1000));
return new RedissonSpringCacheManager(redissonClient, config);
}
}
I think you should realize that classes annotated with #Configuration are not really java classes, or at least you should not treat them like this. I know it sound controversial, I'll explain...
So historically spring used XML configurations to declare beans.
In spring 2.5 I guess, they've introduced an annotation based method where you put annotations #Component/#Service on classes, put #Autowired wherever you want spring to inject dependencies, then spring starts, scans the classpath, detects the beans and starts the application context with these beans.
Then Spring 3.0 has introduced a Java Configuration way of doing spring related configurations:#Configuration / #Bean in a special class.
So you should view these configuration classes as a "substitution" to the methods I've described before
Now let me ask, do you think you should test and XML bean configuration on its own? Probably not...
Do you think you should test that class has annotation #Component on it and all the necessary dependencies are autowired (with reflection or whatever)? Probably not.
So why you want to test the Java Config classes?
Here is another argument that you might find interesting
I've said that these classes are solely for spring to resolve the beans and it internally runs them. But spring doesn't just "run" them - it creates run-time wrapper for them to overcome some technicalities. Here is an example of one such thing:
Lest assume we have three beans: A,B,C such as B and C depend on A.
class A {}
class B {
private A a;
public B(A a) {this.a = a;}
}
class C {
private A a;
public C(A a) {this.a = a;}
}
All beans are expected to be singletons so we define the configuration like this:
#Configuration
public class MyConfig {
#Bean
public A a() { return new A(); }
#Bean
public B b() { return new B(a()); }
public C c() {return new C(a()); }
}
Note that we call a() in definitions of B and C
Now, let me ask you a question: If its a "regular" java code how two different invocations of method a() (in B's and C's constructor) respectively are supposed to return the same instance of A?
So spring indeed uses a lot of sophisticated code and this is what actually runs in runtime, not your class as is, but the transformed version of it and you never know what are those transformations exactly since its and internal spring thing. But is so what is the point of testing it as it is?
I believe there are more arguments like this, but the point is clear - don't test the Configuration classes on their own.
Instead you can use an integration test that will run the spring container and load all the classes required in the configuration. However, in this case you'll probably want to mock some classes (by using #MockBean for example) so it won't be a 100% accurate test.
In terms of code coverage - IMO you should exclude these classes from coverage altogether
After some research and found that, we can run the embedded redis server and we can check whether we able to connect to redis server by spin up the application. I don't if this is correct or not. But by doing so, it really takes time to complete it, took around 20 seconds. Used following dependency to test this // https://mvnrepository.com/artifact/it.ozimov/embedded-redis
testCompile group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
#SpringBootTest(classes = [TestApp])
class CacheConfigurationSpec extends Specification {
#Shared
RedisServer redisServer;
def setupSpec() {
redisServer = RedisServer.builder()
.port(6379)
.setting("bind 127.0.0.1")
.setting("maxmemory 128M")
.build()
redisServer.start()
}
def cleanupSpec() {
if (redisServer != null) {
redisServer.stop()
}
}
#Autowired
private RedissonClient redissonClient;
def "load all contexts"() {
}
}
#SpringBootApplication
class TestApp {
static void main(String[] args){
SpringApplication.run(TestApp.class, args)
}
}

Spring annotation to initialize a bean during Autowire

Do we have a spring annotation that provides an option to initialize a bean (not a component) if not available through default constructor while autowiring?
If yes, that will be awesome. I am tired of initializing beans in some configuration class using default constructor and it occupies space.
I am doing this currently
#Bean
public Test test() {
return new Test();
}
Expecting:
Sometime like:
#Autowire(initMethodType=constructor)
private Test test:
If no, was there no real need of such annotation or any technical limitation?
You have to use #Bean annotation inside an #Configuration class.
Check the following link
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You could annotate your class with #Component, like this:
#Component
public class Test {
...
}
Whereever you need that class, you could then simply autowire it:
#Autowired
private Test test;
You would just have to make sure, that you use #ComponentScan to pick up that bean. You can find more information on that here.

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.

Spring automagic, #Autowired

#Autowired
Environment env;
#Value("${jdbcConnectionString}")
private String jdbcConnectionString;
The above works automagically in certain classes, however is null in similar classes in same package and with same annotations #Configuration/#Component.
I am trying to find out the proper approach to understand what spring does when while configuring various artifacts.
I am able to run stuff every now and then but any good resource to understand the magic is well appreciated.
PS. I am only interested in java-config based approach now.
Working :
package a.b.c;
#Configuration
public class AppConfig {
#Autowired
Environment env;
package a.b.d;
#Configuration
#EnableBatchProcessing
public class JobConfiguration {
#Autowired
private Environment env;
package a.b.L;
public class BatchJobListener implements Ordered, JobExecutionListener {
#Autowired
public Environment env;
Not working inside
package a.b.u
Tried to annotate classes with #Component/#Configuration
In order to autowire a bean, you need first to defined it in a Context.
#Configuration
public class ConfigOne {
#Bean
public String myBean(){
return "my bean";
}
}
The bean that you want to inject and the bean where the bean will be injected need to be in the same context. You can do it with:
JavaConfigApplicationContext context =
new JavaConfigApplicationContext(ConfigOne.class, ConfigTwo.class);
Or you can use #import to import one configuration class into another.
#Configuration
#Import(ConfigTwo.class)
public class ConfigOne {
UPDATE
What I meant was that probably you're not making the configuration in the ringht way. So all your beans where you're injecting the enviroment are not in the same context.
However, if you have configured well everything, it's possible that some classes are loading before enviroment. In this case you can use EnvironmentAware
#Configuration
#PropertySource("classpath:myProperties.properties")
public class MyConfiguration implements EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
public void myMethod() {
final String myPropertyValue = environment.getProperty("myProperty");
// ...
}
}
In your main class where you are initializing the spring boot application, do you have similar configuration:
#Configuration
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan("a.b") //Note that this scans the components where you have configured spring container backed objects
#PropertySource({
"classpath:someProperty1.properties",
"classpath:someProperty2.properties"
})
public class Main{
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
What this basically does is, it tells spring that this is a configuration class, and indicates this configuration class that it also triggers auto-configuration and component scanning (scan only these specific packages (a.b) and check if there are any annotations present for automatic bean detection, like: #Component, #Service, #Controller, #Repository). On detecting any class which have these stereotypes spring would create object(s) depending on the configuration. On creating these objects one may have autowired some objects or one could refer to some property defined in properties file. The config #PropertySource does this.
The package declaration in #ComponentScan should be the base package.

Overriding Spring #PropertySource by #Import

I have a property test=default in class DefaultConfig, and I'm making them available using #PropertySource annotation.
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
I then want to be able to override to test=override, which is in a different properties file in class OverrideConfig, so I again use #PropertySource.
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {}
I configure a test to prove that it works.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={OverrideConfig.class})
public class TestPropertyOverride {
#Autowired
private Environment env;
#Test
public void propertyIsOverridden() {
assertEquals("override", env.getProperty("test"));
}
}
Except of course it does not.
org.junit.ComparisonFailure: expected:<[override]> but was:<[default]>
Maxing out debug, I can see what's happening:
StandardEnvironment:107 - Adding [class path resource [default.properties]] PropertySource with lowest search precedence
StandardEnvironment:107 - Adding [class path resource [override.properties]] PropertySource with lowest search precedence
It seems backwards. Am I making a simple mistake or misthinking this, or would you expect the properties defined by an #PropertySource in an #Import-ed configuration class to be overridden by properties defined in am #PropertySource in the #Import-ing class?
Here is a solution by Helder Sousa, written as a comment of the JIRA issue created by the OP:
[T]he behaviour available in spring xml (one xml importing another xml) is achievable using nested configurations:
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
#Configuration
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Configuration
#Import(DefaultConfig.class)
static class InnerConfiguration {}
}
With this setup, the properties will be gather in the proper order.
Today with Spring 4 you can use this:
#TestPropertySource(value="classpath:/config/test.properties")
And this can be used to use and eventually override properties for the junit test:
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(value="classpath:/config/test.properties")
I'm currently struggling with a similar case in Spring 3.1 but I'm using a different approach to override properties, because #PropertySource does not support optional property files:
#Configuration
#PropertySource("classpath:default.properties")
public class BaseConfig {
#Inject
private ApplicationContext context;
#PostConstruct
public void init() throws IOException {
Resource runtimeProps = context.getResource("classpath:override.properties");
if (runtimeProps.exists()) {
MutablePropertySources sources = ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources();
sources.addFirst(new ResourcePropertySource(runtimeProps));
}
}
...
It seems that #Import does not cause any specific order of #Configuration instantiation whatsoever besides the order dictated by normal bean dependencies. A way to force such an order is to inject the base #Configuration instance itself as a dependency. Could you try:
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Inject
private DefaultConfig defaultConfig;
...
}
Does this help?
And maybe the new ContextHierarchy annotation could be of help here also but I didn't try this one out so far.
You could enforce the loading order of your properties like this:
#Configuration
#PropertySource(value={"classpath:default.properties","classpath:override.properties"})
public class OverrideConfig {
...
}
I had a similar problem and succeeded in just declaring also the default property in my custom configuration:
#Configuration
#Import(DefaultConfig.class)
#PropertySource({"classpath:default.properties", "classpath:override.properties"})
public class OverrideConfig {}

Resources