ComponentScan.basePackageClasses vs ComponentScan.basePackages to register a single Spring webMVC Controller? - spring

I want to add a single specific controller class to my Spring WebApplicationContext. I ran across the following example: (its in Scala, but is adapted from here: using ComponentScan or context:component-scan with only one class)
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
This works nicely (but is very verbose).
But Spring's #ComponentScan also has basePackageClasses
#Configuration
#ComponentScan( basePackageClasses=Array(classOf[com.example.controllers.MyController]))
class MyConfig {
}
Of basePackageClasses, Spring's docs says:
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components.
However, while the first ComponentScan correctly adds only com.example.controllers.MyController, but the second cause all of my #Controller to be scanned and added! Why? What's the use of basePackageClasses?
Example like: https://github.com/mikaelhg/springmvc-example/blob/master/src/main/java/mikaelhg/example/ExampleConfiguration.java
suggest that basePackageClasses can be used to load a single component.
Update:
As an aside, replacing:
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
with
#Configuration
class MyConfig {
#Bean
var myController = new com.example.controllers.MyController
}
it seems that MyController never gets gets connected to the servlet (the behavior changed to 404-NotFound) -- when added to a WebApplicationContext.

The full javadoc for that attribute reads
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components. The package of each class specified
will be scanned.
Consider creating a special no-op marker class or interface in each
package that serves no purpose other than being referenced by this
attribute.
By type-safe, you can't make any mistakes with the String value of the name of the package. If you specify an incorrect class, it will fail at compile time.
When you specify basePackageClasses, Spring will scan the package (and subpackages) of the classes you specify. This is a nice trick with no-op classes/interfaces like Controllers, Services, etc. Put all your controllers in one package containing the Controllers class and specify your Controllers class in the basePackageClasses. Spring will pick them all up.
You still need to specify the filters.

Related

Should #ComponentScan stay in the class containing the main method?

I know that #ComponentScan with #Configuration tell Spring where to look for beans.
#ComponentScan
#Configuration
public class MyApp{
...
}
What I do not understand is on which class I have to put these two annotations. Should they stay on the class containing the main method?
Like this
#ComponentScan
#Configuration
public class MyApp{
public static void main(String[] args) {
...
}
}
Or they can stay on whatever class of the application?
The question comes from the fact that Spring has to know the location of #ComponentScan... or is there an automatic way of detection of the #ComponentScan annotation which Spring is performing under the hood?
Hope to have explained myself!
You can put it wherever you want (I usually put mine in com.domain.project-name.config) and just specify the directories it should scan, for example if you want it to scan everything in project use
#ComponentScan("com.domain.project-name")
#Configuration
public class Config {
...
By default, ComponentScan scans all the annotated classes at the current directory level and below.
#Configuration annotation tells the Spring container that the class contains Spring bean configuration.
#ComponentScan annotation tells the Spring container that the annotated class to scan/searches for other annotations and components. You can also define package name to scan with the annotation like #ComponentScan("your.package.name") or you can give package/class names that need not be scanned.
Hence, you can put these annotations on any class that defines your bean configuration and could be required by spring container to parse and create objects for your entities/POJOs, services and DAOs.
To conclude, I would like to add #ComponentScan and other annotations are there for automatic detection. Else, you would need to define XMLs (that's what happens under the hood with annotations) for spring to read and perform these actions.
Using simple example. You can place #ComponentScan with #Configuration in any class which main method can scan.
Main class scans MyScan class which then scan for bean class.
package com.boot.spring;
#SpringBootApplication
#ComponentScan(basePackages = "com.boot.scan")
public class BootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(BootApplication.class, args);
System.out.println(ctx.getBean("demoBean"));
}
}
Bean class is in different package
package com.boot.bean;
#Service
public class DemoBean {
}
Now, bean class is discovered through DemoScan class
package com.boot.scan;
#ComponentScan(basePackages = "com.boot.bean")
#Configuration
public class DemoScan {
}

`#ConditionalOnProperty` used with multiple `#PropertySource`

