@Aspect方面的Spring autowired bean为null

时间:2012-03-09 12:10:33

标签: java spring aop aspectj spring-aop

我有以下弹簧配置:

<context:component-scan base-package="uk.co.mysite.googlecontactsync.aop"/>

<bean name="simpleEmailSender" class="uk.co.mysite.util.email.simple.SimpleEmailSenderImplementation"/>

<aop:aspectj-autoproxy/>

然后我有一个方面:

@Aspect
public class SyncLoggingAspect {
    @Autowired
    private SimpleEmailSender simpleEmailSender

    @AfterReturning(value="execution(* uk.co.mysite.datasync.polling.Poller+.doPoll())", returning="pusher")
    public void afterPoll(Pusher pusher) {      
        simpleEmailSender.send(new PusherEmail(pusher));
    }
}

此方面有效(我可以在afterPoll上点击断点)但simpleEmailSender为null。不幸的是,我找不到明确的文件说明原因。 (为了记录,我的simpleEmailSender bean存在并正确连接到其他类)以下事情使我感到困惑:

  1. 上下文:组件扫描应该是@Aspect?如果它肯定是一个Spring托管bean,那么autowired应该可以工作吗?
  2. 如果上下文:组件扫描不是用于创建方面,我的方面是如何创建的?我认为aop:aspectj-autoproxy只是创建一个beanPostProcessor来代理我的@Aspect类?如果它不是一个Spring托管bean,它会怎么做呢?
  3. 显然你可以告诉我,我不了解事情应该如何从头开始。

10 个答案:

答案 0 :(得分:33)

方面是单个对象,在Spring容器外部创建。使用XML配置的解决方案是使用Spring的工厂方法来检索方面。

<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect" 
     factory-method="aspectOf" />

使用此配置,该方面将被视为任何其他Spring bean,并且自动装配将正常工作。

您还必须在Enum对象和其他对象上使用factory-method,而不使用构造函数或在Spring容器外部创建的对象。

答案 1 :(得分:18)

另一种选择是将@Configurable添加到方面类,而不是乱用XML。

答案 2 :(得分:8)

对于Spring Boot使用@Autowired和AspectJ,我找到了以下方法。 在配置类中添加您的方面:

@Configuration
@ComponentScan("com.kirillch.eqrul")
public class AspectConfig {

    @Bean
    public EmailAspect theAspect() {
        EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
        return aspect;
    }

}

然后,您可以在方面类中成功自动装配服务:

@Aspect
public class EmailAspect {

    @Autowired
    EmailService emailService;

答案 3 :(得分:4)

仅使用java配置配置@Autowired(因此,没有基于XML的配置)需要一些额外的工作,而不仅仅是向类中添加@Configuration,因为它还需要aspectOf方法。

对我有用的是创建一个新类:

@Component
public class SpringApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
       this.applicationContext = applicationContext;
    }
}

然后在使用@DependsOn @Configured和@Autowired:

的方面使用它
@DependsOn("springApplicationContextHolder")
@Configuration
@Aspect
public class SomeAspect {

    @Autowired
    private SomeBean someBean;

    public static SomeAspect aspectOf() {
        return SpringApplicationContextProvider.getApplicationContext().getBean(SomeAspect.class);
    }

需要@DependsOn,因为spring无法确定依赖项,因为bean是静态使用的。

答案 4 :(得分:4)

我没有50位代表对某个问题发表评论,所以这是另一个与@ Jitendra Vispute答案相关的答案。 春季官方文件提到:

  

您可以在Spring XML配置中将方面类注册为常规bean,或者通过类路径扫描自动检测它们 - 就像任何其他Spring管理的bean一样。但是,请注意@Aspect注释不足以在类路径中自动检测:为此,您需要添加单独的@Component注释(或者根据Spring的组件扫描程序的规则添加符合条件的自定义构造型注释)。 Source: Spring '4.1.7.Release' documentation

这意味着添加@Component注释并在Configuration上添加@ComponentScan会使@Jitendra Vispute的示例工作。对于春季启动aop样本它工作,虽然我没有搞乱上下文刷新。Spring boot aop sample

<强>应用

package sample.aop;
@SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {
    // Simple example shows how an application can spy on itself with AOP
    @Autowired
    private HelloWorldService helloWorldService;
    @Override
    public void run(String... args) {
        System.out.println(this.helloWorldService.getHelloMessage());
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleAopApplication.class, args);
    }
}

应用程序也应该作为普通的Spring Framework应用程序运行,并带有以下注释而不是@SpringBootApplication:

  • @Configuration
  • @EnableAspectJAutoProxy
  • @ComponentScan

和AnnotationConfigApplicationContext而不是SpringApplication。

<强>服务

package sample.aop.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldService {
    @Value("${name:World}")
    private String name;
    public String getHelloMessage() {
        return "Hello " + this.name;
    }
}

监控方面

package sample.aop.monitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ServiceMonitor {
    @AfterReturning("execution(* sample..*Service.*(..))")
    public void logServiceAccess(JoinPoint joinPoint) {
        System.out.println("Completed: " + joinPoint);
    }
}

答案 5 :(得分:2)

blog post解释得非常好。由于在弹簧容器外部创建了方面单例,因此您需要使用仅在AspectJ(而非Spring AOP)编织后才能使用的factory-method =“aspectOf”:

  

注意factory-method =“aspectOf”告诉Spring使用真实的   AspectJ(不是Spring AOP)方面创建这个bean。那之后   在这方面编织它有一个   “aspectOf”方法。

那样:

  

找不到匹配的工厂方法:工厂方法'aspectOf()' - 那   意味着这个方面不是由AspectJ weaver编织的。

根据我对spring 3.1的经验,如果我不使用@Autowired但传统的setter用于依赖注入,它会被注入并按预期工作而不使用aspectJ weaver。虽然我遇到问题的方面是单身...它导致'perthis'实例化模型。

答案 6 :(得分:2)

将@Component添加到aspect类,并且应该自动注入依赖项。 并添加上下文:component-scan for package,其中aspect在spring上下文文件中。

@Component
@Aspect
public class SomeAspect {
    /* following dependency should get injected */
    @Autowired
    SomeTask someTask;
    /* rest of code */  
}

答案 7 :(得分:0)

使用编译时编织,请参阅:https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml

中的插件示例

由于Tobias / Willie / Eric的上述说明,以下注释和Spring配置组合适用于我:

类别:

package com.abc
@Configurable
@Aspect
public class MyAspect {
   @Autowired
   protected SomeType someAutoWiredField;
}

XML:

<context:spring-configured />
<context:component-scan base-package="com.abc" />

答案 8 :(得分:0)

@Configurable(autowire = Autowire.BY_TYPE)

将此注释添加到您的Aspectj类中。然后它将由Spring IOC处理。

答案 9 :(得分:0)

对于在@Aspect 中使用@Autowired 的Spring Boot, 我的方法是使用 spring.factories 配置文件

  1. 在 src/main/resources 中创建一个名为 spring.factories 的文件

  2. 文件内容如下

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=pack.age.to.YourAspect,pack.age.to.YourDAO