Custom spring property source does not resolve placeholders in #Value - spring

I'm trying to build a Spring 3.1 PropertySource which reads its values from Zookeeper nodes. For connecting to Zookeeper I am using Curator from Netflix.
For that I've built a custom property source which reads the value of a property from Zookeeper and returns it. This works fine when I am resolving the property like this
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
However, when I try to instantiate a bean which has a field with an #Value annotation then this fails:
#Component
public class MyBean {
#Value("${foo}") public String foo;
}
MyBean b = ctx.getBean(MyBean.class); // fails with BeanCreationException
This problem has most likely nothing to do with Zookeeper but with the way I'm registering the property sources and creating the beans.
Any insight is highly appreciated.
Update 1:
I'm creating the app context from an XML file like this:
public class Main {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();
}
}
The class which connects to Zookeeper is a #Component.
#Component
public class Server {
CuratorFramework zkClient;
public void connectToZookeeper() {
zkClient = ... (curator magic) ...
}
public void registerPropertySource() {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
}
#PostConstruct
public void start() {
connectToZookeeper();
registerPropertySource();
MyBean b = ctx.getBean(MyBean.class);
}
}
Update 2
This seems to work when I'm using XML-less configuration, i.e. #Configuration, #ComponentScan and #PropertySource in combination with an AnnotationConfigApplicationContext. Why doesn't it work with a ClassPathXmlApplicationContext?
#Configuration
#ComponentScan("com.goleft")
#PropertySource({"classpath:config.properties","classpath:version.properties"})
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

Answering to your Update 2: This does not work with your original configuration(registering a PropertySource using #PostConstruct) because the PropertySource is being registered very late, by this time your target bean has already been constructed and initialized.
Typically the injection of the placeholders happens via a BeanFactoryPostProcessor which is very early in the Spring lifecycle(beans have not been created at this stage) and if a PropertySource is registered at that stage, then placeholders should be resolved.
The best approach though is to use a ApplicationContextInitializer, get a handle on the applicationContext and to register the propertySource there:
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addFirst(zkPropertySource);
}
}

Related

Intellij Spring: NoSuchBeanDefinitionException: No bean named 'accountDAO' available

This is driving me nuts. I have the following files, it is a very simple setup.
public class MainApp {
public static void main(String[] args) {
//read the spring config java class
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("Config.class");
//System.out.println("Bean names: " + Arrays.toString(context.getBeanNamesForType(AccountDAO.class)));
//get the bean from spring container
AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class);
//call the business method
accountDAO.addAccount();
//close the spring context
context.close();
}
}
Config.java:
#Configuration
#ComponentScan("com.aop")
#EnableAspectJAutoProxy
public class Config {
}
LoggingAspectDemo.java:
#Aspect
#Component
public class LoggingAspectDemo {
//this is where we add all our related advices for the logging
//let's start with an #Before advice
#Before("execution(public void addAccount())")
public void beforeAddAccountAdvice() {
System.out.println("\n=======>>>> Executing #Before advice on method addAccount() <<<<========");
}
}
AccountDAO.java
#Component
public class AccountDAO {
public void addAccount() {
System.out.println(getClass() + ": Doing my Db work: Adding an account");
}
}
Everytime I run the MainApp.java, I get:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'accountDAO' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
All the files are under "com.aop" package so #ComponentScan should be scanning all the components. It looks simple enough but I can't get my hands around the problem, can anyone help me where I am going wrong?
You're invoking the constructor of AnnotationConfigApplicationContext with "Config.class" as String argument, but this constructor is actually for invoking with base packages i.e. the argument must be a package name.
Since you want to use it with the Configuration class, use the constructor which accepts Class instance instead i.e.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

What's the best way to add a new property source in Spring?

