Spring boot #ConfigurationProperties not working - spring

I'm using #ConfigurationProperties for auto configuration of properties. My code is working in IDE. But when I run the jar in command line, it is not working.
Configuration class:
#Configuration
#ConfigurationProperties(prefix="location")
public class Location {
private String base;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
}
Main class:
#SpringBootApplication
#EnableConfigurationProperties(Location.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.yml:
location:
base: c:\test
If I autowire Location class, I see an instance created. But there is not property set. The code is not entering setBase() method.
The application prints this in the console.
AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject'
annotation found and supported for autowiring

Make sure that application.yml file is in the root of your classpath, usually it's put in the resources folder.
The fact that the setBase() method is not called suggests that your application.yml file is not being found. Spring looks in the root of your classpath for the application.yml file.
The comment from M. Deinum is correct saying that your duplicated annotations will result in 2 spring beans for Location class. However, as you say you managed to autowire the bean without getting an error it suggests that your Location class isn't in a package that is found by spring when it's scanning for beans. If there were 2 beans then you'd get an error when you autowired it. By default spring will scan use the package where the #SpringBootApplication is as the base. It will then look in this package and all sub packages.
If your package structure is like this...
myapp.main
Application.java
myapp.config
Location.java
Then you need to add scanBasePackages="myapp" to the #SpringBootApplication annotation.
Also change your main class and remove the #Enable.. annotations. i.e.:
#SpringBootApplication(scanBasePackages="myapp")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

As nothing worked with yaml, I had to change to property file and use
#PropertySource({"classpath:application.properties"})
for the spring to identify the properties file

Related

SpringBoot does not resolve #Value properties without PropertySource annotation

I have a bunch of #Value annotated fields in a SpringBoot configuration file, with the matching values in the standard application.properties . If I don't annotate the configuration file with #PropertySource("classpath:application.properties") it will just copy the "${prop1}" string into the actual variable.
I tried adding #EnableAutoConfiguration to the #Configuration class (instead of the PropertySource annotation), but all it does is to break when a requested property is not found.
SpringBoot is supposed to resolve the properties automatically from the standard application.properties file, why this behaviour? Using version 2.2.2.RELEASE
Update:
The answers are correct, the reason it was not working was that I was calling these properties in a test. Annotating the test with #SpringBootTest fixes the issue. In fact when the application is running it is #SpringBootApplication that does the magic
As you can read in this article (chapter 5), SpringBoot manage automatically the application.properties file.
I don't know if this is your problem because I've not seen the code, but in Spring Boot the Application class should be annotated with #SpringBootApplication.
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Take a look at this starting example.
You can then inject the value for example in a controller class in this way:
#RestController
public class HelloController {
#Value("${test}")
private String test;
#RequestMapping("/test")
String hello() {
return test;
}
}

How to convert plain java main method program on docker

I wanted to migrate/run old java code to docker using Jenkins.
It is structured to run using normal main method of java (Jar file having main method is executed through some script).
Its making use of spring.xml(applicationContext.xml) files with spring-context-2.5.xsd
Uses properties file for all configurations.
Questions as I am looking for recommendations now on:
Does this project needs to be migrated to spring-boot application for migrating to/creating docker image?
If yes, please have a look at current code block
Do I need to replace properties files by yml files?
Current code of main class can be framed as :
public class SIIRunner {
public static void main(String[] args){
String envStr = null;
if (args != null && args.length > 0) {
envStr = args[0];
}
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SIIExecutor siiExecutor= (SIIExecutor) ctx.getBean("SIIExecutor");
siiExecutor.pollAndOperate();
}
}
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application.class,
args);//init the context
SIIExecutor siiExecutor = (SIIExecutor)
app.getBean(SIIExecutor.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SIIExecutor getBean(){
return new SIIExecutor();
}
}
As long as you are starting with a base #Configuration class to begin with, which it maybe sounds like you are with #SpringBootApplication, you can use the #ImportResource annotation to include an XML configuration file as well.
#SpringBootApplication
#ImportResource("classpath:beanFileName.xml")
public class SpringConfiguration {
//
}
Spring boot ideal concept is avoid xml file. but if you want to keep xml bean, you can just add #ImportResource("classPath:beanFileName.xml")
I would recommend remove the beanFileName.xml file. and, convert this file to spring annotation based bean. So, whatever class has been created as bean. Just write #Service or #Component annotation before class name. for example:
XML based:
<bean ID="id name" class="com.example.MyBean">
Annotation based:
#Service or #Component
class MyBean {
}
And, add #ComponentScan("Give the package name").
This is the best approach. Hope this helps.

SpringBoot excludeFilter not working with multiple configuration classes

