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();
}
}
摘要:
我在 @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();
}
}