Aspect 中的字段在首次使用后注入,导致启动时出现 NullPointerException

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

摘要:

我在 @Service ServiceInitialiserFacsimile@PostConstruct 中执行了一些初始化操作。这些操作包括对方法的调用,在该方法执行后应用方面 (DoAttionalStuffAspect)。

Aspect是通过aspectOf实例化的,所以由SpringContainer处理,可惜它的依赖被注入了AFTER 执行 ServiceInitialiserFacsimile @PostConstruct,导致 NullPointerException。

如何让 Spring 容器先注入 Aspect 中的字段,然后实例化 ServiceInitialiserFacsimile

我尝试为方面使用自动装配构造函数,但我认为最终 AspectJ 需要无参数构造函数,所以它没有帮助

代码

这是我为了重现我在更复杂的应用程序中遇到的问题而创建的示例。如果您想检查一下,这是该项目。 https://github.com/alessiop86/spring3-mvc-maven-xml-hello-world

代码如下: 这是初始化 class:

@Component
public class ServiceInitialiserFacsimile {

    private final SampleService sampleService;

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

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

}

这是具有一些自定义逻辑的服务,需要由 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");
    }
}

这是我在方面定义中使用的注解

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

这是方面

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

这是已创建的服务,但在方面为 运行:

时尚未实例化
@Service
public class AdditionalStuffService {

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

Spring上下文xml配置文件:

<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>

我能够通过在 xml:

中设置一个 id 来从方面强制执行对 ServiceInitialiserFacsimile 的依赖
  <bean id="myAspect" class="initialisation.mess.aspects.AdditionalStuffAspect" factory-method="aspectOf">
        <property name="service" ref="additionalStuffService" />
    </bean>

然后使用 @DependsOn 注释从 spring 管理的 AdditionalStuffAspect 指定 ServiceInitialiserFacsimile 的依赖关系:

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

}