There are situations in witch you need to access Spring ApplicationContext or any other Spring bean from a static method.
At the beginning I would like to emphasise, that wiring a bean into a static class is strongly discouraged. This article is for those who insist on taking going ahead with it and are aware of what they are doing.
Resources
The problem
We have a static utility class called StaticUtils which looks like below:
import com.bettercoding.spring.config.MyConfig;
import org.springframework.beans.factory.annotation.Autowired;
public final class StaticUtils {
private static MyConfig myConfig;
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
import com.bettercoding.spring.config.MyConfig;
import org.springframework.beans.factory.annotation.Autowired;
public final class StaticUtils {
@Autowired
private static MyConfig myConfig;
private StaticUtils() {
}
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
}
}
import com.bettercoding.spring.config.MyConfig;
import org.springframework.beans.factory.annotation.Autowired;
public final class StaticUtils {
@Autowired
private static MyConfig myConfig;
private StaticUtils() {
}
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
}
}
As you can see there is an @Autowired annotation in the 6th line and we expect that Spring inject a proper bean here. But pay attention to that the wired field is static.
Below is an example MyConfig class used in the utility class. It requires to pass foo property.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Value("${foo}")
private String foo;
public String getFoo() {
return foo;
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Value("${foo}")
private String foo;
public String getFoo() {
return foo;
}
}
Let’s write an application class and to test it. Remember to pass -Dfoo=bar property.
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return new CommandLineRunner() {
public void run(String... args) throws Exception {
System.out.println("Hello World");
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
System.out.println("Hello World");
StaticUtils.printFoo();
}
};
}
}
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
System.out.println("Hello World");
StaticUtils.printFoo();
}
};
}
}
The result is NullPointerException.
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 13:45:47.435 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8077 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 13:45:47.442 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 13:45:47.539 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.495 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 13:45:48.516 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.689 seconds (JVM running for 2.339)
2018-06-02 13:45:48.519 INFO 8077 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-02 13:45:48.524 ERROR 8077 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:781) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at com.bettercoding.spring.MyApp.main(MyApp.java:13) [classes/:na]
Caused by: java.lang.NullPointerException: null
at com.bettercoding.spring.utils.StaticUtils.printFoo(StaticUtils.java:19) ~[classes/:na]
at com.bettercoding.spring.MyApp$1.run(MyApp.java:22) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
... 5 common frames omitted
2018-06-02 13:45:48.525 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.527 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 1
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 13:45:47.435 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8077 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 13:45:47.442 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 13:45:47.539 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.495 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 13:45:48.516 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.689 seconds (JVM running for 2.339)
Hello World
2018-06-02 13:45:48.519 INFO 8077 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-02 13:45:48.524 ERROR 8077 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:781) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at com.bettercoding.spring.MyApp.main(MyApp.java:13) [classes/:na]
Caused by: java.lang.NullPointerException: null
at com.bettercoding.spring.utils.StaticUtils.printFoo(StaticUtils.java:19) ~[classes/:na]
at com.bettercoding.spring.MyApp$1.run(MyApp.java:22) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
... 5 common frames omitted
2018-06-02 13:45:48.525 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.527 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 1
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 13:45:47.435 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8077 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 13:45:47.442 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 13:45:47.539 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.495 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 13:45:48.516 INFO 8077 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.689 seconds (JVM running for 2.339)
Hello World
2018-06-02 13:45:48.519 INFO 8077 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-02 13:45:48.524 ERROR 8077 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:781) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at com.bettercoding.spring.MyApp.main(MyApp.java:13) [classes/:na]
Caused by: java.lang.NullPointerException: null
at com.bettercoding.spring.utils.StaticUtils.printFoo(StaticUtils.java:19) ~[classes/:na]
at com.bettercoding.spring.MyApp$1.run(MyApp.java:22) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
... 5 common frames omitted
2018-06-02 13:45:48.525 INFO 8077 --- [ main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b68b9a4: startup date [Sat Jun 02 13:45:47 CEST 2018]; root of context hierarchy
2018-06-02 13:45:48.527 INFO 8077 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 1
As you can see Spring is not able to inject beans directly to static class and the NullPointerException occurred.
The solution: Use StaticContextInitializer class
The most elegant and quite safe method I’ve known is what I called StaticContextInitializer. The idea is to create component class called StaticContextInitializer, which is responsible to initialize static classes in its @PostConstruct annotated method. Let’s see it in practice.
Step 1 – change utility class
Edit StaticUtils class, remove the @Autowired annotation and add setter method for myConfig field. The class after changes should looks like below.
import com.bettercoding.spring.config.MyConfig;
public final class StaticUtils {
private static MyConfig myConfig;
public static void setMyConfig(MyConfig myConfig) {
StaticUtils.myConfig = myConfig;
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
import com.bettercoding.spring.config.MyConfig;
public final class StaticUtils {
private static MyConfig myConfig;
public static void setMyConfig(MyConfig myConfig) {
StaticUtils.myConfig = myConfig;
}
private StaticUtils() {
}
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
}
}
import com.bettercoding.spring.config.MyConfig;
public final class StaticUtils {
private static MyConfig myConfig;
public static void setMyConfig(MyConfig myConfig) {
StaticUtils.myConfig = myConfig;
}
private StaticUtils() {
}
public static void printFoo() {
System.out.println("Foo:" + myConfig.getFoo());
}
}
Step 2 – create StaticContextInitializer compoment
Create class called StaticContextInitializer with init method annotated by @PostConstruct. Pass myConfig to the StaticUtils class.
import com.bettercoding.spring.config.MyConfig;
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
public class StaticContextInitializer {
private MyConfig myConfig;
private ApplicationContext context;
StaticUtils.setMyConfig(myConfig);
import com.bettercoding.spring.config.MyConfig;
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class StaticContextInitializer {
@Autowired
private MyConfig myConfig;
@Autowired
private ApplicationContext context;
@PostConstruct
public void init() {
StaticUtils.setMyConfig(myConfig);
}
}
import com.bettercoding.spring.config.MyConfig;
import com.bettercoding.spring.utils.StaticUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class StaticContextInitializer {
@Autowired
private MyConfig myConfig;
@Autowired
private ApplicationContext context;
@PostConstruct
public void init() {
StaticUtils.setMyConfig(myConfig);
}
}
Step 3 – test
Run the application once again and you will see the expected result.
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 14:04:21.993 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8556 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 14:04:21.998 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 14:04:22.141 INFO 8556 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.078 INFO 8556 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 14:04:23.093 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.679 seconds (JVM running for 2.253)
2018-06-02 14:04:23.101 INFO 8556 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.106 INFO 8556 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 0
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 14:04:21.993 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8556 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 14:04:21.998 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 14:04:22.141 INFO 8556 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.078 INFO 8556 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 14:04:23.093 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.679 seconds (JVM running for 2.253)
Hello World
Foo:bar
2018-06-02 14:04:23.101 INFO 8556 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.106 INFO 8556 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 0
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2018-06-02 14:04:21.993 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Starting MyApp on bc-vbox with PID 8556 (/home/better-coding/Workspace/static-initializer/out/production/classes started by better-coding in /home/better-coding/Workspace/static-initializer)
2018-06-02 14:04:21.998 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : No active profile set, falling back to default profiles: default
2018-06-02 14:04:22.141 INFO 8556 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.078 INFO 8556 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-02 14:04:23.093 INFO 8556 --- [ main] com.bettercoding.spring.MyApp : Started MyApp in 1.679 seconds (JVM running for 2.253)
Hello World
Foo:bar
2018-06-02 14:04:23.101 INFO 8556 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d36e4cd: startup date [Sat Jun 02 14:04:22 CEST 2018]; root of context hierarchy
2018-06-02 14:04:23.106 INFO 8556 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 0
Resources
That’s all what I’ve prepared for you in this tutorial, if I helped you, please consider sharing this post to help me gain a wider audience.
Thanks and I hope to see you in my next tutorial.