In a spring-boot application, is it possible to change the dependency injection by external configuration? - spring-boot

I am building an application to send email but I want to turn off the actual send and just have it output to a log file in non-prod environments. The class that actually sends mail is a Spring injected dependency, so my thought was just to build a no-op implementation to swap out with the actual implementation.
Second requirement, I don't want to create separate builds for different environments. I want to create one and promote it through dev -> test -> prod.
This seems like a common use case for dependency injection but I don't see how to externally configure it. If I want to do it this way do I need to abandon annotation based injection and use xml instead?

You can add #Profile("PRODUCTION") on your production #Bean and #Profile("!PRODUCTION") on your non-production #Bean. Then you can specify the active profile(s) at runtime:
java -jar bootApp.jar --spring.profiles.active=PRODUCTION, ...

Related

Disable distributed tracing for development

We are setting up microservice framework.
We use following stack for distributed tracing.
Spring boot
Kafka
Zipkin
Following is how the configuration is done
In gradle.build (or pom.xml) following starter dependencies added
compile 'org.springframework.cloud:spring-cloud-starter-sleuth'
compile 'org.springframework.cloud:spring-cloud-sleuth-zipkin'
compile 'org.springframework.cloud:spring-cloud-starter-bus-kafka'
Add one AlwaysSampler bean
#Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
If we have kafka running, things work automatically.
But if kafka is not running, server does not start - this is mostly the case for development environment.
If I want to stop this, I have to comment out all the code mentioned here (as we use starter dependency with spring boot, it automatically configures as I understand).
Can we just make some changes in properties (or yaml) files so that I don't need to go and comment out all these code?
Or probably another way to disable this without doing some commenting, etc.
You can add the following setting on your properties key to disable zipkin, source.
spring.zipkin.enabled=false
Better yet, create separate development properties (like application-dev.properties) to avoid changing above setting everytime you want to run in your machine: https://stackoverflow.com/a/34846351/4504053

How to disable Javers for integration tests?

I am using Javers 3.11.2 with Spring Boot 1.5.17. When I am trying to run integration tests on an embedded database I still see that Javers tables are getting created each time.
Is there a way I can disable Javers during these tests so that these tables will not be created each time?
There is the easy way, put:
javers:
sqlSchemaManagementEnabled: false
in your application-test.yml. See https://javers.org/documentation/spring-boot-integration/
Disclaimer: I've never used Javers.
In general, disabling something in "integration tests" means that you don't want to load some beans (of Javers in this case).
This means in turn that you have to exclude them from the list of configurations spring boot works with.
If you're using javers autoconfiguration module, it has to provide in its own "spring.factories" file (can be found inside the jar) a file for autoconfiguration.
Find its java code and see whether it has some "#Conditional on something (property beans, etc.)" If it has than create a profile for integration test that will configure the beans in a way that conditional in javers won't pass and the bean won't be created as a consequence
If it doesn't have a conditional on something like this, you'll have to exclude the whole configuration. Its usually can be done by annotation #SpringBootApplication(exclude=<JaversAutoconfiguration goes here>
This will, however, turn it off also for production usage, which is obviously not something that you want. So for "production" profile, you'll have to import it as a regular configuration (not an autoconfiguration), for integration test profile you won't need this.

Programmatically configure Spring Boot app

what's the easiest way to get the spring boot goodness but not try to autoconfigure all the things? For instance, to only run flyway with our already configured properties (some loaded via Consul + Spring Cloud), I was hoping I could do something like:
#Configuration
#Import({DataSourceAutoConfiguration.class, FlywayAutoConfiguration.class})
public class FlywaySetup {}
and have the main method just call SpringApplication.run(FlywaySetup.class)
The problem with this is it picks up all the Component Scan / crazy long list of other dependencies. Any way to specifically configure the dependencies (but still get the nicities of the framework)
If you run this app, it shouldn't use component scan at all. There's nothing that triggers component scan in spring boot besides #ComponentScan (that's available on #SpringBootApplication).
It would help if you could provide more accurate details rather than "crazy long list of other dependencies.". Running that FlywaySetup should only load those two configuration classes (important: these are not handled as auto-configuration anymore). If you have component scan, there's something else you're not showing.
You can exclude auto-configurations you don't need.
You may want to take a look at this SO answer to explore mechanism how to do that.

