How to inject List of bean properties by spring SpEL? - java

I have a properties file for defining dynamic endpoints (name, host and port).
So I created a bean named EndPoint with name, host and port as member variables.
How to inject server's host and port dynamically by using Spring #Value and SpEL?
endpoint.properties
names=server1,server2,server3
endpoint.server1.host=192.168.1.101
endpoint.server1.port=10101
endpoint.server2.host=192.168.1.102
endpoint.server2.port=10102
endpoint.server3.host=192.168.1.103
endpoint.server3.port=10103
EndPoint.java
public class EndPoint {
/** name */
private String name;
// hardcode server1 here
// how to inject server's host dynamically by name?
// #Value("${endpoint.#{this.name}.host}")
#Value("${endpoint.server1.host}")
private String host;
#Value("${endpoint.server1.port}")
private int port;
public EndPoint(String name) {
this.name = name;
}
}
EndPointBeanConfig.java
#Configuration
#PropertySource( //
value = { "classpath:conf/endpoint.properties" }, ignoreResourceNotFound = true//
)
public class EndPointBeanConfig {
#Autowired
private Environment env;
#Bean(name = "endPoints")
public List<EndPoint> endPoints() {
final List<EndPoint> endPoints = new ArrayList<EndPoint>();
final String[] names = env.getProperty("names").split(",");
for (final String name : names) {
final EndPoint endPoint = endPoint(name);
endPoints.add(endPoint);
}
return endPoints;
}
#Bean(name = "endPoint")
#Scope("prototype")
public EndPoint endPoint(String name) {
return new EndPoint(name);
}
}

I am just giving suggestion, Go with YAML/Properties for this type of implementation in your project.
This link helpful for your requirement

Related

How to create two beans with the same class by methods?

I want to create Clazz, where I can create two Beans with the same class, but with the different configuration.
public class Clazz {
//same class : Client, inside has the different configuration
//inicilized by methods
#Bean(name="Bean1")
public Client1 (){}
#Bean(name = "Bean2")
public Clien2t (){}
}
Then I want to inject them in other classes
public class ClassForInjectBean1{
#Autowired
#Qualifier("Bean1")
#NotNull
Client client
....
}
public class ClassForInjectBean2{
#Autowired
#Qualifier("Bean2")
#NotNull
Client client
....
}
I have tried this construction in classes ClassForInjectBean1 and ClassForInjectBean2
#Resource(name = "Bean2")
#NotNull
Client client
and
#Autowired
#Qualifier("Bean2")
But spring does not understand
Ошибка :
Parameter 1 of constructor in ClassForInjectBean1 required a single bean, but 2 were found:
- Bean1: defined by method 'Client1' in class path resource...
- Bean2: defined by method ''Client2' in class path resource...
Why I can't do that?
I know that there is this way https://www.baeldung.com/spring-qualifier-annotation, but I don't to create many classes and interfaces.
Try to use #Configuration.
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions and
service requests for those beans at runtime
I provided some example for you.
#Configuration
public class Cfg {
#Bean("client1")
public Client client1() {
return new Client("client1");
}
#Bean("client2")
public Client client2() {
return new Client("client2");
}
}
public class Client {
private String name;
public Client(String name) {
this.name = name;
}
#Override
public String toString() {
return "Client{" +
"name='" + name + '\'' +
'}';
}
}
#Component
public class InjectionTest {
#Component
public class ClassForInjectBean1 {
private final Client client;
public ClassForInjectBean1(#Qualifier("client1") Client client) {
this.client = client;
}
#PostConstruct
public void testInit() {
System.out.println(client.toString());
}
}
#Component
public class ClassForInjectBean2 {
private final Client client;
public ClassForInjectBean2(#Qualifier("client2") Client client) {
this.client = client;
}
#PostConstruct
public void testInit() {
System.out.println(client.toString());
}
}
}
Output would be:
Client{name='client2'}
Client{name='client1'}

