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