I am developing a springboot app that has dependency on another springboot application. I want to include Most beans in the parent springboot app but one.
How can I exclude one springboot bean that the parent package has scanned without touching the ParentApplication class?
Ways I have tried but doesn't work:
1: using exclude filtering in my application class to filter out the particular bean class.
2: I also tried to exclude both the bean class and the parent configuration class.
3: add DisposableBean interface to the bean class I want to exclude and destroy it in run time.
below are my application starter configuration class and parent one.
my MyApplication.class:
package com.myapp;
#ComponentScan(
basePackages = {"com.parent",{my own packages..}},
excludeFilters= {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value= {TheClassToExclude.class}),
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value= {ParentApplication.class})}
)
#SpringBootApplication(exclude=ParentApplication.class)
public class MyApplication{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#PostConstruct
public void init() {
System.out.println("App is initialized.");
}
}
my ParentApplication.class
package com.parent;
#EnableRetry
#EnableScheduling
#SpringBootApplication(exclude = { HibernateJpaAutoConfiguration.class })
#ComponentScan(basePackages = {all the base package including the TheClassToExclude}
#PropertySource({all resources})
public class ParentApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
public void haha() {
System.out.println("configuration class created");
}
The console prints out: "configuration class created", so ParentApplication is initiated by springboot for some reason, so is the Class I want to exclude.
just for reference - I think that since both classes are annotated with #SpringBootApplication, thus are both #Configuration classes and will take part in the automatic Spring component scan - and it is not clear which of this classes will be scanned first in order to "exclude" the other one - unless... you explicitly specify the entry point, and thus, the first SpringBootApplication class to load like here
You can see which classes get instantiated by Spring component scanning and in what order by setting logging.level.org.springframework=DEBUG in application.properties

Spring does not load data beans (#Repository) from dependency [duplicate]

I have a myapp parent pom type maven project with myapp-core and myapp-web modules. myapp-core module is added as dependency to myapp-web.
All the classes in myapp-core module reside in root package com.myapp.core and all classes in myapp-web module reside in root package com.myapp.web
The main Application.java is also in com.myapp.web package. As my core module root package is different I am including common base package "com.myapp" for ComponentScan as follows:
#Configuration
#ComponentScan(basePackages="com.myapp")
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now the surprising thing is if I run this app using Run As -> Spring Boot App it is working fine. But if I run it as Run As -> Java Application it is failing with error saying it can't found beans defined in myapp-core module.
If I move my Application.java to com.myapp package it is working fine.
It should work even if i run it as Java Application also, right?
After enabling debug log level for spring and going through extensive logs I found that scanning for various components like JPA Repositories, JPA Entities etc are depending on the Application.java's package name.
If the JPA Repositories or Entities are not in sub packages of Application.java's package then we need to specify them explicitly as follows:
#Configuration
#ComponentScan(basePackages="com.sivalabs.jcart")
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages="com.sivalabs.jcart")
#EntityScan(basePackages="com.sivalabs.jcart")
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
With the above additional #EnableJpaRepositories, #EntityScan I am able to run it using Run As -> Java Application.
But still not sure how it is working fine when Run As -> Spring Boot App!!
Anyway I think it is better to move my Application.java to com.myapp package rather than fighting with SpringBoot!
I have the same problem. Only adding the #EnableJpaRepositories annotation can solve the issue. I tried to define basePackages in #SpringBootApplication, to no avail.
I think the package of the Application class is fed to the scanning process of JpaRepositories, but other packages defined in #SpringBootApplication are ignored.
It looks like a bug/improvement of Spring Boot.
I had a similar issue with Redis repositories that was fixed in a similar way:
#Configuration
#EnableConfigurationProperties({RedisProperties.class})
#RequiredArgsConstructor
#EnableRedisRepositories(basePackages = {"com.example.another"})
public class RedisConfig {
private final RedisConnectionFactory redisConnectionFactory;
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}

trigger component scan from a application for an included spring boot jar using extra annotations

I need to publish a Spring boot based jar which should be consumed in other Spring/Spring boot based applications.
In my reuse jar I have a class(BusinessConfig) annotated with #Configuration and it gives out two beans. This class is in the base package of the reuse jar.
#Configuration
public class BusinessConfig {
#Bean(name = "BusinessRepoManager")
public BusinessRepoManager businessRepoManager(){
return BusinessRepoManager.getInstance();
}
#Autowired
#Bean(name = "CustomerManager")
#Scope("request")
public CustomerManager customerManager(BusinessRepoManager busrepoManager){
return CustomerManager.getInstance();
}
}
In the second application, I have added the dependency and in the application class I have the statement
#ComponentScan(basePackageClasses = {BusinessConfig.class})
to inform Spring context to look for beans provided in BusinessConfig class as well.
This works well, as I could see the beans getting created.
Is there any possibility to simplify this, should all consuming applications know the class name in which my configuration exists/package name.
I tried creating a custom annotation in the jar project and used that in the consuming application.
#ComponentScan(basePackageClasses = {BusinessConfig.class})
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Component
public #interface EnableDemoBusiness {
}
Then in my consuming application I just added
#EnableDemoBusiness
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Is there any way to get this work ?
Thanks in advance !
You have a couple of options:
Option 1
You can turn your class into "auto-configuration", by creating a META-INF/spring.factories file in your jar with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.your.package.BusinessConfig
Now in applications using your jar if #EnableAutoConfiguration or #SpringBootApplication annotations are used, your configuration will be processed and the beans created.
You might want to annotate your configuration with some #ConditionalXXX annotations if required to give applications that use your jar more control.
Refer to the documentation for more information.
Options 2
You can create a custom #EnableXXX annotation like you attempted.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(com.your.package.BusinessConfig.class)
public #interface EnableDemoBusiness {
}

Resources