Target.java:
package me;
#Component
public class Target implements Runnable {
#Autowired
private Properties properties;
public Target(){
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
Config.java:
#Configuration
#ComponentScan(basePackages = {"me"})
public class Config {
#Bean(name="properties")
public PropertiesFactoryBean properties() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new FileSystemResource("src/my.properties"));
return bean;
}
public static void main(String[] argv) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Target t = context.getBean(Target.class);
t.run();
}
}
With the above code. Bottom of stack:
Caused by: java.lang.NullPointerException
at me.Target.<init>(Target.java:9)
....
You are doing field injection. You're referencing "to-be-autowired" instance variable inside constructor. In constructor, properties field is null because it has not been autowired yet. You can use constructor injection.
package me;
#Component
public class Target implements Runnable {
private final Properties properties;
#Autowired
public Target(Properties properties){
this.properties = properties;
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
The constructor is called before properties is set. You are calling a method on properties in constructor before spring gets to set it. Use something like PostConstruct:
package me;
#Component
public class Target implements Runnable {
#Autowired
private Properties properties;
public Target(){}
#PostConstruct
public void init() {
properties.getProperty('my.property');
}
public void run() {
//do stuff
}
}
Related
I am trying to write test case for the below class where everytime myConfig instance is coming as null. Is there any way to pass the autowired instance.
public class MyClass {
#Autowired
MyConfig myConfig ;
public Properties getUnAckMessage(String queueName) {
Properties prop=new Properties()
URL url = new URL(StringUtils.join(myConfig.getQueueHost(),
myConfig.getQueueURL(),myConfig.getQueueVm(),queueName));
return prop;
}
public Properties request(String queue) {
return getUnAckMessage(queue);
}
}
public class Main {
public void method() {
MyClass myClass=new MyClass();
myClass.getUnAckMessage("test");
}
}
Test case
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Test
public void testMyClass() {
MyClass propertiesExchangeManager1 = new MyClass ();
propertiesExchangeManager1.request("test");
}
}
You must activate Spring for your test if you want Spring to autowire. For example:
#RunWith(SpringRunner.class)
public class Test {
#Autowired private MyClass myClass
#Test
public void test() {
///...
}
}
If you instantiate the class MyClass by yourself, Spring cannot inject the needed classes. You should modify your test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Autowired
MyClass propertiesExchangeManager1;
#Test
public void testMyClass() {
propertiesExchangeManager1.request("test");
}
}
I have a bean as below,
#Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext context = null;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
}
public static <T> T getBean(String beanName) {
return (T) context.getBean(beanName);
}
public static String getActiveProfile() {
return context.getEnvironment().getActiveProfiles()[0];
}
}
it is used by a plain class,
public class ConfigUtil
{
static{
String env = SpringContextUtil.getActiveProfile();
...
}
public static getVal(String key){...}
}
If the Spring Bean SpringContextUtil is initialized after the plain class ConfigUtil, then it is not OK.
So I want to know how Spring decide which one is initilialized earlier? For example, will the invocation of SpringContextUtil.getActiveProfile() trigger the initialization of the Spring bean? or the Spring Bean is only initialized but havn't been injected applicationContext yet, thus leads to a Null Pointer Exeception?
You just need to run your code after spring has been initialized.
If you're using spring-boot, create new bean that implements ApplicationListener<ApplicationReadyEvent> and run your code inside the onApplicationEvent method.
Example
#Component
public class ApplicationListenerBean implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
ConfigUtil.getVal("A");
}
}
However if you want to make ConfigUtil also as a spring bean you need to get rid of static initialization block and use instead #PostConstruct annotation on your init method.
Example
#Component
#DependsOn("springContextUtil")
public class ConfigUtil {
private String env;
#PostConstruct
private void init() {
env = SpringContextUtil.getActiveProfile();
}
public static void getVal(String key) {
System.out.print("Hello");
}
}
I'll start with a simple example. You have a Spring boot application that runs a CommandLineRunner class on initialization.
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
#Autowired //IntelliJ Warning
private DataSource ds;
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.java
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner();
}
}
Now, like this, this works, everything is OK. However, IntelliJ reports a warning where #Autowired is located (I marked where in the comment)
Spring team recommends: Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.
Now if I follow this, I have a constructor based dependency injection
#Autowired
public MyCommandLineRunner(DataSource ds) { ... }
This also means that I have to edit Application.java as well, since the constructor needs an argument. In Application.java if I try to use the setter injection, I'll get the same warning. If I refactor that as well, I'll end up with some, in my opinion, nasty code.
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private DataSource ds;
#Autowired // Note that this line is practically useless now, since we're getting this value as a parameter from Application.java anyway.
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.java
#SpringBootApplication
public class Application {
private DataSource ds;
#Autowired
public Application(DataSource ds) { this.ds = ds; }
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
The above code yields the same result, but doesn't report any warnings in IntelliJ.
I'm confused, how is the 2nd code better than the first one? Am I following an incorrect logic? Should this be wired differently?
In short, what's the correct way to do this?
note DataSource is just a pure example, this question applies to anything being autowired.
note 2 Just to say that MyCommandLineRunner.java can't have another, empty, constructor, since DataSource needs to be autowired/initialized. It will report an error and will not be compiled.
There are several ways to improve it.
You can remove #Autowired from your MyCommandLineRunner as you are letting a #Bean method construct an instance of it. Inject the DataSource directly into the method as an argument.
Or remove #Autowired and remove the #Bean and slap a #Component annotation on your MyCommandLineRunner to have it detected and remove factory method.
Inline your MyCommandLineRunner inside your #Bean method as a lambda.
No Autowiring in the MyCommandLineRunner
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
And the application class.
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
Usage of #Component
#Component
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
And the application class.
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
Inline CommandLineRunner
#SpringBootApplication
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class)
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return (args) -> (logger.info("DataSource: {}", ds);
}
}
All of these are valid ways of constructing your instances. Which one to use, use the one that you feel comfortable with. There are more options (all variations on the ones mentioned here).
Consider making the field ds final, then you don't need #Autowired. See more about dependency injection http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html#using-boot-spring-beans-and-dependency-injection
To keep the code clean, have you considered using Lombok annotations? #RequiredArgsConstructor(onConstructor = #__(#Autowired))
would generate the constructor with #Autowired annotations. See more here
https://projectlombok.org/features/Constructor.html
Your code could look like this:
#Slf4j
#RequiredArgsConstructor
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
//final fields are included in the constructor generated by Lombok
private final DataSource ds;
#Override
public void run(String... args) throws Exception {
log.info("DataSource: {} ", ds.toString());
}
}
// Application.java
#SpringBootApplication
#RequiredArgsConstructor(onConstructor_={#Autowired}) // from JDK 8
// #RequiredArgsConstructor(onConstructor = #__(#Autowired)) // up to JDK 7
public class Application {
private final Datasource ds;
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
Later edit
Solution without Lombok relies on Spring to inject dependency when the bean is created
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
/**
* dependency ds is injected by Spring
*/
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds){
this.ds = ds;
}
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: "+ ds.toString());
}
}
I have three classes as below.
#Service(value="singleOuter")
public class SingletonBeanOuter {
#Autowired
public SingletonBeanInner sbi;
#Autowired
public PrototypeBean pb;
public SingletonBeanOuter()
{
System.out.println("Singleton OUTER Bean instance creation");
}
}
#Service(value="prot")
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public class PrototypeBean{
public PrototypeBean()
{
System.out.println("Prototype Bean instance creation");
}
#Autowired
public SingletonBeanInner sbi;
}
#Service(value="singleInner")
public class SingletonBeanInner {
public SingletonBeanInner()
{
System.out.println("Singleton Bean INNER instance creation");
}
}
HERE: When i try to access instance of SingletonBeanInner through SingletonBeanOuter->prototype it returns null.
public class TestMain {
public static void main(String args[])
{
ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
SingletonBeanOuter a = context.getBean("singleOuter", SingletonBeanOuter.class);
System.out.println("=================================================================");
System.out.println(a);
System.out.println(a.sbi);
System.out.println(a.pb);
System.out.println(a.pb.sbi); //Returns null
System.out.println("=================================================================");
SingletonBeanOuter b = context.getBean("singleOuter", SingletonBeanOuter.class);
System.out.println(b);
System.out.println(b.sbi);
System.out.println(b.pb);
System.out.println(b.pb.sbi); //Returns null
}
}
My intention here is get the singleton instance of SingletonBeanInner when i access it from PrototypeBean with the TARGET_CLASS proxy setting.
If I have a bean like so:
#Lazy
public class MyBean{
public MyBean(String argument){}
#Bean
#Scope("prototype")
public MyBean myBean(String argument){
return new MyBean(argument);
}
}
Is there a way to get an instance of that bean via Provider, like so:
#Component
public class MyOtherBean{
#Autowired
private javax.inject.Provider<MyBean> myBean;
public void operation(){
MyBean bean = myBean.get(); //I would like to pass argument in when getting the bean
}
}
I was also reading into #Lookup annotation, since it has similar (or the same?) effect, but I am using spring 3.1.1 where this annotation is not implemented yet I believe...
If what I am trying to do here can't be done this way, how would you go about such functionality?
Thanks for your help :)
ApplicationContext gives you this ability.
public class Test {
public static void main(String args[]) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Factory.class);
// calls the #Bean factory method for the myBean bean with the argument provided
ctx.getBean("myBean", "first");
ctx.getBean("myBean", "second");
}
}
#Configuration
class Factory {
#Bean()
#Scope("prototype")
public MyBean myBean(String arg) {
return new MyBean(arg);
}
}
class MyBean {
public MyBean(String arg) {
System.out.println(arg);
}
}