How to create a bean from a fully-qualified class name specified in external configuration? (Spring) - spring

I'm using Spring Boot and have an external configuration file called application.yml.
In this file, suppose I have a property foo that takes a fully-qualified class name as value.
Using Java configuration, what is the typical way to create a bean of the type specified by the foo property?

I suppose that foo is implementing some known interface. Otherwise, it is kind of pointless to create a bean of an unknown Type.
Something like:
#Configuration
public class FooConfiguration {
#Value("foo.class-name") // ref to the key used in you application.yml
private String fooClassName;
#Bean
public FooInterface fooBean(){
FooInterface fooImpl = Class.forName(fooClassName).newInstance(); // concreet implemetation
return fooImpl;
}
}

Related

Autowire a Bean in spring-boot-starter library which is not created yet | TransformerSupplier Kafka Streams

I am creating a spring-boot-starter kafka streams library.
In that I am defining a configuration class which will have a list of storeBuilders.
class CustomConfiguration {
private String A;
private String B;
private Set<StoreBuilder<?>> transformationStoreBuilders;
private Set<StoreBuilder<?>> processorStoreBuilders;
// Constructor and Getter
}
The client which will import this spring-boot-starter library will have to create a bean of the above configuration class.
Now I am creating a custom TransformerSupplier and I want to auto-set the stores by autowiring stores from CustomConfiguration.
I am doing something like this:
public abstract class CustomTransformerSupplier<A, B, C, D> implements TransformerSupplier<A, B, KeyValue<C, D>> {
#Autowired
private CustomConfiguration configuration;
public abstract CustomTransformer<A, B, KeyValue<C, D>> get();
public Set<StoreBuilder<?>> stores() {
return configuration.getTransformationStoreBuilders();
}
}
However the CustomTransformerSupplier bean will be created by the client and we cannot Autowire bean in a non Bean class.
How do I auto set the store builders in the transformer supplier?
Why am I following the approach mentioned is because:
The client can have n store builders
There is going to be a ProcessorSupplier and a TransformerSupplier. They can share common state stores.
I want the client to just create the state stores and not worry about injecting the state stores in the TransformerSupplier and the processorSupplier.
Thanks in advance!
See if #Configurable may answer to your question: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-atconfigurable

How to define a #Qualifier value from config file?

Case:
#Autowired
#Qualifier("${value_from_config_file}")
A a;
config file:
value_from_config_file = a
just like:
#Autowired
#Qualifier("a")
A a;
However I failed with #Qualifier("${value_from_config_file}").
How to implement this kind of requirement?
Or dynamic get a bean from config file indication.
Define a configuration class like "AppConfig". Put "${value_from_config_file}" as "someValue" field in this class with a getter. Then write a public method which returns "someValue" as result. put #Bean on the top of the method with a given name. Then use this bean name as your #Qualifier

how to use org.springframework.format.Formatter.print()

#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Override
public FormattingConversionService mvcConversionService() {
FormattingConversionService f = super.mvcConversionService();
f.addFormatter(new DateFormatter("yyyy-MM-dd"));
return f;
}
}
#RestController
public class TestController {
#GetMapping
public Date test(Date date) {
return date;
}
}
When we access http://localhost:8080?date=2021-09-04, the argument type is converted through the DateFormatter's parse method, which relies on the SpringMVC framework to do the conversion. I wonder if the print method can also be invoked through the framework to return a string.
Do we need to manually invoke the print method, for example
#RestController
public class TestController {
#Resource
private FormattingConversionService conversionService;
#GetMapping
public String test(Date date) {
return conversionService.convert(date, String.class);
}
}
Inside the controller
You could use a class extending java.text.Format like SimpleDateFormatin your controller:
#RestController
public class TestController {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#GetMapping
public String test(Date date) {
return dateFormat.format(date);
}
}
At application level
Use DateTimeFormatterRegistrar to register your formats, like described in this tutorial.
Then you can register this set of formatters at Spring's FormattingConversionService.
Using Jackson
However if you would like to work with JSON or XML you should consider using FasterXML's Jackson. See similar question:
Spring 3.2 Date time format
This is the interface representing the environment in which the current application is running. It models two key aspects of the application environment: profiles and properties. The methods related to property access are exposed via the PropertyResolver superinterface.
A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or via annotations; see the spring-beans 3.1 schema or the #Profile annotation for syntax details. The role of the Environment object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default.
Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.
Beans managed within an ApplicationContext may register to be EnvironmentAware or #Inject the Environment in order to query profile state or resolve properties directly.
In most cases, however, application-level beans should not need to interact with the Environment directly but instead may have to have ${...} property values replaced by a property placeholder configurer such as PropertySourcesPlaceholderConfigurer, which itself is EnvironmentAware and as of Spring 3.1 is registered by default when using context:property-placeholder/.
Configuration of the environment object must be done through the ConfigurableEnvironment interface, returned from all AbstractApplicationContext subclass getEnvironment() methods. See ConfigurableEnvironment Javadoc for usage examples demonstrating manipulation of property sources prior to application context refresh().

