How to inject Bean of property within my context Bean? - spring-boot

I'm trying to inject Bean of properties within context Bean.
(Spring-boot 2.7.3 / Java 11)
My application.yml is like below:
spring:
config:
active: dev
---
spring:
config:
activate:
on-profile: dev
keycloak:
username: "local"
password: "local"
---
spring:
config:
activate:
on-profile: stg
keycloak:
username: "stg"
password: "stg"
---
spring:
config:
activate:
on-profile: prod
keycloak:
username: "prod"
password: "prod"
and my KafkaProducerConfig.java code like below:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.stereotype.Component;
import lombok.Getter;
#Getter
#Component
#ConstructorBinding
#ConfigurationProperties("keycloak")
public class KafkaProducerConfig {
private final String username;
private final String password;
public KafkaProducerConfig(String username, String password) {
this.username = username;
this.password = password;
}
}
and finally I failed to inject within another class.
Actually, UserDataProducer class extended in a context bean class which means UserDataProducer class also instanciated by spring IoC Container as I know.
I also tried #DependsOn which doesn't work.
#Slf4j
#DependsOn(value = {"KafkaProducerConfig"})
public class UserDataProducer {
#Autowired
KafkaProducerConfig kafkaProducerConfig;
private final String topicName;
public UserDataProducer() {
log.info("===========================================================");
log.info("Initializing UserDataProducer ...");
System.out.println(kafkaProducerConfig.getPassword());
log.info("===========================================================");
// additional properties for transactional producing
topicName = ProducerConfig.PRODUCER_PROPS.getProperty("default.topic");
}

#Slf4j
#Component
public class UserDataProducer {
// use static initializer block to initialize your static fields
// private static final Producer<String, Object> producer;
// initialzer order : static{} -> instance block {} -> constructor
private final String topicName;
public UserDataProducer(KafkaProducerConfig kafkaProducerConfig) {
log.info("===========================================================");
log.info("Initializing UserDataProducer ...");
System.out.println(kafkaProducerConfig.getPassword());
log.info("===========================================================");
// additional properties for transactional producing
topicName = ProducerConfig.PRODUCER_PROPS.getProperty("default.topic");
}

Related

How to load properties in Spring Boot Library Project with environments?

I have a Spring Boot Security Library project as package/dependency. Now I want to add properties for different environments [local,dev,stage,prod], so I added file security-properties.yml in resources directory as shown below
security:
jwt:
secret: xxxxxxxxxxxxxxxxxxxx
---
spring:
profiles: local
security:
# Proxy
proxy:
authUrl: http://localhost:8080
# JWT
jwt:
expiryMs: 14400000
---
spring:
profiles: dev
security:
# Proxy
proxy:
authUrl: https://example-dev.com
# JWT
jwt:
expiryMs: 43200000
---
spring:
profiles: stage
security:
# Proxy
proxy:
authUrl: https://example-stage.com
# JWT
jwt:
expiryMs: 43200000
---
spring:
profiles: prod
security:
# Proxy
proxy:
authUrl: https://example.com
# JWT
jwt:
expiryMs: 43200000
Now to load the properties I created SecurityProperties.class
#Getter
#Setter
#Configuration
#PropertySource(value = "classpath:security-properties.yml", factory = YamlPropertySourceFactory.class)
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private Jwt jwt = new Jwt();
private Proxy proxy = new Proxy();
#Getter
#Setter
public static class Jwt {
private String tokenHeader = "Authorization";
private String tokenHead = "Bearer ";
private String secret;
private Long expiryMs = 43200000L;
}
#Getter
#Setter
public static class Proxy {
private String authUrl;
}
#Getter
#Setter
public static class IgnoreUrls {
private String[] get = {};
private String[] post = {};
private String[] patch = {};
private String[] delete = {};
}
}
and YamlPropertySourceFactory.class to load yaml file
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(resource.getResource());
yamlFactory.afterPropertiesSet();
Properties properties = yamlFactory.getObject();
assert properties != null;
return new PropertiesPropertySource(Objects.requireNonNull(resource.getResource().getFilename()), properties);
}
}
Now after publishing the artifact for the library project and injecting the dependency in main project, I am unable to load security properties for environments from the library project.
Note: I can add security properties in application-{profile}.yml file in main project , it works
How to add properties in Spring Boot Library projects [dependency] ?

No qualifying bean repository with r2dbc postgres