Why is bean only created when main method is placed at the beginning of the class?

I have a spring application whose entry point is the following:
#SpringBootApplication
#EnableAutoConfiguration(exclude = { HypermediaAutoConfiguration.class })
public class Application {
#Bean(name = "firstApi")
private static RestApiProxy getFirstApi(#Value("${midtier.host}") String host,
#Value("${midtier.port}") int port) {
String baseURI = "/cesmidtier/api";
return new FirstApi("http", host, port, baseURI, new RequestContextHolder());
}
#Bean(name = "secondApi")
private static RestApiProxy getSecondApi(#Value("${yodlee.host}") String host,
#Value("${yodlee.port}") int port, #Value("${yodlee.baseURI}") String baseURI,
#Value("${proxy}") String proxyURI) {
return new SecondApi("https", host, port, baseURI, proxyURI);
}
#Bean(name = "thirdApi")
private static RestApiProxy getThirdApi(#Value("${amexopen.host}") String host,
#Value("${amexopen.port}") int port, #Value("${proxy}") String proxyURI) {
return new ThirdApi("https", host, port, "/", proxyURI);
}
#Bean
#Profile("Test-Mocked")
public CommonsRequestLoggingFilter logger() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(5120);
return filter;
}
#Bean
#Autowired
#ConditionalOnProperty(name = "security.enabled", havingValue = "true")
public AuthorizeHelper authorizeHelper(final ProfileService profileService, final JWTHandler jwtHandler,
final APIGatewayHandler apiGatewayHandler,
#Value("${security.permittedClientServices}") String[] permittedClientServices) {
System.out.println("######################################### 111111111111 ");
return new DefaultAuthorizeHelper(profileService, jwtHandler, apiGatewayHandler, permittedClientServices);
}
#Bean
#ConditionalOnProperty(name = "security.enabled", havingValue = "false")
public AuthorizeHelper authorizeHelper() {
System.out.println("######################################### 2222222222222 ");
return new NullAuthorizeHelper();
}
public static void main(String[] args) {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
SpringApplication.run(Application.class, args);
}
}
As you can see the class has 2 methods with similar name:
(i) authorizeHelper(...) with arguments
(ii) authorizeHelper() without arguments
When I start the application with security=false, then everything work as expected (the application goes into the second authorizeHelper method, the one without arguments).
Problem:
But, when I start the application with security=true, the application does not create the expected bean (it does NOT go into the first authorizeHelper method, the one with arguments).
Solutions I've found:
1.If I name both methods differently, then this problem disappears.
2.If I put the main method at the beginning of the class (as opposed to the end of the class) then the problem also disappears!
Question Why does the position of the main method seem to play a role in the bean instantiation when both authorizeHelper method have the same name ?

How to initialize log4j2 after Spring bean creation?

I want to get some log4j2 properties from my configuration bean but problem is Spring initializes this beans after log4j2 starts. Is there any way to initialize this bean before log4j2 provider?
<NoSql name="elasticsearchTimeAppender">
//I will set below values from Spring conf. bean
<Mongo cluster="" host="" port="" index="" type="" flushInterval=""/>
</NoSql>
Here is my provider;
#Plugin(name = "Mongo", category = "Core", printObject = true)
public class MongoProvider implements NoSqlProvider<MongoConnection> {
....
host = MyConfiguration.DatabaseName; //I am getting null value here because Spring initializes after provider completed.
}
Here is my configuration bean;
#Component
public class MyConfiguration {
public static String DatabaseName;
#PostConstruct
private void setStaticFields() {
MyConfiguration.DatabaseName= databaseName;
}
#Value("${mydbconf.name}")
private String databaseName;
public String getDatabaseName() {
return DatabaseName;
}
public void setDatabaseName(String DatabaseName) {
MyConfiguration.DatabaseName = DatabaseName;
}
}

How to connect ElasticSearch without creating model