Maven 2 replace class implementation depending on profile?

I have MailTransport.java and two classes extending it: LiveMailTransport.java and TestMailTransport.java.
LiveMailTransport will really send emails while TestMailTransprot will only write them to the log for testing purpose.
Somewhere I do new MailTransport(); and I would like to replace every usage of MailTransport in my server-side code either with Live- or with TestMailTransport depending on the profile used for compiling (local, production, etc..).
(Similar to gwts "replace-with" on client side...)
How could I do that with maven?
Thanks!
What you want is a factory which accepts a system property. If the system property isn't set, create an instance of LiveMailTransport. If the property is there, create an instance of TestMailTransport.
Proposed name of property: com.pany.app.enableTestMails
Boolean.getBoolean(String) is your friend.
Now configure the surefire plugin to set the property and you're done.
That sounds like a misuse of Maven, cause this looks more like dependency injection task (guice for example) but there is no relationship with Maven.
If you're using Spring or some other dependency injection framework you could manipulate dependencies injected based on inclusion of corresponding configuration.
But if you want to do it with a plain bare bone Java application you could create multiple factories that will create corresponding instances of yoor MailTransport and place these factories into a different source folders. Then use build-helper-maven-plugin to add correspoinding source folder based on active profiles.

Runtime dependency (e.g. connection pooling) and classpath?

I have a Maven 3 project that uses Hibernate 3. In the Hibernate properties file, there is an entry for hibernate.connection.provider_class with the class corresponding to the C3P0 connection provider (org.hibernate.connection.C3P0ConnectionProvider). Obviously, this class is only used at runtime, so I don't need to add the corresponding dependency in my POM with the compile scope. Now, I want to give the possibility to use any connection pooling framework desired, so I also don't add a runtime dependency to the POM.
What is the best practice?
I thought about adding an entry to the classpath corresponding to the runtime dependency (in this case, hibernate-c3p0) when the application is run (for example, using the command line). But, I don't know if it's possible.
This is almost (maybe exactly) the same problem as with SLF4J. I don't know if Hibernate also uses the facade pattern for connection pooling.
Thanks
Since your code doesn't depend on the connection pooling (neither the main code nor the tests need it), there is no point to mention the dependency anywhere.
If anyone should mention it, then that would be Hibernate because Hibernate offers this feature in its config.
But you can add it to your POM with optional: true to indicate:
I support this feature
If you use it, then I recommend this framework and this version
That will make life slightly more simple for consumers of your project.
But overall, you should not mention features provided/needed by other projects unless they have some impact on your code (like when you offer a more simple way to configure connection pooling for Hibernate).
[EDIT] Your main concern is probably how to configure the project for QA. The technical term for this new movement is "DevOps" - instead of producing a dump WAR which the customer (QA) has to configure painstakingly, configuration is part of the development process just like everything else. What you pass on is a completely configured, ready-to-run setup.
To implement this, create another Maven module called "project-qa" which depends on your project and everything else you need to turn the dead code into a running application (so it will depend on DBCP plus it will contain all the necessary config files).
Maven supports overlayed WARs which will allow you to implement this painlessly.
You can mark your dependency as optional. In this case it will not be packaged into archives. In this case you have to ensure that your container provides required library.
You could use a different profile for each connection provider. In each profile you put the runtime dependency that correspond to the connection provider you want to use and change the hibernate.connection.provider_class property accordingly.
For more details about how to configure dependencies in profiles, see Different dependencies for different build profiles in maven.
To see how to change the value of the hibernate.connection.provider_class property see How can I change a .properties file in maven depending on my profile?

Resources