Java 第一次使用后注入Aspect中的字段,在启动时导致NullPointerException
摘要: 我在Java 第一次使用后注入Aspect中的字段,在启动时导致NullPointerException,java,spring,spring-mvc,aop,spring-aop,Java,Spring,Spring Mvc,Aop,Spring Aop,摘要: 我在@Service的@PostConstruct中执行了一些初始化操作。这些操作包括对方法的调用,在该方法执行后,将应用一个方面(DoAttionalStuffAspect) Aspect是通过aspectOf实例化的,因此它由Spring容器处理,但不幸的是,它的依赖项在执行ServiceInitializerFax@PostConstruct后被注入,导致NullPointerException 如何告诉Spring容器首先在方面中注入字段,然后实例化ServiceInitiali
@Service
的@PostConstruct
中执行了一些初始化操作。这些操作包括对方法的调用,在该方法执行后,将应用一个方面(DoAttionalStuffAspect
)
Aspect是通过aspectOf实例化的,因此它由Spring容器处理,但不幸的是,它的依赖项在执行ServiceInitializerFax@PostConstruct
后被注入,导致NullPointerException
如何告诉Spring容器首先在方面中注入字段,然后实例化ServiceInitializerFax
我尝试了一个方面的自动连接构造函数,但我认为最终AspectJ需要无参数构造函数,因此没有帮助
代码
这是我创建的一个示例,目的是在一个更复杂的应用程序中重现我遇到的问题。这是你想要查看的项目
代码如下:
这是初始化类:
@Component
public class ServiceInitialiserFacsimile {
private final SampleService sampleService;
@Autowired
public ServiceInitialiserFacsimile(SampleService ss) {
this.sampleService = ss;
}
@PostConstruct
public void initialiseAllTheServices() {
this.sampleService.init();
}
}
这是具有某些自定义逻辑的服务,需要由服务初始化器传真
@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>
/WEB-INF/views/jsp/
.jsp
通过在xml中设置id,我能够从方面强制ServiceInitializerFax的依赖性:
<bean id="myAspect" class="initialisation.mess.aspects.AdditionalStuffAspect" factory-method="aspectOf">
<property name="service" ref="additionalStuffService" />
</bean>
如果您使用的是基本SpringAOP,那么它将永远不会工作。代理尚未创建,因此尚未应用AOP,除此之外,如果它是代理,它仍然无法工作,因为它是一个内部方法调用,并且不会通过代理。如果您使用LTW或CTW,并且由Spring管理方面,Spring如何知道注入什么。。。注入方面没有@Autowired
。@M.Deinum很抱歉,我忘了将xml添加到问题中(它在github上提供,我也将在此处添加)。我使用xml配置进行依赖项注入,因为我正在复制我必须更新的更大项目的代码库。问题不在于方面不起作用或依赖项注入不起作用。一切都在运行,但执行顺序是错误的。如果没有出现错误,你的方面是编织在一起的,所以它会在需要时执行,因为没有明确的顺序(spring可以检测到!)你必须自己指定它,在需要方面的每个bean上添加一个dependsOn
。
@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();
}
}