Spring boot #Value NullPointerException - java

I'm writing a Spring Boot application and am trying to load some values from a properties file using the #Value annotation. However, the variables with this annotation remain null even though I believe they should get a value.
The files are located in src/main/resources/custom.propertes and src/main/java/MyClass.java.
(I have removed parts of the code that I believe are irrelevant from the snippets below)
MyClass.java
#Component
#PropertySource("classpath:custom.properties")
public class MyClass {
#Value("${my.property:default}")
private String myProperty;
public MyClass() {
System.out.println(myProperty); // throws NullPointerException
}
}
custom.properties
my.property=hello, world!
What should I do to ensure I can read the values from my property file?
Thanks!

#value will be invoked after the object is created. Since you are using the property inside the constructor hence it is not available.
You should be using constructor injection anyway. It makes testing your class easier.
public MyClass(#Value("${my.property:default}") String myProperty) {
System.out.println(myProperty); // doesn't throw NullPointerException
}

You are getting this error because you are initializing the class with new keyword. To solve this,
first you need to create the configuration class and under this class you need to create the bean of this class.
When you will call it by using bean then it will work..
My code:
#Component
#PropertySource("db.properties")
public class ConnectionFactory {
#Value("${jdbc.user}")
private String user;
#Value("${jdbc.password}")
private String password;
#Value("${jdbc.url}")
private String url;
Connection connection;
#Bean
public String init(){
return ("the value is: "+user);
}
My Config.class:
#Configuration
#ComponentScan
public class Config {
#Bean
public Testing testing() {
return new Testing();
}
#Bean
public ConnectionFactory connectionFactory() {
return new ConnectionFactory();
}
}
Calling it:
public static void main(String[] args) {
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(Config.class);
ConnectionFactory connectionFactory= context.getBean(ConnectionFactory.class);
System.out.println(connectionFactory.init());
}




Related

How can I start a Main-Class in Spring-Boot?

I want to execute a very simple example which explains the IoC-concept in Spring-Boot.
For that I have create a Bean which gets #Autowired to a main-class, which has a method which does something with the bean.
The bean:
The main:
#Component
public class MyMain {
#Autowired
private MyBean bean1;
public void usingTheBean()
{
bean1.setName("Thats my first bean!");
bean1.setAttribute("And thats just an attribute");
System.out.println(bean1);
}
public static void main(String[] args) {
//MyMain main = new MyMain();
//main.usingTheBean();
}
}
My SpringBootApplication:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
MyMain main = new MyMain();
main.usingTheBean();
}
}
How can I start the Main-class? with out getting the
java.lang.NullPointerException
for the #Autowired Bean "MyBean" in the main?
I know that the reason for the NullPointer-Exception is that I created the Main-class with the "new" keyword.
But the question focuses more on the question "How can I start a main-class with spring-boot"
Usually, you do not want to use the context directly to create a bean yourself. You should just let the context initialize and then just use the autowired beans. Most likely, the way you approach this problem is very different from the Spring-way of achieving it.
You should have a look at the following examples:
using the CommandLineRunner interface (see here) or
using the InitializingBean interface (see here)
Alternatively, you can solve this via configuration:
#Configuration
public class MyConfig {
#Bean
public MyBean myBean() {
MyBean bean = new MyBean();
bean.setName("...");
bean.setAttribute("...");
return bean;
}
}
You can then simply use
#Autowired
MyBean myBean;
to autowire it.
Yet another alternative would be to inject the values from a config file (e.g. application.properties) if this is possible in your case:
#Component
public class MyBean {
#Value("${my.config.value}")
private String name;
#Value("${my.config.attribute}")
private String attribute;
public MyBean(){
}
...
Having the following entries in your application.properties:
my.config.value = Some value content
my.config.attribute = Some attribute content

Spring-Boot multi module project load property-file

I have a Spring-Boot-Application as a multimodule-Project in maven. The structure is as follows:
Parent-Project
|--MainApplication
|--Module1
|--ModuleN
In the MainApplication project there is the main() method class annotated with #SpringBootApplication and so on. This project has, as always, an application.properties file which is loaded automatically. So I can access the values with the #Value annotation
#Value("${myapp.api-key}")
private String apiKey;
Within my Module1 I want to use a properties file as well (called module1.properties), where the modules configuration is stored. This File will only be accessed and used in the module. But I cannot get it loaded. I tried it with #Configuration and #PropertySource but no luck.
#Configuration
#PropertySource(value = "classpath:module1.properties")
public class ConfigClass {
How can I load a properties file with Spring-Boot and access the values easily? Could not find a valid solution.
My Configuration
#Configuration
#PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {
#Value("${moviedb.tmdb.api-key}")
private String apiKey;
public String getApiKey() {
return apiKey;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Calling the Config
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
public TMDbWarper(){
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
I'm getting an NullPointerException in the constructor when I autowire the warper.
For field injection:
Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public. Refer Autowired annotation for complete usage. Use constructor injection in this case like below:
#Component
public class TMDbWarper {
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
#Autowired
public TMDbWarper(final TMDbConfig tmdbConfig){
this.tmdbConfig = tmdbConfig;
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
(or)
Use #PostConstruct to initialise like below:
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
#PostConstruct
public void init() {
// any initialisation method
tmdbConfig.getConfig();
}
Autowiring is performed just after the creation of the object(after calling the constructor via reflection). So NullPointerException is expected in your constructor as tmdbConfig field would be null during invocation of constructor
You may fix this by using the #PostConstruct callback method as shown below:
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
public TMDbWarper() {
}
#PostConstruct
public void init() {
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
public TmdbApi getTmdbApi() {
return this.tmdbApi;
}
}
Rest of your configuration seems correct to me.
Hope this helps.
Here is a Spring Boot multi-module example where you can get properties in different module.
Let's say I have main application module, dataparse-module, datasave-module.
StartApp.java in application module:
#SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
Configuration in dataparse-module. ParseConfig.java:
#Configuration
public class ParseConfig {
#Bean
public XmlParseService xmlParseService() {
return new XmlParseService();
}
}
XmlParseService.java:
#Service
public class XmlParseService {...}
Configuration in datasave-module. SaveConfig.java:
#Configuration
#EnableConfigurationProperties(ServiceProperties.class)
#Import(ParseConfig.class)//get beans from dataparse-module - in this case XmlParseService
public class SaveConfig {
#Bean
public SaveXmlService saveXmlService() {
return new SaveXmlService();
}
}
ServiceProperties.java:
#ConfigurationProperties("datasave")
public class ServiceProperties {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
application.properties in datasave-module in resource/config folder:
datasave.message=Multi-module Maven project!
threads.xml.number=5
file.location.on.disk=D:\temp\registry
Then in datasave-module you can use all your properties either through #Value.
SaveXmlService.java:
#Service
public class SaveXmlService {
#Autowired
XmlParseService xmlParseService;
#Value("${file.location.on.disk: none}")
private String fileLocation;
#Value("${threads.xml.number: 3}")
private int numberOfXmlThreads;
...
}
Or through ServiceProperties:
Service.java:
#Component
public class Service {
#Autowired
ServiceProperties serviceProperties;
public String message() {
return serviceProperties.getMessage();
}
}
I had this situation before, I noticed that the properties file was not copied to the jar.
I made the following to get it working:
In the resources folder, I have created a unique package, then stored my application.properties file inside it. e.g: com/company/project
In the configuration file e.g: TMDBConfig.java I have referenced the full path of my .properties file:
#Configuration
#PropertySource("classpath:/com/company/project/application.properties")
public class AwsConfig
Build and run, it will work like magic.
You could autowire and use the Enviornment bean to read the property
#Configuration
#PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {
#Autowired
private Environment env;
public String getApiKey() {
return env.getRequiredProperty("moviedb.tmdb.api-key");
}
}
This should guarantee that property is read from the context when you invoke the getApiKey() method regardless of when the #Value expression is resolved by PropertySourcesPlaceholderConfigurer.

#Autowired create null object inspite #configuration

I have the following configuration class
#org.springframework.context.annotation.Configuration
public class TemplateConfiguration {
#Bean
public Configuration configuration() {
Configuration configuration = new Configuration(new Version(2, 3, 23));
configuration.setClassForTemplateLoading(TemplateConfiguration.class, "/templates/");
configuration.setDefaultEncoding("UTF-8");
configuration.setLocale(Locale.US);
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
return configuration;
}
}
and I use it at the following #service
#Service
public class FreeMarkerService {
#Autowired
private Configuration configuration;
private static final Logger logger = LoggerFactory.getLogger(FreeMarkerService.class);
public String process() {
try {
Template template = configuration.getTemplate("someName");
....
} catch (IOException | TemplateException e) {
logger.error("Error while processing FreeMarker template: " + e);
throw new RuntimeException(e);
}
}
}
but when I try to call process() like
FreeMarkerService f = new FreeMarkerService()
f.process()
I get a null exception cause the configuration Object is null
I want to create an instance using #Autowired and #Configuration annotations
what am I doing wrong?
You should use the Spring instantiated FreeMarkerService object avoiding use of new keyword for objects like Controllers or Services as possible.
For example,
#Service
public class SampleService {
#Autowired
private FreeMarkerService freeMarkerService;
public String callProcess() {
return freeMarkerService.process();
}
}
More details you can find in many posts like this.
This is a member injection:
#Autowired
private static Configuration configuration;
Which spring does after instantiating the bean from its constructor. So at the time you are making that static method call spring has not injected the value.
This is because you are trying to autowire a static field. This is not possible in Spring. Remove static from your Configuration property and it should work.
#Autowired
private Configuration configuration;
#Autowired
private static Configuration configuration;
Why autowired a static field? this is the reason. static member load as class definition load so it is not getting injected value and getting default value which is null.

SpringApplication not able to instantiate bean

I am new to Spring & WebService and trying a few guides on Spring.io.
I planned to create a basic RESTful WebService which consumes Google Direction API and returns just the status.
Here are the classes:
Resource
#JsonIgnoreProperties(ignoreUnknown=true)
public class Direction {
// getters & setters
public Direction() {
super();
}
private String status;
public String toString() {
return status;
}
}
Controller
#Controller
public class Consumer {
public Consumer() {
super();
}
#Resource
private String url;
#Resource
private RestTemplate client;
#Resource
private String apiKey;
#RequestMapping(value = "/directions", method=RequestMethod.GET)
public #ResponseBody Direction consume(#RequestParam(value="source") String source, #RequestParam(value="destination") String destination) {
return consumeDirections(buildURI(source, destination));
}
// Builds URI
private String buildURI(...) {
...
}
private Direction consumeDirections(final String requestURI) {
return client.getForObject(requestURI, Direction.class);
}
}
Configuration v1
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
}
Springconfig
http://pastebin.com/dsNVBWQq
Spring returns that No qualifying bean of type [java.lang.String] found for dependency.
This happens for all the beans in Consumer.
However, this works Configuration v2
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Resource
private Consumer consumer;
public void execute() {
System.out.println(consumer.consume("x", "z"));
}
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");
context.getBean(Application.class).execute();
}
}
Some observations
#Resouce(Explicitly define bean) doesnt work for v1
SpringApplication is not aware of the Springconfig and fails during bean instantiation
I would like to understand why this issue crops up and how to resolve it?
The reason is very easy, the xml config is not loaded. have a look at Spring-Boot: XML Config
if you don't wanna touch existing xml, you need another #configuration annotated class and #ImportResource to load the xml configuration, just like the document says.
IMO, you don't need apiKey and url in the config, you should annotate them with #value, and define them in a .properties file. There are also default settings of spring boot, you get take advantage of it. like, name the properities application.properities and put it on classpath, spring boot will load it automatically.

Cannot access properties

I got a Spring MVC application. It runs over the Tomcat 7 server.
I want to make a props.properties file, so my app could access properties during Beans initialization process.
So i did the following:
1.Create a context-parameter to my web.xml
<context-param>
<param-name>mainProps</param-name>
<param-value>${catalina.home}/conf/props.properties</param-value>
</context-param>
2. I created a MainCobfig class
#Configuration
#PropertySource("classpath:/racoonsoft/wish/properties/props.properties")
#Import({WebConfig.class })
public class MainConfig {
#Autowired
Environment env;
#Value("${db.host}")
static String dbHost;
#Value("${db.name}")
static String dbName;
#Value("${db.login}")
static String dbLogin;
#Value("${db.password}")
static String dbPassword;
#Value("${ozon.login}")
static String ozonLogin;
#Value("${ozon.password}")
static String ozonPassword;
#Value("${ozon.apiurl}")
static String ozonApiUrl;
#Value("${payture.apihost}")
static String paytureApiHost;
#Value("${payture.merchant}")
static String paytureMerchant;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
#Bean
public OzonProcessor apiProcessor() {
return new OzonProcessor(ozonLogin, ozonPassword, ozonApiUrl);
}
#Bean
public PGSQLDataSource pgsqlDataSource() throws Exception{
PGSQLDataSource result = new PGSQLDataSource(dbHost,dbName,5432,dbLogin,dbPassword,"org.postgresql.Driver","jdbc:postgresql:");
result.loadSettings();
if(FacebookController.dbProc==null)
{
FacebookController.dbProc = result;
}
//FacebookController.dbProc = result;
return result;
}
#Bean
public PaytureProcessor paytureProcessor()
{
PaytureProcessor proc = new PaytureProcessor(paytureApiHost,paytureMerchant);
return proc;
}
}
3 - I created props.properties file and put it into /conf directory
When i start my application it didnt throw the exception (file not found) - so i beleave it sees the properties file. But during bean initialization my fields (dbHost,dbLogin etc.) are still null`s.
How can i put values from properties file to my fields?
Help me please.
The annotated factory method of PropertySourcesPlaceholderConfigurer MUST be a static method:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Spring API Reference manual of #Bean remarks this.
A little bit more detailed explanation:
This is because PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor (BFPP).
BFPP does a post-processing on the bean factory just before other (normal) beans are being instantiated and intialized. So, BFPP's are needed to be created in order to work, before the MainConfig bean is instantiated. Marking this factory method as a static method, we can invoke this method without instantiating MainConfig.
# form user login properties
userName.required = User Name is required
In controller side you declare only userName.required. That's how you declare it.

Categories

Resources