spring boot component with string parameters

i have a component that reads a configuration value from application.properties and accepts a string parameter in its constructor as such..
#Component
public class Person
{
#Value("${greeting}")
String greeting;
String name;
public Person(String name)
{
this.name = name;
onGreet( greeting + ", " + name );
}
public void onGreet(String message)
{
}
}
I need to instantiate this component as follows and override its "onGreet" event in the calling code as follows:
Person jack = new Person("jack")
{
public void onGreet(String message)
{
System.out.println( message );
}
};
However I end up getting this..
Parameter 0 of constructor in demo11.Person required a bean of type 'java.lang.String' that could not be found.
My application.properties is as follows:
greeting=hello
What am I missing here? Thank you.
It is literally telling you that the only constructor that you have requires a parameter that Spring knows nothing about.
Add a #Value to that String name in the constructor (right before the parameter) like so public Person(#Value("${name}") String name) if you want Spring to initalize it or remove that constructor
EDIT: some more explanation:
Spring is a dependency injection container. Meaning you define beans and let Spring create and inject them for you. Defining beans can be done in several ways (Java configuration, annotations or xml) here you are using annotation way via #Component.
Now that you have defined your bean (aka component) for Spring it will create it. For it to create it it needs to call a constructor. For that you need to provide it with all information necessary for constructor call - meaning all parameters. If parameters are other classes they need to be defined as beans as well (For example via #Component) if they are simple types like String you need to provide #Value for them.
Lastly if you ever use new ... to define Spring managed beans then the whole Spring magic disappears since Spring doesnt know about this bean instantiation anymore and will not autowire anything into it. For all intenses and purposes Spring is not aware of any objects you create with new.

#Value annotation is only loading default - not using property file

Problem
I think that I havn't understood something properly because my #Value is always loading the default calue.
Java Code
So I have the following:
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
And this is set to false always.
Property file: application-disableQuerySecurityDebug.properties
I have a properties file called application-disableQuerySecurityDebug.properties.
And I have the following entry inside the file:
disableQuerySecurityDebugMne=true
And I run the application with the following profile: disableQuerySecurityDebugMne
I was expecting the value to be set to true, but it is always false.
Update
Based on deadpool's answer, I ended up with the following:
#Profile("disableQuerySecurityDebug") #Data
#Configuration
public class DisableSecurityConfig implements DisableQuerySecurityDebug {
#Value("${disableQuerySecurityDebug:true}")
private boolean securityDisabled;
}
#Profile("!disableQuerySecurityDebug") #Data
#Configuration
public class EnableSecurityConfig implements DisableQuerySecurityDebug{
#Value("${disableQuerySecurityDebug:false}")
private boolean securityDisabled;
}
public interface DisableQuerySecurityDebug{
public boolean isSecurityDisabled();
}
#Value annotation is only used to inject properties values into spring Beans from yml or properties file
This annotation can be used for injecting values into fields in Spring-managed beans and it can be applied at the field or constructor/method parameter level.
If you want to inject values based on profile specific then use #Profile on class
#Profile("disableQuerySecurityDebug")
#Configuration
public class Config {
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
}
You could also specify it on the command line by using the following switch:
java -jar demo.jar --spring.profiles.active=disableQuerySecurityDebug

Resources