Custom AutoConfiguration is not detected by spring application - spring

Hi I created one custom auto configuration library as following:
#Configuration
#EnableConfigurationProperties(RefreshProperties.class)
public class PropertiesRefresherAutoConfiguration {
public static final String AUTO_REFRESH_ENABLED = RefreshProperties.PROPERTIES_AUTO_REFRESH + ".enabled";
#ConditionalOnProperty(
name = PropertiesRefresherAutoConfiguration.AUTO_REFRESH_ENABLED,
havingValue = "true"
)
#Bean
public Refresher getRefresher(){
return new Refresher();
}
}
I have added spring.factories in META-INF with content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bla.PropertiesRefresherAutoConfiguration
What I want to achieve is when I use dependency of this custom library and I set application.yaml properties:
properties:
refresh:
enabled: true
Refresher instance should automatically be created. In Refresher class there is one method that should be called on application start
#PostConstruct
public void startTask() {
//do something
}
In my main application I've added dependency to it and defined properties in application.yaml but Refresher Bean is not created and startTask() method is not called
I have tried to remove #Conditional annotation in order to create instance of Refresh every time, but that didn't help.

Related

Spring managed StatementInspector

How can I create a "Spring managed" StatementInspector to enable certain Springboot functionality like Autowiring classes and referencing spring properties via #Value within the StatementInspector class.
The current method I use to register a StatementInspector via a configuration property (below) does not allow for these Spring functionalities.
spring:
jpa:
properties:
hibernate:
session_factory:
statement_inspector: x.x.SqlStatementInspector
A possible solution:
Configure a HibernatePropertiesCustomizer-bean
#Bean
public HibernatePropertiesCustomizer hibernateCustomizer(StatementInspector statementInspector) {
return (properties) -> properties.put(AvailableSettings.STATEMENT_INSPECTOR, statementInspector);
}
Provide a or more conditional StatementInspector-beans based on a property value.
example: OneStatementInspector is only created when demo.statement_inspector is equal to one
#Component
#ConditionalOnProperty(prefix = "demo", name ="statement_inspector", havingValue = "one" )
public class OneStatementInspector implements StatementInspector {
#Value("${demo.my-value}") String myValue;
#Override
public String inspect(String s) {
// myValue is available here
...
}
}
application.properties
demo.my-value=my autowired value
demo.statement_inspector = one
If the configuration of a StatementInspector is optional ( demo.statement_inspector is not mandatory) there are multiple options:
Make one of the possible StatementInspector the default (match if property is missing) #ConditionalOnProperty(prefix = "demo", name ="statement_inspector", havingValue = "...", matchIfMissing = true )
Make HibernatePropertiesCustomizer-bean optional:
#Bean
#ConditionalOnProperty("demo.statement_inspector")
public HibernatePropertiesCustomizer hibernateCustomizer(StatementInspector statementInspector) {
...
}
Provide a default bean as #dekkard suggests:
#Bean
#ConditionalOnMissingBean(StatementInspector.class)
public StatementInspector emptyInspector() {
return EmptyStatementInspector.INSTANCE;
}
note: No need to set spring.jpa.properties.hibernate.session_factory.statement_inspector

Spring ShedLock without Database

I want to disable ShedLock with a configuration yml property. So i don't have to create the table shedlock, if it is not necessary. Is there a way to disable Shedlock usage?
I tried to remove #Bean lockProvider but then I got this message:
No qualifying bean of type 'net.javacrumbs.shedlock.core.LockProvider
If you are using ShedLock in combination with the shedlock-spring dependency, there probably is a #EnableSchedulerLock somewhere in your application. Move the LockProvider bean setup together with the annotation to a separate #Configuration class and add a #ConditionalOnProperty annotation, for example:
#Configuration
#EnableSchedulerLock
#ConditionalOnProperty(value = "shedlock.enabled", matchIfMissing = true)
public class ShedLockConfig {
#Bean
public LockProvider lockProvider(DataSource dataSource) {
// ...
}
}
And add the property
shedlock:
enabled: true
To your application.yml

#PropertySource does not bind a String propety to enum automatically?

In our integration test using springboot 1.4, we used
#ConfigurationProperties(locations = "classpath:test.yml")
with the locations attribute. This was mapping a string property to enum automatically. But starting from springboot 1.5, the locations attribute is removed.
As a workaround, I'm using #PropertySource but this doesn't support yaml file. So, I'm using a factory class to convert the yaml to java.util.properites. But I'm facing issues, with string property not binding to enum automatically.
Is there any good solution for this?
You can map yaml file to config class
The relative path of application.yml file is /myApplication/src/main/resources/application.yml.
The Spring application takes the first profile as the default profile unless declared otherwise in the Spring application.
YAML FILE
spring:
profiles: test
name: test-YAML
environment: test
servers:
- www.abc.test.com
- www.xyz.test.com
---
spring:
profiles: prod
name: prod-YAML
environment: production
servers:
- www.abc.com
- www.xyz.com
Binding YAML to a Config Class
To load a set of related properties from a properties file, we will create a bean class:
Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class YAMLConfig {
private String name;
private String environment;
private List<String> servers = new ArrayList<>();
// standard getters and setters
}
The annotation used here are:
#Configuration marks the class as a source of bean definitions
#ConfigurationProperties binds and validates the external configurations to a configuration class
#EnableConfigurationProperties this annotation is used to enable #ConfigurationProperties annotated beans in the Spring application
USAGE:
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
 
    #Autowired
    private YAMLConfig myConfig;
 
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApplication.class);
        app.run();
    }
 
    public void run(String... args) throws Exception {
        System.out.println("using environment: " + myConfig.getEnvironment());
        System.out.println("name: " + myConfig.getName());
        System.out.println("servers: " + myConfig.getServers());
    }
}