I'd like to add a new property source that could be used to read property values in an application. I'd like to do this using Spring. I have a piece of code like this in a #Configuration class:
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
MutablePropertySources sources = new MutablePropertySources();
MyCustomPropertySource propertySource = new MyCustomPropertySource("my custom property source");
sources.addFirst(propertySource);
properties.setPropertySources(sources);
return properties;
}
This seems to work pretty well. However, what it is also doing is overriding other property values (e.g. server.port property in application.properties file used by spring boot) which I don't want to overwrite. So the basic question is what's the best way to add this propertysource but not have it override other properties. Any way to grab the existing propertysources and simply add on to it?
I got this working by adding a custom initiailizer to my spring boot app:
#SpringBootApplication
public class MyApp {
public static void main(String[] args) {
new SpringApplicationBuilder(MyApp.class)
.initializers(new MyContextInitializer()) // <---- here
.run(args);
}
}
Where MyContextInitializer contains: -
public class MyContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
// Create map for properites and add first (important)
Map<String, Object> myProperties = new HashMap<>();
myProperties.put("some-prop", "custom-value");
environment.getPropertySources().addFirst(
new MapPropertySource("my-props", myProperties));
}
}
Note, if your application.yaml contains: -
some-prop: some-value
another-prop: this is ${some-prop} property
Then the initialize method will update the some-prop to custom-value and when the app loads it will have the following values at run-time:
some-prop: custom-value
another-prop: this is custom-value property
Note, if the initialize method did a simple System.setProperty call i.e.
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
System.setProperty("some-prop", "custom-value");
}
... then the another-prop would be equal to this is some-value property which is not what we generally want (and we lose the power of Spring config property resolution).
Try setting IgnoreUnresolvablePlaceholders to TRUE. I had a similar problem which I was able to resolve in this way. In my case, I had another placeholderconfigurer, which was working - but properties in the second one were not being resolved unless I set this property to TRUE.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(Boolean.TRUE);
return propertySourcesPlaceholderConfigurer;
}
Yet another possibility (after lots of experimentation, it's what worked for me) would be to declare your PropertySource inside a ApplicationContextInitializer and then inject that one in your SpringBootServletInitializer:
public class MyPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger logger = LoggerFactory.getLogger(ApplicationPropertyInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
MyPropertySource ps = new MyPropertySource();
applicationContext.getEnvironment().getPropertySources().addFirst(ps);
}
}
public class MyInitializer extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return super.configure(builder.initializers(new MyPropertyInitializer()));
}
}
You can perhaps add your propertySource straight into environment once it is initialized.
EDIT: As this is done AFTER the class is processed, you cannot expect the #Value annotations to pick up anything from this particular PropertySource in the same #Configuration class - or any other that is loaded before.
#Configuration
public class YourPropertyConfigClass{
#Value("${fromCustomSource}")
String prop; // failing - property source not yet existing
#Autowired
ConfigurableEnvironment env;
#PostConstruct
public void init() throws Exception {
env.getPropertySources().addFirst(
new MyCustomPropertySource("my custom property source"));
}
}
#Configuration
#DependsOn("YourPropertyConfigClass")
public class PropertyUser {
#Value("${fromCustomSource}")
String prop; // not failing
}
You could move the #PostConstruct to a separate #Configuration class and mark other classes using those properties #DependOn("YourPropertyConfigClass") (this works - but perhaps there is a better way to force configuration order?).
Anyway - it is only worth it, if MyCustomPropertySource cannot be simply added using #PropertySource("file.properties") annotation - which would solve the problem for simple property files.
If you implement PropertySourceFactory as such:
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
public class CustomPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) {
...
}
}
You can use the following property source:
#PropertySource(name="custom-prop-source", value="", factory=CustomPropertySourceFactory.class)
Kind of hack-ish, but it works.

Spring #Required properties when creating #Bean annotated beans