I researched on ElasticSearch and knew how to connect it but I need to create a model using anotation #Document() like this.
#Document(indexName = "resource", type = "person", shards = 1, replicas = 0)
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
}
I create a Repository accordingly:
public interface PersonRepository extends ElasticsearchRepository<Person ,Long> { }
My purpose is that I don't want create model anymore. I want to use Json file instead so my repository should be like this:
public interface PersonRepository extends ElasticsearchRepository<String,Long> { }
My problem is that I don't know the configuration of ElasticSearch in order to create indexName and type without using #Document(). I wonder if my idea is possible. I hope everyone can give me some hints. I really appreciate your supports. Thank you very much in advance.
This is my current configuration:
#Configuration
#PropertySource(value = "classpath:elasticsearch.properties")
#EnableElasticsearchRepositories(basePackages = "spring.demo")
public class ElasticsearchConfiguration {
#Resource
private Environment environment;
#Bean
public Client client() throws UnknownHostException {
String host = environment.getProperty("elasticsearch.host");
int port = Integer.parseInt(environment.getProperty("elasticsearch.port"));
String cluster = environment.getProperty("cluster.name");
Settings settings = Settings.settingsBuilder()
.put("cluster.name", cluster)
.build();
Client client = TransportClient.builder().settings(settings).build()
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
return client;
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(client());
}
}

autowired component is null

I'm developing a web application with spring. I've had no problem autowiring and using database #Service classes. Now I'm trying to read a global property file and provide the values to all classes that need them. The solution I've come up with so far seem to be overly complicated (too many classes - AppConfig, ServerConfig iface, ElasticServerConfig) for such a trivial task but I could live with it if it worked.
my applicationContext.xml contains
<context:component-scan base-package="my.package" />
AppConfig.java:
package my.package.configuration;
#Configuration
#PropertySource("classpath:application.properties")
public class AppConfig {
}
ServerConfig.java:
public interface ServerConfig {
String getUrl();
String getUser();
String getPassword();
}
ElasticSearchConfig.java:
package my.package.configuration;
#Component(value = "elasticServerConfig")
public class ElasticServerConfig implements ServerConfig {
private static final Logger LOGGER = LogManager.getLogger(ElasticServerConfig.class);
private String url;
private String user;
private String password;
#Autowired
public ElasticServerConfig(final Environment env) {
this.url = env.getProperty("elastic_server.url");
this.user = env.getProperty("elastic_server.user");
this.password = env.getProperty("elastic_server.password");
LOGGER.debug("url=" + url + "; user=" + user + "; password=" + password); // this works!
}
#Override
public final String getUrl() {
return url;
}
#Override
public final String getUser() {
return user;
}
#Override
public final String getPassword() {
return password;
}
}
When the web application boots, the ElasticServerConfig constructor prints out the correct url/user/pwd as read from application.properties. However an instance of ElasticServerConfig is not injected into a Search object:
package my.package.util;
public class Search {
#Autowired
#Qualifier("elasticServerConfig")
private ServerConfig elasticServerConfig;
public final List<Foobar> findByPatternAndLocation() {
if (elasticServerConfig == null) {
LOGGER.error("elasticServerConfig is null!");
}
// and i get a NullPointerException further on
// snip
}
}
You have to register the Search class as a Spring Bean and take it from the Spring context when you want to use it. It's important to get the bean from the spring context. If you create an object of that class with new, Spring has no way to know about that class and mange it's dependencies.
You can get get a bean from the Spring context by #Autowire it somewhere or by accessing an instance of the context and use the getBean method:
#Configuration
#PropertySource("classpath:application.properties")
public class AppConfig {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(AppConfig.class, args);
ctx.getBean...
}
}
Either use #Component annotation on the class and make sure that the class is in package thats under my.package
or register it in the configuration class
#Configuration
#PropertySource("classpath:application.properties")
public class AppConfig {
#Bean
public Search search(){
return new Search();
}
}

Categories

Resources