1

I'm trying to use @configurable in spring to use a @autowired service in a non bean class I create.
It doesn't want to work anymore whatever I try.
Can someone tell me what I'm doing wrong? (I did some research but I'm totally clueless now)
Here is a very basic code example I made :

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo2</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Configuration ComponentScan class

package com.example.demo2;

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

@Configuration
@ComponentScan
@EnableSpringConfigured
public class AspectJConfig
{
    
}

@SpringBootApplication class

package com.example.demo2;

import javax.annotation.PostConstruct;

//import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Demo2Application
{
    //@Autowired
    //private HelloWorldService helloWorldService;
    
    public static void main(String[] args)
    {
        SpringApplication.run(Demo2Application.class, args);
    }
    
    @PostConstruct
    public void doSomethingIProbablyShouldNotBeDoing()
    {
        //helloWorldService.sayHello();
        HelloWorldClient client = new HelloWorldClient();
        client.sayHello();
    }
    
}

class with @Configurable and @Autowired service

package com.example.demo2;

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();
    }
}

@Service class

package com.example.demo2;

import org.springframework.stereotype.Service;

@Service
public class HelloWorldService
{
    public void sayHello()
    {
        System.out.println("Hello world!");
    }
}

Also here is a link to my previous post on that subject. I did received an answer to my question that was working. But for whatever reason it doesn't work anymore on my side.
Spring @configurable NullPointerException

  • How about some feedback to my answer? I think it is rather impolite to ask for help in public and then keep your helpers waiting. – kriegaex Nov 13 '22 at 09:23
  • you're right, sorry about that. I've noticed your response last week but I've been pretty busy and I haven't took the time to check it out yet. I'm going to try it out before this week end and tell you if it's working. – whiteRabbit Nov 14 '22 at 10:14

2 Answers2

1

@Configurable should be used in connection with native AspectJ, not with Spring AOP, see here. Because the annotation is meant to be used with POJOs rather than Spring beans, this makes sense.

In my answer to your first question, we used AspectJ Maven Plugin to do compile-time weaving (CTW). Either you need to do the same here or configure load-time weaving (LTW) instead.

When using LTW, in order for org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect to work, which is responsible for achieving the POJO dependency injection, you need either of

  • -javaagent:/path/to/aspectjweaver.jar on the JVM command line or
  • -javaagent:/path/to/spring-instrument.jar on the JVM command line and @EnableLoadTimeWeaving in your Spring configuration or
  • de.invesdwin:invesdwin-instrument in the dependency list plus a code snippet initialising the weaver in your application. On more recent Java versions, you probably also need --add-opens java.base/java.lang=ALL-UNNAMED on the JVM command line to enable invesdwin-instrument to work correctly, using reflection.

You also need spring-aspects for the AnnotationBeanConfigurerAspect to be found. Optionally, you might want to add your own META-INF/aop.xml (or org/aspectj/aop.xml) file in the resources directory, if you want to configure certain weaving options or deactivate unneeded aspects from spring-aspects, if the corresponding warning messages on the console get on your nerves.

I know that this is not trivial, which is why I think that the CTW approach is easier to set up. But if you want to apply aspects dynamically via LTW, you need to use one of the approaches mentioned above. It is not rocket science, just a bit complicated when doing it the first time. But like Miyamoto Musashi said: "It may seem difficult at first, but all things are difficult at first."


Update: Here is an MCVE for you, i.e. a full, minimal example:

https://github.com/kriegaex/SO_AJ_SpringMavenConfigurableNPE_74184130

Feel free to play with it and remove the code using invesdwin-instrument and the corresponding dependency, if you want to use -javaagent and Spring on-board means. I actually recommend that.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Please note the update, adding a link to a GitHub repository with an [MCVE](https://stackoverflow.com/help/mcve) solving your problem. – kriegaex Nov 07 '22 at 17:17
  • Hi, sorry for the long wait. I tried your mvce and it's working. So at least it doesn't come from my computer. Yeah it's really not trivial for me. I don't understand the difference between different type of weaving, I thought the aspect oriented programming would include native aspectJ and other stuff. You're answer is very professional and gives me hint to understand what I missed, I'm going to look more deeply into it. Thank you very much. – whiteRabbit Nov 18 '22 at 20:30
0

You are creating service manually using new HelloWorldClient() @Autowired works only when bean/service is created by spring

try autowiring HelloWorldClient in Demo2Application and not creating it manually

Maybe You could try Spring @Autowired on a class new instance maybe it works

lukwas
  • 232
  • 1
  • 2
  • 11
  • Hello, thanks for your answer. Well that's the purpose of @configurable actually. What I don't understand is why the aspect thing isn't working. It's like it's deactivated. – whiteRabbit Oct 25 '22 at 14:50
  • Reading about it might be some problem with 'aspectjweaver' previous answer had it, but current solution has 'spring-boot-starter-aop' Afyer reading this https://stackoverflow.com/questions/4703206/spring-autowiring-using-configurable it looks like it might be many reasons it might not work :/ – lukwas Oct 25 '22 at 20:10
  • Yeah I tried a lot of different things, including the code of the previous answer without changing anything. And also upgrading everything (spring, java, eclispe etc). Or testing on two different computers. Or with different libraries (I used spring-boot-starter-aop this time to be sure to use the officials one for this example). – whiteRabbit Oct 25 '22 at 23:12