0

I am trying to create a Spring boot application with JFrame. I can see my beans in applicationContext but they are not getting autowired. I am unable to find the reason for this issue. Can someone help me with this?

Here is the code:

JavauiApplication - it is showing both userManager and userNameRepository is beans

@SpringBootApplication
public class JavauiApplication implements CommandLineRunner {

    @Autowired
    private ApplicationContext appContext;

    public static void main(String[] args) {
        new SpringApplicationBuilder(JavauiApplication.class).headless(false).run(args);

        java.awt.EventQueue.invokeLater(() -> new InputNameForm().setVisible(true));
    }

    @Override
    public void run(String... args) throws Exception {

        String[] beans = appContext.getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans) {
            System.out.println(bean);
        }

    }
}

InputNameForm.java -> userManager coming null

@Component
public class InputNameForm extends javax.swing.JFrame {

    /**
     * Creates new form InputNameForm
     */
    public InputNameForm() {
        initComponents();
    }

    @Autowired
    UserManager userManager;

    private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
        userManager.setName(firstName.getText(), lastName.getText());
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(InputNameForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new InputNameForm().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextField firstName;
    private javax.swing.JLabel firstNameLabel;
    private javax.swing.JTextField lastName;
    private javax.swing.JLabel lastNameLabel;
    private javax.swing.JButton submitButton;
    // End of variables declaration//GEN-END:variables
}

UserManager.java -> userNameRepository is coming null

@Component
public class UserManager {

  @Autowired
  UserNameRepository userNameRepository;

  public void setName(String firstName, String lastName) {
    userNameRepository.save(new UserName(firstName, lastName));
    System.out.println(userNameRepository.findAllByFirstName(firstName));
  }
}
Carlos Nantes
  • 1,197
  • 1
  • 12
  • 23
Ayush
  • 49
  • 1
  • 7
  • 1
    You create an instance of `InputNameForm` yourself with the new keyword. Spring doesn't know about that instance, and thus doesn't inject anything. – dunni Mar 10 '18 at 14:37
  • 1
    Possible duplicate of [Why is my Spring @Autowired field null?](https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null) – dunni Mar 10 '18 at 14:38
  • @dunni I autowired InputNameForm and now that is also coming null. – Ayush Mar 10 '18 at 16:27
  • It got fixed. I made the instance static to call inside void main. When I replaced it with appContext.getBean(InputNameForm.class), it all worked – Ayush Mar 10 '18 at 16:41

4 Answers4

1

It's a very common problem and it occurs because newcomers don't understand how the IoC container works.

  1. Firstly, BeanDefinitionReader reads metadata about your beans from XML, Annotations(@Component, @Service etc), JavaConfig or Groovy script.
  2. There are several BeanPostProcessor's which is responsible for reading all of these Spring annotation you're writing(@Autowired etc).
  3. BeanFactory creates all BeanPostProcessor's then it creates all of your beans.

What happen if you create your bean with @Autowired dependencies via new operator? Nothing, because it isn't actually a bean. The object you created isn't related to IoC container. You may have the bean already in your ApplicationContext if you marked it with @Component(for example) but the object which was created via new operator wont be processed by Spring(annotations won't work).

Hope this helps.

PS: The lifecycle is simplified.

Artem Malchenko
  • 2,320
  • 1
  • 18
  • 39
0

I had the same problem few days ago. What I undertood was that GUI builders like the one that comes with netbeans will automatically create components using new keyword. This means that those components won't be manage by spring. The code usually loks like this:

private void initComponents() {
    jPanel1 = new javax.swing.JPanel(); //This component will not be managed by spring.
    //...
}

You could use the following class provided here, to make it work.

@Component
public class BeanProvider {
    private static ApplicationContext applicationContext;

    // Autowires the specified object in the spring context
    public static void autowire(Object object) {
        applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
    }

    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        BeanProvider.applicationContext = applicationContext;
    }
}

The top level SwingApp class:

@SpringBootApplication
public class SwingApp implements CommandLineRunner {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SwingApp.class)
                .headless(false).bannerMode(Banner.Mode.OFF).run(args);
    }

    @Override
    public void run(String... args) throws Exception {
        SwingUtilities.invokeLater(() -> {
            MainFrame frame = new MainFrame();
            frame.setVisible(true);
        });
    }
}

The MainFrame class:

public class MainFrame extends javax.swing.JFrame {
    public MainFrame() {
        initComponents();
    }
    private void initComponents() {
        //Gui Builder generated code. Bean not managed by spring. 
        //Thus, autowired inside CustomPanel won't work if you rely on ComponentScan. 
        jPanel1 = new CustomJPanel();         
        //...
    }
    private CustomJPanel jPanel1;
}

The panel class where you want to autowire things:

//@Component //not needed since it wont work with gui generated code.
public class CustomJPanel extends javax.swing.JPanel{
    @Autowired
    private SomeRepository someRepository
    public CustomJPanel(){
        BeanProvider.autowire(this); //use someRepository somewhere after this line.
    }
}
Carlos Nantes
  • 1,197
  • 1
  • 12
  • 23
0

I have the same problem in a JavaFx project. Service and Component annotated classes were null in UI controllers even if it was shown in context that it was created. Below code worked for me

@Component
public class FxmlLoaderWithContext {
private final ApplicationContext context;

@Autowired
public FxmlLoaderWithContext(ApplicationContext context) {
    this.context = context;
    FXMLLoader fxmlloader = new FXMLLoader();
    fxmlloader.setControllerFactory(context::getBean); //this row ensure services and components to be autowired
}

}

Ömer
  • 84
  • 8
0

I think it returns null because you using command new to create object, such as new InputNameForm(). When creating object like that, the object isn't managed by Spring. That's why autowired not working. The solution is registering your class as a bean. You can use a class like in here.

@Component
public class BeanProvider {
private static ApplicationContext applicationContext;
    
    public static void autowire(Object object) {
        applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
    }
    
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        BeanProvider.applicationContext = applicationContext;
    }
}

And then, in your class InputNameForm constructor, call this:

class InputNameForm() {
BeanProvider.autowire(this);
...
}

And that's it. Spring will take care the rest.

Vanpn
  • 1