I have some beans with #ConditionalOnProperty, where the property is taken from some #PropertySource. But I have multiple #PropertySources. Only in one of them, given property for condition will be defined. But for my surprise, the #ConditionalOnProperty annotation consults every property source. And since property isn't in every property source, PropertyResolver will flag bean as non-mathing.
What I'd like to have, is interface, with actual implementation and no-op implementation, and control which implementation will be used. I don't want to control it using profile, but profile is based on conditionals so there should be a way. So what is happening for me is, that I'm regardless of setting left out with no implementation. Sure, I can add matchIfMissing, and then I will be left out with both, regardless of setting.
Annotation is:
#ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
and in property file is
feature.enabled=true
What is wrong here? But it really cannot be, that if I'm using #Conditional... I have to use just one property source, right?
UPDATE:
having following beans definitions, I have behavior as described: no bean is registered, because feature.enabled is not defined in application.properties. Adding matchIfMissing=true does not help, because bean with this parameter will be added always, since it's (always) not present in application.properties. Adding #ConditionalOnMissingBean did not help me also. I set feature.enabled to true in another.properties, but that FeatureImpl got vetoed, because there is property source, where feature.enabled isn't true. And surprisingly FeatureNoOp, at that time annotated with #ConditionalOnMissingBean, wasn't registered either. No idea why.
#Service
#Slf4j
#ConditionalOnProperty(name = "feature.enabled", havingValue = "false")
public class FeatureNoOp implements Feature {
vs
#Service
#Slf4j
#ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureImpl implements Feature {
And configuration looks like:
#Configuration
#PropertySource("classpath:application.properties")
public class Config {
//...
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
#PropertySource("classpath:another.properties")
#Profile("war-deployment")
public static class WarDeploymentConfig {
//...
}
}
The true cause was, as M.Deinum correctly suggested (thanks!), in #PropertySource annotations.
I had #PropertySource for application.properties, plus I have some configuration classes like one below, just to bring another property source based on active profile:
#Configuration
#PropertySource("classpath:profile-related-properties.properties")
#Profile("some-profile")
public static class SomeProfileConfig {}
even if this provide some explicit saying what's being used, it apparently spells some issues. I changed the code, so that I do not 'repeatedly' declare usage on application.properties and renamed files/profiles so that another automatically discovered properties pattern can be used: application-{profile_name}.properties. After this change, everything works as expected.

#EnableAutoConfiguration annotation with class prameter is not initializing properties object

i have following #CongfigurationProperties class
//#Component
#ConfigurationProperties
#PropertySource("classpath:typesofcharge.properties")
public class ChargeProperties {
private HashMap<String,String> charge=new HashMap<>();
public HashMap<String,String> getCharge()
{
return this.charge;
}
}
And this is my Configuration file
#SpringBootApplication
#ComponentScan({"com.vehiclemanagement.config,com.vehiclemanagement.client,"
+ "com.vehiclemanagement.controller,"
+ "com.vehiclemanagement.exception,"
+ "com.vehiclemanagement.model,"
+ "com.vehiclemanagement.service"})
#EnableConfigurationProperties(ChargeProperties.class)
public class VehicleManagementConfig {
public static void main(String[] args) {
SpringApplication.run(VehicleManagementConfig.class, args);
}
}
If i use #Component annotation in ChargeProperties and remove ChargeProperties.class annotation in Configuration class the charge HashMap is initialized properly
If i remove #Component and pass ChargeProperties.class as argument like this
#EnableConfigurationProperties(ChargeProperties.class) like how document says the charge HashMap is empty when i run
I am using spring boot 2.0.2 release .But i am following latest docs. Can anyone explain why this are not working as document suggest
content of property file is as follows
UPDATE the content of property files are as shown
#DO NOT MODIFY THIS FILE
charge.peak=Double_rate;
charge.lateNight=duration_based_charge;
charge.earlyMorning=special_offers;
When specifying ChargeProperies.class on the #EnableConfigurationProperties annotation it will be registered as a bean through the EnableConfigurationPropertiesImportSelector class inside #EnableConfigurationProperties.
So in the example, if you have only annotated the ChargeProperties class with #ConfigurationProperties it will create a chargeProperties bean with an empty charge HashMap because it defaulted back to application.properties as the source.
A custom source can be specified by using #PropertySource.
#PropertySource annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
As per documentation above, to use #PropertySource to load the custom source, one has to use the #Configuration annotation.
#Configuration
#PropertySource("classpath:typesofcharge.properties")
Under the hood a #Configuration class is a #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
So to your question. By specifying a custom #PropertySource without #Configuration, spring did not load the properties in the #PropertySource annotation and defaulted back to the application.properties.
If we use #PropertySource we have to use component otherwise the properties will not be read
since we added the #ComponentScan We don't have to mention #EnableConfiguationProperties annotation at all The propety class object can be autowired as Bean

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.

#AutoConfigureAfter not working as desired

I have 3 spring-boot-starter projects
One of the autoconfiguration class has the following code:
#Configuration
#ConditionalOnClass(value = Config.class)
#AutoConfigureAfter(value = {FileGeneratorConfig.class, FileUploaderConfig.class})
public class JobConfig
FileGeneratorConfig and FileUploaderConfig are also autoconfiguration classes.
I was expecting that beans created in FileUploaderConfig will be created first. So test this I had put a break point in the method that creates bean in JobConfig and FileUploaderConfig. But the break point hits JobConfig first which makes me believe that my #AutoConfigureAfter is not working. Is that the right assumption.
Also in FileUploaderConfig i have this:
#Bean
FileUtilContainer fileUtilContainer(FileUtilContainerProperties fileUtilContainerProperties){
return new FileUtilContainer(FileUtil.createDirectory(fileUtilContainerProperties.getArchive()),
FileUtil.createDirectory(fileUtilContainerProperties.getWorking()),
FileUtil.createDirectory(fileUtilContainerProperties.getConfirmation()),
FileUtil.createDirectory(fileUtilContainerProperties.getConfirmationProcessed()),
FileUtil.createDirectory(fileUtilContainerProperties.getError()),
FileUtil.createDirectory(fileUtilContainerProperties.getErrorProcessed()));
}
and FileUtilContainerProperties:
#Component
#ConfigurationProperties(prefix = "batch.letter.directory", ignoreUnknownFields = false)
public class FileUtilContainerProperties
but it is not creating FileUtilContainerProperties bean. Am I missing something here?
AutoConfigureAfter controls the order in which the configuration files are processed and their bean definitions are created. The order in which beans are created from those definitions is a separate concern and depends on, among other things, the dependencies that exist between your beans.

Resources