2

I am trying to do a full annotations (no xml) implementation of Spring. The autowired members are not being populated. From my research, there are 3 things to do:

  • Set up a config file that manages the bean

  • Use @Autowired to get the bean to the file

  • Instantiate an application context to get the ball rolling

It is difficult to find a complete example of this which uses annotations only, so I don't have much to reference. Most examples use at least some xml.

There is no error message, so I don't have any idea where the problem is. The value is just null. Here are my files:

Trivial.java

public class Trivial {

    public TrivialBean trivialBean;

    @Autowired
    public void setTrivialBean(TrivialBean trivialBean) {
        this.trivialBean = trivialBean;
    }

    public static void main(String...args) {

        ApplicationContext context
                = new AnnotationConfigApplicationContext(
                TrivialConfig.class);

        new Trivial().go();
    }

    private void go() {

        System.out.println("trivialBean: " + trivialBean);
    }
}

TrivialBean.java

public class TrivialBean {

    public String foo = "TEST TEST TEST";

    @Override
    public String toString() {
        return foo;
    }
}

TrivialConfig.java

@Configuration
public class TrivialConfig {

    @Bean
    public TrivialBean trivialBean() {
        return new TrivialBean();
    }
}

I would expect this to output trivialBean: TEST TEST TEST, but is just outputs trivialBean: null

Don Subert
  • 2,636
  • 4
  • 27
  • 37

2 Answers2

2

For the @Autowired in Trivial to work, you need to have Trivial instantiated by Spring. new Trivial() won't work. For your sample to work, I think you need the following:

  1. Configure Trivial as a bean.
  2. Change new Trivial() to context.getBean(Trivial.class).

However, note that it is considered bad practice to use context.getBean under normal circumstances.

Sanjay
  • 8,755
  • 7
  • 46
  • 62
  • what would be the proper way to instantiate Trivial then? At some point, I have to explicitly instantiate some class, don't I? If I don't, then there is no instance of a class for autowired properties to exist on – Don Subert Aug 07 '15 at 07:10
  • Right, the applicationContext etc. has to be instantiated once. But normally, the framework code would do that. As an application developer, we write code in Service, Controller etc. classes, where normally we should not need to refer to context directly. But, in practice, getBean is very useful sometimes, particularly when we write static utility methods. – Sanjay Aug 08 '15 at 01:55
1

Regular autowiring in annotation-based container configuration

In order for autowiring to work, the lifecycle of the instance of Trivial has to be managed by the Spring container.

Example

TrivialBean.java is the same

public class TrivialBean {

    public String foo = "TEST TEST TEST";

    @Override
    public String toString() {
        return foo;
    }
}

TrivialConfig.java

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

@Configuration
public class TrivialConfig {

    @Bean
    public TrivialBean trivialBean() {
        return new TrivialBean();
    }

    @Bean
    public Trivial trivial() {
        return new Trivial();
    }
}

Trivial.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Trivial {

    public TrivialBean trivialBean;

    @Autowired
    public void setTrivialBean(TrivialBean trivialBean) {
        this.trivialBean = trivialBean;
    }

    public static void main(String... args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(TrivialConfig.class);
        Trivial trivial = context.getBean(Trivial.class);
        trivial.go();
    }

    private void go() {
        System.out.println("trivialBean: " + trivialBean);
    }
}

Output

trivialBean: TEST TEST TEST

Please consult Spring documentation for more information on Annotation-based container configuration.


AspectJ compile-time weaving and @Configurable

It is possible to autowire TrivialBean instance into Trivial instance created by new.

spring-aspects.jar contains an annotation-driven aspect that allows dependency injection for objects created outside of the control of the container. However, it should not be used in new Spring-based projects. It is intended to be used for legacy projects, where for some reason some instances are created outside of the Spring container.

Example for Spring 4.2.0 (the latest at the moment), AspectJ 1.8.6 (the latest at the moment), Maven and Java 1.8.

Additional dependencies on spring-aspects and aspectjrt

   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.6</version>
    </dependency>

Compile time weaving via AspectJ Maven plugin

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
            <encoding>UTF-8</encoding>
            <aspectLibraries>
                <aspectLibrary>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aspects</artifactId>
                </aspectLibrary>
            </aspectLibraries>
            <Xlint>warning</Xlint>
      </configuration>
      <executions>
          <execution>
              <goals>
                  <goal>compile</goal>
                  <goal>test-compile</goal>
              </goals>
          </execution>
      </executions>
</plugin>

TrivialBean.java is the same

public class TrivialBean {

    public String foo = "TEST TEST TEST";

    @Override
    public String toString() {
        return foo;
    }
}

TrivialConfig.java

@EnableSpringConfigured is analogous to <context:spring-configured>. It signals the current application context to apply dependency injection to classes that are instantiated outside of the Spring bean factory.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;

@Configuration
@EnableSpringConfigured
public class TrivialConfig {

    @Bean
    public TrivialBean trivialBean() {
        return new TrivialBean();
    }
}

Trivial.java

@Configurable applies Spring-driven configuration to Trivial

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@Configurable
public class Trivial {

    public TrivialBean trivialBean;

    @Autowired
    public void setTrivialBean(TrivialBean trivialBean) {
        this.trivialBean = trivialBean;
    }

    public static void main(String... args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(TrivialConfig.class);
        Trivial trivial = new Trivial();
        trivial.go();
    }

    private void go() {
        System.out.println("trivialBean: " + trivialBean);
    }
}

Output

trivialBean: TEST TEST TEST

It works! Please consult Spring documentation for more information on AspectJ and @Configurable.

Constantine
  • 3,187
  • 20
  • 26