Adding #Conditional to an existing spring annotation in spring boot - spring

I have an application which uses an existing spring annotation (#EnableResourceServer). I want this particular annotation to be enabled only when a particular property value is not false.
To do this, I created a meta-annotation and applied #ConditionalOnProperty on that :
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#ConditionalOnProperty(prefix = "custom.resource", name = "enabled", matchIfMissing = true)
#EnableResourceServer
public #interface EnableCustomResourceSecurity {
}
In my application I'm now using #EnableCustomResourceSecurity like :
#EnableCustomResourceSecurity
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
and it all works fine if the property is missing or true but when I change the property to custom.resource.enabled=false I get the following exception :
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
I tried putting this annotation in a couple of other places and noticed that when the conditional expression fails for this annotation, any annotation after this also stops getting processed.
What would be the correct way to achieve what I'm trying to do?

Your annotation #EnableCustomResourceSecurity has the meta annotation #ConditionalOnProperty. While it may seem as if it enables/disables the #EnableResourceServer annotation, it actually enables/disables your MyApplication bean as a whole. It is as if you would write:
#SpringBootApplication
#ConditionalOnProperty(...)
#EnableResourceServer
public class MyApplication {
To avoid this, simply create an empty SomeConfiguration class and annotate it with your custom annotation:
#Configuration
#EnableCustomResourceSecurity
public class SomeConfiguration {}
Instead of adding it to your MyApplication class.

I would recommend, you don't even need a custom annotation but just an empty configuration as mentioned by Michiel. This configuration, in turn, will also import the #EnableResourceServer annotation.
#Configuration
#EnableResourceServer
#ConditionalOnProperty(prefix = "custom.resource", name = "enabled", matchIfMissing = true)
public class ResourceServerConfig {
public ResourceServerConfig() {
System.out.println("initializing ResourceServerConfig ...");
}
}
If you want to control based on annotation, you can import the same configuration in the custom annotation as below:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(ResourceServerConfig.class)
public #interface EnableCustomResourceSecurity {
}

Related

Must #ComponentScan be placed with #Configuration? (Spring Core)

I read inside many articles that #ComponentScan should be placed with #Configuration on top of a class. Here some references:
we use the #ComponentScan annotation along with #Configuration
annotation to specify the packages that we want to be scanned
(https://www.baeldung.com/spring-component-scanning)
#ComponentScan(basePackages = "com.zetcode") #Configuration public
class Application { ... } (http://zetcode.com/spring/componentscan)
The #ComponentScan annotation is used with the #Configuration
annotation to tell Spring the packages to scan for annotated
components. (https://dzone.com/articles/spring-component-scan)
I was curious to try if without #Configuration an exception would have been thrown. Surprisingly everything works fine even without #Configuration. Here the code:
#ComponentScan
public class AppConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
}
I had just one sample bean which got printed.
#Component
public class Car {
}
This was the output of the main method:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig
car
Why does it work? and why do they tell to use it with configuration? was it an old requirement?
And even more surprisingly appConfig becomes a bean, even if it does not have any particular annotation such as #Configuration or #Component. So does that mean that anything that gets put as argument of new AnnotationConfigApplicationContext() gets turned into a bean no matter what annotation does it has or has not?
I probably miss some core spring behavior which would justify this. Any idea?
You are still using #Configuration together with #ComponentScan implicitly. By pasing the AppConfig.class as param to the context, it considers it configuration. That would explain the bean created for it as well

SpringBoot 2.1 spring.main.allow-bean-definition-overriding=true not working

We are upgrading to SpringBoot 2.1.x and Spring Security 5.1.x. We have our own SecurityConfig that overrides springSecurityFilterChain bean that is found in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration. I have added that property to the applications.property file and set it to true but it is still not allowing overrides.
Our class:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public FilterChainProxy springSecurityFilterChain() throws Exception {
blahblahblah
}
}
And the Spring class:
package org.springframework.security.config.annotation.web.configuration;
#Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
#Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
blahblah
}
}
The error:
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'springSecurityFilterChain' defined in class path resource
Its not clear to me why setting the override property to true is not being picked up. I have also tried to annotate our bean as primary, the #autoconfigureBefore(WebSecurityConfiguration.class) and removing non-wanted bean from registry (but haven't figured out how to do that successfully). Is there something special about the bean I am trying to override that prevents it? Do I need to have the applications.property file loaded earlier somehow?
same issue here. what is the solution for this. I have
#Bean
#Primary
public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
}
But it doesn't seem to be invoked as another bean in third party library exists with same name.

