Search code examples
javaspringmavenaspectjaspectj-maven-plugin

Spring @configurable NullPointerException


I tried to use @configurable on my project to have @autowired service in object that are outside of spring context or something like that but didn't manage to make it work. The service is always null.

(I compared 4 different tutorials but nothing worked)

So, by despair I tried to download working examples directly to compare them but they didn't worked as well. (but I had to change them a bit, see in "Notes")

Here are the two examples I tried to download but gave me a nullPointerException too :
https://github.com/kenyattaclark/Spring-Configurable-Example
https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects (the link is at the end of the page)

So, are these examples working on your side? Did I miss something really important?

Notes :
I'm using the correto version of java(but I suppose it's unlikely that the problem come from here)

I had to make some changes in the two examples ot make them work so maybe that's why :
for the first one I added a <pluginManagement> in the pom because if I didn't, eclipse was giving me an error "Plugin execution not covered by lifecycle configuration" and if I tried to compile I had the error : "error can't determine superclass of missing type java.lang.Object when batch building BuildConfig[null] #Files=3 AopXmls=#0 [Xlint:cantFindType]"
for the second one I didn't use <pluginManagement>. But I didn't download the parent folder so I changed that in the pom and also forced the java version to 11 or maven was resetting it to 1.5 and had to update the junit test to junit5.

So there is room for me screwing all this up, but I'm tired of looking everywhere on internet and couldn't even get one working example.
So, if someone know what's wrong or how to make one example of @configurable work please tell me.


Solution

  • I looked at the first example. It uses very old versions of Spring, AspectJ and AspectJ Maven Plugin. I upgraded the POM to use

    • Spring 5.3.16,
    • AspectJ 1.9.9 (supports up to Java 18)
    • AspectJ.dev AspectJ Maven Plugin 1.13.1 (better and more up to date than Mojohaus, also supports Java 18 and can support any more recent version in the future by simply upgrading the aspectjtools plugin dependency, no plugin upgrade necessary).

    Please use JDK 11+ on your build system. More recent AspectJ compiler versions need it. You can still compile to Java 8 byte code, though. (But why would you?)

    Here is the Maven POM. I also added a separate dependency management section in order to globally manage dependency versions and exclusions. Then I used mvn dependency:analyze and mvn dependency:tree in order to clean up used undeclared and delcared unused dependencies. If your project needs another set of dependencies, you need to adjust it to your needs, of course. I configured it just for what is used in this tiny sample program. In doing so, I also cleaned out a few unused XML name-space and schema declarations. If you need them in your program, just add them again.

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.brightdome</groupId>
      <artifactId>spring-configurable-sample</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>Spring Configurable Sample</name>
      <description>
        Sample project to show how to work with Spring's @Configurable capability
        to inject dependencies into classes not instantiated by Spring.
      </description>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <aspectj.version>1.9.9</aspectj.version>
        <spring.version>5.3.16</spring.version>
      </properties>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
          </plugin>
          <plugin>
            <groupId>dev.aspectj</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.13.1</version>
            <dependencies>
              <!-- Override older AspectJ compiler in plugin, use latest one -->
              <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>${aspectj.version}</version>
              </dependency>
            </dependencies>
            <executions>
              <execution>
                <goals>
                  <goal>compile</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <source>${maven.compiler.source}</source>
              <target>${maven.compiler.target}</target>
              <aspectLibraries>
                <aspectLibrary>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aspects</artifactId>
                </aspectLibrary>
              </aspectLibraries>
            </configuration>
          </plugin>
        </plugins>
      </build>
    
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
          </dependency>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
          </dependency>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
          </dependency>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
            <exclusions>
              <!-- Only needed for load-time weaving, not compile-time -->
              <exclusion>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
              </exclusion>
            </exclusions>
          </dependency>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
        </dependencies>
      </dependencyManagement>
    
      <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
        </dependency>
        <!-- Needed by spring-aspects -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
        </dependency>
        <!-- Needed by compile-time aspects during runtime -->
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
        </dependency>
      </dependencies>
    
    </project>
    
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <beans
      xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
      "
    >
      <context:spring-configured/>
      <context:component-scan base-package="com.brightdome"/>
      <context:annotation-config/>
    </beans>
    
    package com.brightdome.sample.spring;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class HelloWorldService {
      public void sayHello() {
        System.out.println("Hello world!");
      }
    }
    
    package com.brightdome.sample.spring;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Configurable;
    
    @Configurable
    public class HelloWorldClient {
      @Autowired
      private HelloWorldService service;
    
      public void sayHello() {
        // Used injected instance of service
        service.sayHello();
      }
    }
    
    package com.brightdome.sample.spring;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class HelloWorld {
      public static void main(String[] args) {
        // Initialize Spring Context
        new ClassPathXmlApplicationContext("/META-INF/applicationContext.xml");
    
        // Instantiate class by new'ing it up. i.e., Do not obtain from Spring context
        HelloWorldClient client = new HelloWorldClient();
        client.sayHello();
      }
    }
    

    This compiles and runs just fine in both my IDE (IntelliJ IDEA) and with Maven. On the console, it should simply print "Hello world!" and then exit.