alessiop86 alessiop86 - 1 month ago 14
Java Question

Field in Aspect injected after its first use, causing NullPointerException at startup

ABSTRACT:

I have some initialisation operations executed in

@PostConstruct
of
@Service
ServiceInitialiserFacsimile
. Those operations include a call to a method after whose execution an Aspect (
DoAttionalStuffAspect
) is applied.

The Aspect is instantied through aspectOf, so it is handled by the Spring Container, but unfortunately its dependencies are injected AFTER the execution of ServiceInitialiserFacsimile
@PostConstruct
, resulting in a NullPointerException.

How can I tell the Spring Container to inject first the fields in the Aspect and then instantiate the
ServiceInitialiserFacsimile
?

I tried with an Autowired constructor for the aspect, but I think in the end AspectJ requires the no-arg constructor, so it was no help

CODE

This is a Sample I created in order to reproduce the issue I have in a much more complicated app. Here is the project if you want to check it out. https://github.com/alessiop86/spring3-mvc-maven-xml-hello-world

Code below:
This is the initialisation class:

@Component
public class ServiceInitialiserFacsimile {

private final SampleService sampleService;

@Autowired
public ServiceInitialiserFacsimile(SampleService ss) {
this.sampleService = ss;
}

@PostConstruct
public void initialiseAllTheServices() {
this.sampleService.init();
}

}


This is the service with some custom logic that requires to be initialised by the
ServiceInitialiserFacsimile
@PostConstruct
:

@Service
public class SampleService {

public void init() {
System.out.println("do some stuff");
try {
execute();
}
catch(Exception e) {
System.err.println("I do not want to block to whole framework initialisation");
}
}

@DoAdditionalStuff
public void execute() {
System.out.println("Phase 1");
}
}


This is the annotation I use in the aspect definition

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoAdditionalStuff {
}


This is the aspect

@Aspect
public class AdditionalStuffAspect {

private AdditionalStuffService service;

public AdditionalStuffService getService() {
return service;
}

public void setService(AdditionalStuffService service) {
this.service = service;
}

@Pointcut(value="execution(public * *(..))")
private void anyPublicMethod() { }

@AfterReturning("anyPublicMethod() && @annotation(doAdditionalStuff)")
public void afterReturning(JoinPoint jointPoint, DoAdditionalStuff doAdditionalStuff) {
System.out.println(jointPoint);
service.doStuff();
}
}


This is the service that is created, but not yet instantiated when the aspect is run:

@Service
public class AdditionalStuffService {

public void doStuff() {
System.out.println("Phase2: additional stuff");
}
}


Spring context xml configuration file:

<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:component-scan base-package="initialisation.mess"/>

<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

<mvc:resources mapping="/resources/**" location="/resources/"/>

<mvc:annotation-driven/>

<bean class="initialisation.mess.aspects.AdditionalStuffAspect" factory-method="aspectOf">
<property name="service" ref="additionalStuffService" />
</bean>
</beans>

Answer

I was able to enforce a dependency on the ServiceInitialiserFacsimile from the Aspect by setting an id in the xml:

  <bean id="myAspect" class="initialisation.mess.aspects.AdditionalStuffAspect" factory-method="aspectOf">
        <property name="service" ref="additionalStuffService" />
    </bean>

and then specifying the dependency of ServiceInitialiserFacsimile from the spring managed AdditionalStuffAspect with a @DependsOn annotation:

@Component
@DependsOn("myAspect")
public class ServiceInitialiserFacsimile {

    private final SampleService sampleService;

    @Autowired
    public ServiceInitialiserFacsimile(SampleService ss) {
        this.sampleService = ss;
    }

    @PostConstruct
    public void initialiseAllTheServices() {
        this.sampleService.init();
    }

}