Error while exluding MongoDataAutoConfiguration

I've tried to exclude mongoDB autoconfiguration from a spring-boot project but i keep having that error:
Method mvcConversionService in org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport required a bean named 'mongoTemplate' that could not be found.
Configuration:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {MongoDataAutoConfiguration.class})
public class ChromeDataCoreApplication {
public static void main(String[] args) {
SpringApplication.run(ChromeDataCoreApplication.class, args);
}
}
Any help?
Thanks.
I found that in my case I had an interface that was annotated with #Repository and even though nothing depended on it Spring Boot created it anyway and tried to connect to the Mongo database.
WebMvcConfigurerComposite#addFormatters tries to add HateoasAwareSpringDataWebConfiguration which, in turn, requires the mongoTemplate bean. To fix this I was able to put an annotation on my repository interface:
#ConditionalOnProperty(name = "mongo.enabled", havingValue = "true")
To remove the HateoasAwareSpringWebConfiguration bean from the list of delegates used in addFormatters the following can be added to your #SpringBootApplication:
#SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration.class})
Of course I also included the two classes for Mongo auto configuration:
#SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, SpringDataWebAutoConfiguration.class})
I should note that once the #ConditionalOnProperty annotation was added to the repository interface the SpringDataWebAutoConfiguration.class was no longer required.

How to autowire #ConfigurationProperties into #Configuration?

I have a properties class defined like this:
#Validated
#ConfigurationProperties(prefix = "plugin.httpclient")
public class HttpClientProperties {
...
}
And a configuration class like this:
#Configuration
#EnableScheduling
public class HttpClientConfiguration {
private final HttpClientProperties httpClientProperties;
#Autowired
public HttpClientConfiguration(HttpClientProperties httpClientProperties) {
this.httpClientProperties = httpClientProperties;
}
...
}
When starting my spring boot application, I'm getting
Parameter 0 of constructor in x.y.z.config.HttpClientConfiguration required a bean of type 'x.y.z.config.HttpClientProperties' that could not be found.
Is this not a valid use case, or do I have to declare the dependencies some how?
This is a valid use case, however, your HttpClientProperties are not picked up because they're not scanned by the component scanner. You could annotate your HttpClientProperties with #Component:
#Validated
#Component
#ConfigurationProperties(prefix = "plugin.httpclient")
public class HttpClientProperties {
// ...
}
Another way of doing so (as mentioned by Stephane Nicoll) is by using the #EnableConfigurationProperties() annotation on a Spring configuration class, for example:
#EnableConfigurationProperties(HttpClientProperties.class) // This is the recommended way
#EnableScheduling
public class HttpClientConfiguration {
// ...
}
This is also described in the Spring boot docs.
In Spring Boot 2.2.1+, add the #ConfigurationPropertiesScan annotation to the application. (Note that this was enabled by default in version 2.2.0.) This will allow all classes annotated with #ConfigurationProperties to be picked up without using #EnableConfigurationProperties or #Component.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
#SpringBootApplication
#ConfigurationPropertiesScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also, to generate metadata for the classes annotated with #ConfigurationProperties, which is used by IDEs to provide autocompletion and documentation in application.properties, remember to add the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

Spring3's #Configuration cannot #Inject component-scanned beans

This is my app.xml :
<context:component-scan base-package="destiny.web" />
<context:annotation-config/>
And there is a Dao(interface) , and DaoImpl (annotated with #Repository) inside destiny.web package.
There is another Spring3's destiny.web.AppConfig class :
#Configuration
public class AppConfig
{
#Inject
private Dao daoImpl
public AppConfig()
{
System.out.println("dao = " + daoImpl);
}
}
It prints 'null' , why ?
I am sure all these beans/configuration/repositories are scanned. But it seems #Configuration doesn't know other scanned beans . Did I miss anything ?
I try to solve it by #ImportResource :
#Configuration
#ImportResource("classpath:app.xml")
public class AppConfig
But it seems causing cyclic bean scan and throws this exception :
{main} org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Only one AsyncAnnotationBeanPostProcessor may exist within the context.
Offending resource: class path resource [app.xml]
How to solve it ?
Thanks.
Spring will invoke constructor firstly before inject / autowiring the other component. therefore your dao is null while you print at the constructor, because the dao still not injected yet.
Have a try to create test application for your configapp.
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("stackoverflow.xml");
AppConfig appConfig = context.getBean(AppConfig.class);
appConfig.getConfig("smtp.host");
}
}
have you tried it also with the annotation #Autowired instead of #Inject?

Resources