I recently try to using r2dbc with postgres "0.8.8.RELEASE" (pg r2dbc maven dependecy) and spring boot "2.5.2"
I declared following properties in my application.yml:
spring: jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQL82Dialect
hibernate:
ddl-auto: create
show-sql: false
database-platform: org.hibernate.dialect.PostgreSQLDialect r2dbc:
url: r2dbc:postgresql://localhost:5432/postgres
username: postgres
password: postgres
pool:
enabled: true
initial-size: 00
max-size: 500
max-idle-time: 30m
validation-query: SELECT 1 sql:
init:
schema-locations: classpath:/schema.sql
mode: always
Bellow my Application.java:
#EnableJpaRepositories(basePackages = {"fr.mycompany.common"})
#EntityScan("fr.mycompany")
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
#EnableConfigurationProperties
public class Application {
I precise that the "fr.company.common" is in another subproject with own entities & repositories
My DatabaseConfig class:
#Configuration
#EnableTransactionManagement
#EnableR2dbcRepositories(basePackages = "fr.mycompany.activite.ingester.database.repos")
#Slf4j
public class DatabaseConfig extends AbstractR2dbcConfiguration {
#Value("${spring.r2dbc.host}")
private String host;
#Value("${spring.r2dbc.username}")
private String username;
#Value("${spring.r2dbc.password}")
private String password;
#Value("${spring.r2dbc.database}")
private String database;
#Override
public ConnectionFactory connectionFactory() {
log.info("Init rd2dbc with host: {}", host);
log.info("Init rd2dbc with database: {}", database);
log.info("Init rd2dbc with username: {}", username);
log.info("Init rd2dbc with password: {}", password);
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
.username(username)
.password(password)
.host(host)
.database(database)
.build());
}
#Bean
ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
#Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("data.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
My repository:
public interface OrdreDeTravailPivotV2SplittedRepository extends ReactiveCrudRepository<OrdreDeTravailPivotV2SplittedEntity, OrdreDeTravailPivotV2IdEntity> {
#Query(value = "SELECT * FROM splitted.ordredetravail WHERE idot = :idOt ORDER BY datemajstatut DESC LIMIT 1", nativeQuery = true)
Optional<OrdreDeTravailPivotV2SplittedEntity> findLastByItOt(String idOt);
}
my entity:
#Entity
#Getter
#Setter
#IdClass(OrdreDeTravailIdEntity.class)
#Table(name = "ordredetravail", schema = "splitted")
#TypeDef(
name = "jsonb",
typeClass = JsonBinaryType.class
)
public class OrdreDeTravailSplittedEntity implements ISplittedEntity {
#Id
#Column(name = "idot")
private String idOt;
#Id
#Column(name = "datemajstatut")
private Instant dateMajStatut;
Finally my business class which uses my repository:
#Slf4j
#Component
public class OrdreDeTravailConverter implements IModelConverter<OrdreDeTravailRawEntity, OrdreDeTravailSplittedEntity, OrdreDeTravailComputedEntity, OrdreDeTravailInputConversionModel> {
private final OrdreDeTravailSplittedRepository ordreDeTravailSplittedRepository;
private final OrdreDeTravailComputedRepository ordreDeTravailComputedRepository;
#Autowired
public OrdreDeTravailConverter(OrdreDeTravailSplittedRepository ordreDeTravailSplittedRepository, OrdreDeTravailComputedRepository ordreDeTravailComputedRepository) {
this.ordreDeTravailSplittedRepository = ordreDeTravailSplittedRepository;
this.ordreDeTravailComputedRepository = ordreDeTravailComputedRepository;
}
and when I try to launch the application I get the following error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'fr.mycompany.activite.demande.orion.ingester.database.repos.splitted.OrdreDeTravailPivotV2SplittedRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 85 more
do you have an idea ?
Best regards
Adrien
Maybe because you include multiple implementations of RepositoryFactorySupport. You need to let AnnotationRepositoryConfigurationSource#hasExplicitFilters function return true.In spring boot 2.4.2 you must use #EnableR2dbcRepositories and Must have "includeFilters" or "excludeFilters "
My solution(kotlin):
//Custom annotations
//Used on the Repository interface
#Target(AnnotationTarget.CLASS)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
annotation class R2dbcRepository()
//Use above the configuration class
#EnableR2dbcRepositories(includeFilters = [ComponentScan.Filter(type = FilterType.ANNOTATION, classes = [R2dbcRepository::class])])

Spring Boot - MongoDB integration test database config change

MongoDBConfig
#Configuration
#EnableMongoRepositories(basePackages="......persistence.repositories")
public class MongoDBConfig extends AbstractMongoClientConfiguration {
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private String port;
#Value("${spring.data.mongodb.username}")
private String username;
#Value("${spring.data.mongodb.password}")
private String password;
#Value("${spring.data.mongodb.database}")
private String database;
#Override
public MongoClient mongoClient() {
return MongoClients.create("mongodb://" + host + ":" + port);
}
#Override
protected String getDatabaseName() {
return database;
}
}
application.yml
spring:
data:
mongodb:
host: localhost
port: 27017
username:
password:
database: spring-mongodb-demo
With the above configurations, spring app using mongo database as per defined in yml - spring-mongodb-demo.
One of my integration test beings like this:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class UserRepositoryIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private UserRepository userRepo;
When I run an integration tests it uses the database named - test.
I want the integration tests to use the database - spring-mongodb-demo as per defined in yml file or setting a different database as my preference.
I have tried by making a new profile named application-test.yml and set #ActiveProfiles(profiles = "test") in test class.
I have found lots of thread in this regard, nothing worked.

spring boot can not get configuration from application.yml

My spring boot application can not get configuration parameters from application.yml file. My main class as following:
#SpringBootApplication(scanBasePackages={"com.test"})
public class Main {
#Bean
public Validator validator(){
return new org.springframework.validation.beanvalidation.CustomValidatorBean();
}
public static void main(String[] args) throws IOException {
new SpringApplicationBuilder(Main.class)
.properties("application.yml")
.build()
.run(args);
}
}
My controller class as following:
#RestController
#RequestMapping("/test_traffic")
#Component
public class AnycastTrafficController {
#Autowired
TestService testService;
#GetMapping("/test")
public Object component() {
return testService.getTraffic();
}
}
My Service class as following:
#Service
public class TestService {
#Autowired
TestDao testDao;
public Object getTraffic() {
testDao.getTraff();
}
}
My Dao class as following:
#Component
public class TestDao {
#Autowired
MyDBConfig mydbConfig;
public DB getMyDBConfig () {
DB db = new DB(mydbConfig.id, mydbConfig.key);
return db;
}
}
My Config class as following:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "mydb")
public class MyDBConfig {
public String id;
public String key;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
My application.yml (which located at /src/main/resources)as following:
server:
port: 8003
context-path: /
logging:
level:
ROOT: INFO
org.springframework: INFO
org.springframework.data: INFO
com.alibaba: INFO
file: log/
docserver:
accessKeyId: 1111
accessKeySecret: 2222
---
spring:
profiles: dev
application:
name: test-application
mydb:
id: 1111111
key: 2222222
But when I started the Main class and request the url, it threw exception as following:
the id should not be empty.
that mean my Configuration class didn't get the configure data from yml file, so where I did wrong please. p.s(but the server port 8003 could be found by application). Thanks!
Your application.yml contains an invalid property option.
Instead of
spring:
profiles: dev
you should use
spring:
profiles:
active: dev
After correcting this this, the configuration processor should work properly.

Spring Boot Configuration Not Working

I'm using Spring boot.
I have an application.yml in src/main/resources. I then have a Configuration class that I am trying to get to load the application.yml. However, when I try to use the configuration class in another bean, the values are null. See the ApiHelper.java as to where the values are null.
I'm attempting to run the jar as so:
java -jar build/libs/app.jar
Am I doing something wrong? I've also tried using a properties file instead. When I unzip the jar file the configuration files are in the root.
src/main/resources/application.yml
spring:
profiles.active: default
---
spring:
profiles: default
api:
path: http://some-path
---
spring:
profiles: qa
api:
path: http://some-path2
src/main/java/AppConfig.java
#Configuration
#EnableConfigurationProperties(ApiConfig.class)
public class AppConfig {
#Autowired
private ApiConfig apiConfig;
#ConfigurationProperties(value = "api", exceptionIfInvalid=true)
public static class ApiConfig {
private String path;
public ApiConfig() {
System.out.println("Am I getting called?"); // yes it is
}
public String getPath() {
return path;
}
}
#Bean
public ApiHelper getApiHelper() {
return new ApiHelper();
}
}
src/main/java/ApiHelper.java
public class ApiHelper {
#Autowired
private ApiConfig apiConfig;
#PostConstruct
private void init() {
System.out.println(apiConfig); // prints ApiConfig#168498d6
System.out.println(apiConfig.getPath()); // prints null
}
}
It turns out that you need a setter to make it work:
#ConfigurationProperties(value = "api", exceptionIfInvalid=true)
public static class ApiConfig {
...
public void setPath(String path) {
this.path = path;
}
}

Resources