I'm developing a Spring Boot application and am trying out using Java annotation-based bean creation (using #Configuration and #Bean) rather than the familiar old XML-based bean creation. I'm puzzled though. If I attempt to create a bean in XML but fail to set an #Required property I get a BeanInitializationException when the application context is created. In my trials so far with annotation-based bean creation though this does not seem to be the case.
For example:
public class MyClass {
...
#Required
public void setSomeProp(String val){
}
}
Then in Spring XML:
<bean class="MyClass"/>
This will blow up during application startup (and IntelliJ flags it) because the required property is not set. But the same does not seem to be true of this:
#Configuration
public class MyConfig {
#Bean
public MyClass myClass() {
return new MyClass();
}
}
This application starts up just fine even though the required property is not ever set. I must be missing something here, because this seems like a pretty key feature in Spring.
UPDATE
I did some digging & debugging and it turns out that the bean definition is somehow being flagged to skip checking that #Required fields are set. In the Spring class 'RequiredAnnotationBeanPostProcessor' the boolean method 'shouldSkip()' is returning true for beans created this way. When I used the debugger to force that method to return false bean creation did indeed blow up with the expected exception.
Seeing as I'm making a pretty basic Spring Boot application I'm inclined (as Zergleb suggests) to submit this as a bug.
UPDATE 2
Some further debugging has revealed that even if the field is getting set forcing the check still throws the same exception, as if it hadn't been set. So perhaps dunni is correct and there is no way for this to work with #Bean notation.
As you said I also could not get #Required to run as expected this may be a bug and needs to be reported. I have a few other suggestions that did work for me.
Class annotated with #Configuration
//With the bean set up as usual These all worked
#Bean
public MyClass myClass() {
return new MyClass();
}
When you annotate the class #Component and load using component scanning works as expected.(The component scanning part is important you either need your #Configuration class to either have #ComponentScan or perhaps remove #Configuration and replace with #SpringBootApplication and this will enable scanning for components without needing to wire them up using #Bean configs)
#Component // Added this
public class MyClass {
...
#Required //Failed as expected
public void setSomeProp(String val){
}
}
Use #Autowired(required=true) //Fails with BeanCreationException //No qualifying bean of type [java.lang.String] found for dependency
//No more #Component
public class MyClass {
...
#Autowired(required=true) //Fails
public void setSomeProp(String val){
}
}
#Autowired required=false //Does not crash
public class MyClass {
...
#Autowired(required=false) //Simply never gets called if missing
public void setSomeProp(String val){
}
}
#Value //Does not work if test.property is missing // Could not resolve placeholder 'test.property' in string value "${test.property}
public class MyClass {
#Value("${test.property}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp;
}
}
#Value with default value//Does not crash // When getSomeProp is called it returns "My Default Value"(Unless you have test.property=Anything in your application.properties file then it returns "Anything"
public class MyClass {
#Value("${test.property:My Default Value}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp; //Returns "My Default Value"
}
}
Inside your #Configuration file also fails if it cannot find anything to populate String someProp in the myClass method
#Bean
public MyClass myClass(String someProp) { //Fails being unable to populate this arg
MyClass myObj = new MyClass();
myObj.setSomeProp(someProp);
return ;
}
If course this won't work, since you create the object of MyClass yourself (new MyClass()), thus the annotations are not evaluated. If you create a bean with a #Bean method, the container will only make sure, that all dependencies are there (method parameters) and that the bean scope is adhered to, meaning if it's a singleton bean, only one bean is created per application context. The creation of the bean/object itself is solely the responsibility of the developer.
The equivalent of the xml <bean> tag is annotating the class with #Component, where the bean is created completely by the container, thus the annotations are evaluated.
As it is being said that when you are having your own #Configuration class where you are creating the bean by itself, #Required doesn't apply there.
When you already have a #Component, let Spring Boot do the component scan and at the required setter property you can add #Autowired and it will work fine.
Found this link on web- https://www.boraji.com/spring-required-annotation-example
For example:
I have a Component called Employee having Id and Name.
#Component
public class Employee {
int id;
String name;
public int getId() {
return id;
}
#Autowired
#Required
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have a Configuration class called AppConfig.java
#Configuration
public class AppConfig {
#Bean
public int getId() {
return 1;
}
}
So now we see, that component Employee needs an Id property for binding during startup, so I wrote bean method of type Integer, which will get autowired during runtime. If you do not write a bean of type Integer, it will result a BeanCreationException.
And here is my main class file.
#SpringBootApplication
public class SingletonApplication {
public static void main(String[] args) {
ApplicationContext ctx =
SpringApplication.run(SingletonApplication.class, args);
Employee emp = (Employee)ctx.getBean(Employee.class);
System.out.println(emp.getId());
}
}

Why does Springs #Required not work when configured by annotations

I wonder if there is a way to get #Required working when doing the configuration by annotations. I turned my configuration up-and-down and back again but nothing seems to work for me. I'm using Spring 3.1
My basic configuration looks like this:
#Configuration
public class SpringConfig {
#Bean
public MailSender mailSender() {
MailSender MailSender = new MailSender();
// mailSender.setBean(dlMailSender);
return mailSender;
}
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
// setting som props
return myBean;
}
}
MailSender is here:
#Configurable
public class MailSender {
private MyBean myBean;
#Required
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
I'm testing it with this junit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringConfig.class }, loader = AnnotationConfigContextLoader.class)
public class MailSenderTest {
#Test
public void test_main_beans_exists() {
// when then given
}
}
Thanks for any help
Short answer - this is not even theoretically possible.
When using XML-based, bean definitions with their dependencies are completely managed by application context. Spring is able to check, what is being set and what is not being set.
When using annotation-based configuration, you are setting the dependencies yourself. There is no way how Spring can even know what you are doing with the bean before returning it from the factory method.
If you want to check whether the bean is correctly initialized, use InitializingBean or #PostConstruct and implement self-checking method. Spring is doing this regularly in its own beans.

Resources