The documentation for the #Profile annotation is not very clear on whether multiple active profile names are AND'd or OR'd together.
I would like a bean to be configured if neither "test" nor "integration-test" profiles are active. I have tried the following but it does not work (appear to OR the conditions).
#Bean
#Profile({"!test", "!integration-test"})
SomeLogicNotForTests someLogicNotForTests() {
return new SomeLogicNotForTests();
}
The only work around I can think of is to remove this annotation and instead #Autowired and Environment into the bean itself and test the environment.getActiveProfiles()
Is there any way to make it AND the conditions? If not could the annotation be extended to do that?
Related
I have such app conf in, jar which will be added to the classpath after startup:
#Configuration
#ComponentScan("com.transportexchangegroup.testConf")
public class AppConf {
}
How to load beans dynamically? I saw solutionsm when it is required to write and add bean definitions, but if we do not know everything about new beans and just want to load them automatically?
Class conf = jarService.loadClass("com.x.testConf.AppConf");
((AnnotationConfigServletWebServerApplicationContext) applicationContext).register(conf);
((AnnotationConfigServletWebServerApplicationContext) applicationContext).refresh();
As I see, refresh() turns off web app started locally from IDE.
Do you know any other solutions or what is wrong? Will this work for spring rest controllers from jar?
I'm not sure what do you mean "dynamically".
In general you can load beans if some condition applies, usually depending on the configuration. So you can do something like this:
application.properties: // or yaml it doesn't matter
feature.enabled=true
#Component
#ConditionalOnProperty(name="feature.enabled", havingValue="true", matchIfMissing="true" / "false") // matchIfMissing depends on whether you want the bean to be loaded if the property is not defined
public MyBean {
}
Some caveats:
If you have many beans that depend on "business" feature in order to avoid placing #ConditionalOnProperty you can do one of the following:
Define your own #Component annotation:
// runtime retention, place on class
#Component
#ConditionalOnProperty(...)
#MyFeatureComponent
... and use it in all the beans that define the feature:
#MyFeatureComponent
public class MyBean
{}
Use Java Configuration instead of annotations:
#Configuration
#ConditionalOnProperty(...)
public class MyFeatureConfiguration {
#Bean
public MyBean myBean(){return new MyBean();}
#Bean
public MyAnotherBean myAnotherBean(){return new MyAnotherBean();}
}
In this case you don't need to place any annotation on MyBean at all.
Spring also has a concept of profiles which is just the same, something that it under the hood implemented with these conditionals.
It allows however to define configuration files per profile, so you might want to read about #Profile annotation as well.
As for the bean definitions - this is way more advanced stuff, in general when spring loads it "recognizes" which bean should be loaded and in which order and for doing that it creates a bean definition before loading the bean. So if you hook into this process you can define your own bean definitions if you want and spring will create beans based on these definitions as well. So basically its a hook that allows altering the bean defitions / create new one during the startup process and hence affect the actual beans that will be loaded into the application context.
I doubt, but if you really need that, read about Bean Factory Post Processors in spring.
We´re working on multi-module project where each module is a cutom spring boot starter that holds several retryable tasks. (Using spring-retry)
In order to ensure that retry annotations are activated only once cross starters, a configuration bean is added to each starter auto configuration submodule:
#EnableRetry
#Configuration
#ConditionalOnMissingBean(RetryConfiguration.class)
public class StarterRetryAutoConfiguation {}
The solution is working as expected.
Question: What's the difference between #ConditionalOnSingleCandidate and #ConditionalOnMissingBean ?
I've read the Spring documentation more then once. However, I didn't get when and where should we use each one of them.
ConditionalOnSingleCandidate:
#Conditional that only matches when a bean of the specified class is
already contained in the BeanFactory and a single candidate can be
determined. The condition will also match if multiple matching bean
instances are already contained in the BeanFactory but a primary
candidate has been defined; essentially, the condition match if
auto-wiring a bean with the defined type will succeed.
The condition can only match the bean definitions that have been
processed by the application context so far and, as such, it is
strongly recommended to use this condition on auto-configuration
classes only. If a candidate bean may be created by another
auto-configuration, make sure that the one using this condition runs
after.
ConditionalOnMissingBean:
#Conditional that only matches when no beans meeting the specified
requirements are already contained in the BeanFactory. None of the
requirements must be met for the condition to match and the
requirements do not have to be met by the same bean.
We can use #ConditionalOnMissingBean if we want to load a bean only if a certain other bean is not in the application context:
#Configuration
class OnMissingBeanModule {
#Bean
#ConditionalOnMissingBean
DataSource dataSource() {
return new InMemoryDataSource();
}
}
In this example, we’re only injecting an in-memory datasource into the application context if there is not already a datasource available. This is very similar to what Spring Boot does internally to provide an in-memory database in a test context.
We can use #ConditionalOnSingleCandidate if we want to load a bean only if a single candidate for the given bean class has been determined.
#Configuration
#ConditionalOnSingleCandidate(DataSource.class)
class OnSingleCandidateModule {
...
}
In this example, the condition matches if there is exactly one primary DataSource bean specified in your application context.
I have multiple Spring Boot Starters, each of which define a DataSource like this:
#Bean
#ConfigurationProperties(prefix = "some.unique.namespace.datasource")
public DataSource someUniqueNamespaceDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public SomeOtherBean someOtherBean() {
return new SomeOtherBean(someUniqueNamespaceDataSource())
}
As you can see, the bean method someUniqueNamespaceDataSource() is being called directly in another bean method, within the same configuration class. However, Spring Boot is intercepting the method, and then performing its own internal injection. This time, it injects with a type of DataSource.
When an application uses one of these starters, it works without issue. However, when it uses multiple starters, I get errors like this:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: someUniqueNamespaceDataSource,someOtherUniqueNamespaceDataSource
I believe this is because Spring Boot is internally injected by type, even though my code injects a qualified bean.
Is there some way that the starter libraries can indicate that the DataSources should not be considered candidates for auto-configuration?
Is there some way that an application depending on more than one of these starter libraries can exclude them from auto-configuration?
Disabling auto-configuration entirely is not really viable. Additionally, manually excluding all current auto-configurations that trigger on existence of a DataSource bean is far too brittle because the addition of dependencies later, especially transitive dependencies, which trigger based on a DataSource bean, will reintroduce the error.
In your #SpringBootApplication or #EnableAutoConfiguration annotations set exclude property to:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
That should do the trick.
I liked annotations used for bean declaration etc. But now we have so many beans with order (#Depends). It is tough to maintain or look at a glance the configuration.
Is there any tool that provides "Effective Spring Config" information based on all your bean annotations?
Answer: you should not be using that many #DependsOn annotations.
From the javadoc:
Used infrequently in cases where a bean
does not explicitly depend on another through properties or
constructor arguments, but rather depends on the side effects of
another bean's initialization.
You can just do this:
#Configuration
public class MyConfig {
#Bean
public MovieClient movieClient(RestOperations rest) {
return new MovieClientImpl(rest);
}
#Bean
public RestOperations restOps() {
return new RestTemplate();
}
}
In this example, the RestOperations bean will be instantiaded before the MovieClient bean, just because the movieClient bean asks for it in the constructor. You don't need any #DependsOn annotion in cases like this one.
Edit: as OP commented, there is still the issue of showing the "Effective Spring Config".
I do not think there is any tool for that, because your dependencies may change at runtime (because of AutoConfiguration, #Conditional annotations, profiles, other?).
If you need to know your "Effective Spring Config" (i.e. "what beans are present at runtime"), you can do this:
ConfigurableApplicationContest context;
context = SpringApplication.run(Application.class, finalArgs);
// Print all the beans:
System.out.println(context.getBeanDefinitionNames());
However, if you meant how can you view and navigate all the configuration, you can organize your beans in different #Configuration files, pick them up using #ComponentScan, and navigate them using the Spring IDE pluguin for Eclipse, like this:
I'm in the process of moving all of my Spring Configurations to Java code. I've run into a problem where I now want to set which profile I am using based on a command line switch or maven profile, etc... I also want to avoid having to place all of the same annotations on each of my test classes. This is not a web application, but rather a functional test suite.
Here is my attempt:
public class CompanyApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
rootContext.getEnvironment().setActiveProfiles(System.getProperty("spring.profile.active", "local"));
rootContext.register(LocalConfiguration.class, SauceLabsConfiguration.class);
}
}
Then I have my tests annotated with the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = CompanyApplicationContextInitializer.class)
However when I attempt to run my tests, my autowired pieces are not being located. Am I on the right track at all? How can I wire in this class to programatically set my ApplicationContext?
The problem with your example above is that you're passing an ApplicationContextInitializer class #ContextConfiguration#classes. The #classes attribute is intended to accept classes marked with Spring's #Configuration annotation.
ApplicationContextInitializer is intended for use primarily in web applications, where it is difficult to get programmatic access to the WebApplicationContext. The "contextInitializerClasses" init-param can be passed to the Spring DispatcherServlet, and Spring will call your ACI implementation at the right time, allowing you to manipulate the application context prior to #refresh().
In your case, it appears you are concerned only with activating profiles for an integration test. So your ACI is unnecessary. Mark your integration test with Spring's #ActiveProfiles annotation to dictate which profiles are active.
Note that if spring.profiles.active has been set as a JVM system property or environment variable, the specified profile(s) will be activated automatically. i.e. there is no need to call System#getProperty as you do in your ACI implementation. One thing to note, however, is that based on the logic in your ACI implementation, it appears you want to fall back to a profile named "local" if spring.profiles.active is note supplied as a system property or environment variable. You may be interested to know that there is a "reserved default profile" named literally "default". This probably has the same semantics you're looking for with your "local" profile. Consider renaming your 'local' profile to 'default'.
Finally, note that there does exist an open improvement request for providing ApplicationContextInitializer support in #ContextConfiguration classes: https://jira.springsource.org/browse/SPR-9011. You might want to put a watch on that. It would, for example, allow you a simple option for programmatically activating 'local' if no other profiles are active.
Try adding the locations of your app context XML to the second annotation:
#ContextConfiguration(locations = {
"classpath:applicationContext.xml"
})