SpringBoot difference between #Configuration and #configurationProperties

From spring boot documentation, #ConfigurationProperties will
generate your own configuration metadata file from items annotated
with #ConfigurationProperties
I tried use #Configuration and #ConfigurationProperties separately on my configuration class.
#Component
//#Configuration
#ConfigurationProperties
#EnableSpringDataWebSupport
#EnableAsync
public class AppConfig {
...
}
I didn't see any noticable difference.
What's the usage of #ConfigurationProperties or #Configuration?
#Configuration is used to create a class the creates new beans (by annotating its methods with #Bean):
#Configuration
public class CustomConfiguration {
#Bean
public SomeClass someClass() {
return new SomeClass();
}
}
#ConfigurationProperties binds external configuration into the fields of the class which it annotates. It's common to use it with a #Bean method to create a new bean that encapsulates configuration which can be controlled externally.
Here's a real world example of how we've used it. Consider a simple POJO that holds some values related to connecting to ZooKeeper:
public class ZookeeperProperties
{
private String connectUrl;
private int sessionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int connectTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(15);
private int retryMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int maxRetries = Integer.MAX_VALUE;
// getters and setters for the private fields
}
Now we can create a bean of type ZookeeperProperties and automatically populate it using external configuration:
#Configuration
public class ZooKeeperConfiguration {
#ConfigurationProperties(prefix = "zookeeper")
#Bean
public ZookeeperProperties zookeeperProperties() {
// Now the object we create below will have its fields populated
// with any external config that starts with "zookeeper" and
// whose suffix matches a field name in the class.
//
// For example, we can set zookeeper.retryMillis=10000 in our
// config files, environment, etc. to set the corresponding field
return new ZookeeperProperties();
}
}
The benefit of this is that it's less verbose than adding #Value to every field of ZookeeperProperties. Instead, you provide a single annotation on the #Bean method and Spring automatically binds any external configuration it finds with the matching prefix to the fields of that class.
It also lets different users of my class (i.e. anyone who creates a bean type of ZookeeperProperties) use their own prefix to configure the class.
The use case of ConfigurationProperties is for externalizing configuration.
#Configuration
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
#ConfigrationProperties
-- Is added to a class definition or a #Bean method in a #Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
See the screenshot to differentiate #Value from #ConfigurationProperties.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties

Modify the bean created in main application context during Integration test

In my springboot application I am performing Integration tests using the following class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AccountLoadApplication.class,
loader = SpringApplicationContextLoader.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest {
AccountLoadApplication.class is a spring boot main class and the actual application has a bean defined like below:
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), new SFTPRemoteFileService());
}
Also I have a Test Configuration class like below
#Configuration
public class AtddTestConfig {
#Bean
public ResourceLoader mvsFileResourceLoader() {
ResourceLoader recapMvsFileResourceLoader =
new RemoteFileResourceLoader(remoteHostProperties(), new FakeSFTPRemoteFileService());
return recapMvsFileResourceLoader;
}
My Idea is that I want to override the bean created in the main application using the new bean defined in the test Configuration file.
But during integration tests the main application bean is considered instead of the bean defined in the test application context?
Is There any other way to achieve what i am trying to achieve ?
Additional Info:
Here are the beans defined in my Application configuration class
#Bean
public RemoteFileService remoteFileService() {
return new SFTPRemoteFileService();
}
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), remoteFileService());
}
Here are the beans defined in my Test configuration class
#Bean
#Profile("local")
#Primary
public RemoteFileService remoteFileService() {
return new FakeSFTPRemoteFileService();
}
Still the production bean is only created instead of this primary bean.
Use #Profile annotation to enable testing bean only in test context
Use #Primary annotation on testing bean, so that spring would use test bean instead of production one.
Here is my Github repository with working example using this mechanism.
Maybe when you add your test configuration as parameter for #ContextConfiguration it resolves problem, e.g.
#ContextConfiguration(classes = {AccountLoadApplication.class, AtddTestConfig.class},
loader = SpringApplicationContextLoader.class)
Along with the other changes suggested by #luboskrnac, you have to declare #ActiveProfiles; otherwise, your local profile is simply ignored.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("local")
#SpringApplicationConfiguration(AccountLoadApplication.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest { /* ... */ }
Note that the above assumes that your AtddTestConfig class gets picked up via component scanning by your AccountLoadApplication class.

Resources