15

all I want to do is autowire the field backgroundGray in the NotesPanel class, but all I get is the exception below.

So, question is, how to autowire it correctly ? It really drives me crazy because it's probably something very stupid I'm doing wrong...

thanks for any help! Thorsten

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'notepad' defined in class path resource [Beans.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [notepad.Notepad]: Constructor threw exception; nested exception is java.lang.NullPointerException
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [notepad.Notepad]: Constructor threw exception; nested exception is java.lang.NullPointerException
Caused by: java.lang.NullPointerException
    at notepad.NotesPanel.<init>(NotesPanel.java:23)
    at notepad.Notepad.<init>(Notepad.java:18)

Class Notepad:

package notepad;

import java.awt.BorderLayout;
import java.awt.Dimension;

import javax.swing.JFrame;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Notepad
{

  public Notepad()
  {
    JFrame frame = new JFrame();
    frame.setLayout(new BorderLayout());
    frame.add(new NotesPanel(), BorderLayout.CENTER);

    frame.setPreferredSize(new Dimension(1024, 768));
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
  }

  public static void main(String[] args)
  {

    ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
    context.getBean("notepad");

  }
}

Class Notespanel:

package notepad;

import java.awt.BorderLayout;

import javax.swing.JPanel;
import javax.swing.JTextPane;

import org.springframework.beans.factory.annotation.Autowired;

public class NotesPanel
    extends JPanel
{
  JTextPane tPane = new JTextPane();

  @Autowired
  private BackgroundGray backgroundgray;

  public NotesPanel()
  {
//    backgroundgray = new BackgroundGray();
//    backgroundgray.setGray("200");
    setLayout(new BorderLayout());
    tPane.setBackground(backgroundgray.getGrayObject());
    add(tPane, BorderLayout.CENTER);
    tPane.setText("Fill me with notes... ");
  }

}

Class BackgroundGray:

package notepad;

import java.awt.Color;

public class BackgroundGray
{
  String gray;

  public BackgroundGray()
  {
    System.out.println("Background Gray Constructor.");
  }

  public String getGray()
  {
    return gray;
  }

  public void setGray(String gray)
  {
    this.gray = gray;
  }

  public Color getGrayObject()
  {
    int val = Integer.parseInt(gray);
    return new Color(val, val, val);
  }

}

Beans.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <context:annotation-config />

    <bean id="notepad" class="notepad.Notepad"/>

    <bean id="backgroundgray" class="notepad.BackgroundGray" autowire="byName">
        <property name="gray" value="120"></property>
    </bean>
</beans>
user1119859
  • 669
  • 2
  • 9
  • 20
  • In order for Spring to be able to perform autowiring, your instance must be managed by Spring. You can autowire your NotesPanel as well. – M Rajoy Aug 13 '14 at 10:41
  • 1
    `new` defeats the concepts of `DI` and `IoC`, use the bean factory to get bean instances and every thing should be fine. – A4L Aug 13 '14 at 10:42

5 Answers5

23

Spring support @Autowire, ... only for Spring Beans. Normally a Java Class become a Spring Bean when it is created by Spring, but not by new.

One workarround is to annotate the class with @Configurable but you must use AspectJ (compile time or loadtime waving)!

@see Using Spring's @Configurable in three easy steps for an short step by step instruction.

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • Would this be considered as "bad practice" or is this a totally "legal" was to solve this ? – user1119859 Aug 13 '14 at 11:28
  • Depends. If this is the only one instance that go this way and if there is an other way to solve the problem then I would choose the other way. - But it is a legal way, for example the complete Spring Roo Architecture is based on @Configurable – Ralph Aug 13 '14 at 11:35
  • 1
    FYI the [Using Spring's @Configurable in three easy steps](http://olivergierke.de/2009/05/using-springs-configurable-in-three-easy-steps/) link that you are referencing has broken links to the example source code which makes the reference impossible to follow. – Danny Bullis Jul 20 '18 at 05:52
8

When you create an object by new, autowire\inject don't work...

as workaround you can try this:

create your template bean of NotesPanel

<bean id="notesPanel" class="..." scope="prototype">
    <!-- collaborators and configuration for this bean go here -->
</bean>

and create an istance in this way

context.getBean("notesPanel");

PROTOTYPE : This scopes a single bean definition to have any number of object instances.

Xstian
  • 8,184
  • 10
  • 42
  • 72
1

The problem is here:

frame.add(new NotesPanel(), BorderLayout.CENTER);

you are creating a new object for class NotesPanel in the constructor of class Notepad.

The constructor is called before method main, so Spring context has not been loaded yet.

When instantiating the object for NotesPanel, it can't auto wire BackgroundGray because Spring context doesn't exists at that moment.

1

I share with you an example. I hope you love it :)

public class Main {
    public static void main(String args[]) {


        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager
                            .setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                new Ihm().setVisible(true);
            }
        });
    }
}

My configuration bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.myproject.configuration")
@PropertySource("classpath:/application.properties")
public class Config {

    @Bean
    public Configurator configurator() {
        return new Configurator();
    }

}

My java swing ihm that uses my configuration bean:

public class Ihm extends JFrame {

    private MyConfiguration configuration;

    public SmartRailServerConfigurationFileIhm() {

        try {
            ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
            configurator = context.getBean(MyConfiguration.class);
        } catch (Exception ex) {

        }
        System.out.println(configuration);

        ...
        ...
    }
}
Aron
  • 1,142
  • 1
  • 14
  • 26
0

You can perform DI on any instance, whether Spring managed or created with new.

To do so, use the following code...

AutowireCapableBeanFactory awcbf = applicationContext.getAutowireCapableBeanFactory();
awcbf.autowireBean(yourInstanceCreatedWithNew);

This is also a great way to introduce Spring into an application that was developed originally without Spring - as it allows you to use Spring where you want it without having to convert every class in the application into a Spring bean (because typically, you can't use a Spring bean without a Spring bean).

Rodney P. Barbati
  • 1,883
  • 24
  • 18