简体   繁体   English

@Aspect 方面的 Spring 自动装配 bean 为空

[英]Spring autowired bean for @Aspect aspect is null

I have the following spring configuration:我有以下弹簧配置:

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

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

<aop:aspectj-autoproxy/>

Then I have an aspect:然后我有一个方面:

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

This aspect works (I can hit a breakpoint on afterPoll) but simpleEmailSender is null.这方面有效(我可以在 afterPoll 上打断点)但 simpleEmailSender 为空。 Unfortunately I cannot find clear documentation on why this is.不幸的是,我找不到关于为什么会这样的明确文档。 (For the record, my simpleEmailSender bean exists and is correctly wired into other classes) The following things confuse me: (作为记录,我的 simpleEmailSender bean 存在并正确连接到其他类)以下事情让我感到困惑:

  1. Is context:component-scan supposed to be picking up @Aspect?上下文:组件扫描是否应该拾取@Aspect? If it is then surely it would be a spring managed bean, thus autowired should work?如果是,那么它肯定是一个弹簧管理的 bean,因此自动装配应该工作?
  2. If context:component-scan isn't for creating aspects, how is my aspect being created?如果 context:component-scan 不是用于创建方面,我的方面是如何创建的? I thought aop:aspectj-autoproxy just creates a beanPostProcessor to proxy my @Aspect class?我以为 aop:aspectj-autoproxy 只是创建了一个 beanPostProcessor 来代理我的 @Aspect 类? How would it do this if it isn't a spring managed bean?如果它不是 spring 管理的 bean,它会怎么做?

Obviously you can tell I don't have an understanding of how things should be working from the ground up.显然,您可以看出我不了解事情应该如何从头开始。

The aspect is a singleton object and is created outside the Spring container.该方面是一个单例对象,是在 Spring 容器之外创建的。 A solution with XML configuration is to use Spring's factory method to retrieve the aspect. XML 配置的解决方案是使用 Spring 的工厂方法来检索方面。

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

With this configuration the aspect will be treated as any other Spring bean and the autowiring will work as normal.使用此配置,方面将被视为任何其他 Spring bean,并且自动装配将正常工作。

You have to use the factory-method also on Enum objects and other objects without a constructor or objects that are created outside the Spring container.您还必须在 Enum 对象和其他没有构造函数的对象或在 Spring 容器外部创建的对象上使用工厂方法。

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

For Spring Boot to use @Autowired with AspectJ I have found the following method.为了让 Spring Boot 在 AspectJ 中使用 @Autowired,我找到了以下方法。 In configuration class add your aspect:在配置类中添加您的方面:

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

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

}

Then you can successfully autowire your services in your aspect class:然后,您可以在方面类中成功自动装配您的服务:

@Aspect
public class EmailAspect {

    @Autowired
    EmailService emailService;

Configuring @Autowired with java config only (so no XML based configuration) requires a bit of extra work than just adding @Configuration to the class, as it also needs the aspectOf method.仅使用 java config 配置@Autowired (因此没有基于 XML 的配置)需要一些额外的工作,而不仅仅是将@Configuration添加到类中,因为它还需要 aspectOf 方法。

What worked for me was creating a new class:对我有用的是创建一个新类:

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

And then use that in you aspect in conjunction with using @DependsOn @Configured and @Autowired:然后在你的方面结合使用@DependsOn @Configured 和@Autowired:

@DependsOn("springApplicationContextHolder")
@Configuration
@Aspect
public class SomeAspect {

    @Autowired
    private SomeBean someBean;

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

The @DependsOn is needed because spring can't determine the dependency because the bean is used staticly.需要@DependsOn 是因为spring 无法确定依赖项,因为bean 是静态使用的。

I dont have 50 rep to comment on a question so here is another answer relating to @ Jitendra Vispute answer.我没有 50 位代表对一个问题发表评论,所以这里是另一个与@ Jitendra Vispute答案相关的答案。 The official Spring doc mentions:官方 Spring 文档提到:

You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean.您可以在 Spring XML 配置中将方面类注册为常规 bean,或者通过类路径扫描自动检测它们 - 就像任何其他 Spring 管理的 bean 一样。 However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring's component scanner).但是,请注意 @Aspect 注释对于类路径中的自动检测是不够的:为此,您需要添加一个单独的 @Component 注释(或者,根据 Spring 组件扫描器的规则,您需要添加一个符合条件的自定义构造型注释)。 Source: Spring '4.1.7.Release' documentation . 来源:Spring '4.1.7.Release' 文档

This would mean that adding a @Component annotation and adding the @ComponentScan on your Configuration would make @Jitendra Vispute's example work.这意味着在您的配置上添加@Component 注释和@ComponentScan 将使@Jitendra Vispute 的示例工作。 For the spring boot aop sample it worked, though I did not mess around with context refreshing.对于 spring boot aop 示例,它起作用了,尽管我没有搞乱上下文刷新。 Spring boot aop sample : Spring Boot aop 示例

Application :应用

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

The application should also run as plain Spring Framework application with the following annotations instead of @SpringBootApplication:应用程序还应该作为普通的 Spring Framework 应用程序运行,使用以下注释而不是 @SpringBootApplication:

  • @Configuration @配置
  • @EnableAspectJAutoProxy @EnableAspectJAutoProxy
  • @ComponentScan @ComponentScan

and an AnnotationConfigApplicationContext instead of SpringApplication.和 AnnotationConfigApplicationContext 而不是 SpringApplication。

Service :服务:

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

Monitor Aspect :监控方面

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

This blog post explains it really well.这篇博文很好地解释了它。 Due to the fact that aspect singleton is created outside spring container you'd need to use factory-method=”aspectOf” that is only available after it is woven in by AspectJ ( not Spring AOP ) :由于方面单例是在 spring 容器之外创建的,因此您需要使用 factory-method="aspectOf" ,它只有在被 AspectJ(而不是 Spring AOP)编织后才可用:

Notice factory-method=”aspectOf” that tells Spring to use a real AspectJ ( not Spring AOP ) aspect to create this bean.注意 factory-method="aspectOf" 告诉 Spring 使用真正的 AspectJ(而不是 Spring AOP)方面来创建这个 bean。 So that after the aspect is woven in it has an “aspectOf” method.因此,在编织方面之后,它有一个“aspectOf”方法。

So that :以便 :

No matching factory method found: factory method 'aspectOf()' - That would mean that the aspect was not woven by AspectJ weaver.找不到匹配的工厂方法:工厂方法 'aspectOf()' - 这意味着该方面不是由 AspectJ 编织者编织的。

From my experience with spring 3.1, if I don't use @Autowired but traditional setter for dependency injection, it gets injected and works as expected without aspectJ weaver.根据我对 spring 3.1 的经验,如果我不使用 @Autowired 而是使用传统的 setter 进行依赖项注入,它会被注入并在没有 aspectJ weaver 的情况下按预期工作。 Although I'm encountering problems with the aspect being singleton... It results in 'perthis' instantiation model.尽管我遇到了单例方面的问题......它导致了'perthis'实例化模型。 . .

Add @Component to aspect class and your dependencies should get injected automatically.将@Component 添加到方面类,您的依赖项应该会自动注入。 and add context:component-scan for package where your aspect is in spring context file.并为您的方面在 spring 上下文文件中的包添加上下文:组件扫描。

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

Use compile time weaving, see for plugin example at: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml使用编译时编织,插件示例见: https : //github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml

The following combination of annotation and Spring config works for me thanks to notes above by Tobias/Willie/Eric:由于 Tobias/Willie/Eric 上面的注释,以下注释和 Spring 配置的组合对我有用:

Class:班级:

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

XML: XML:

<context:spring-configured />
<context:component-scan base-package="com.abc" />
@Configurable(autowire = Autowire.BY_TYPE)

Add this annotation to your Aspectj class.将此注释添加到您的Aspectj类。 Then it will be handled by Spring IOC.然后由Spring IOC处理。

For Spring Boot using @Autowired in @Aspect, my way is using spring.factories configuration file对于在@Aspect 中使用@Autowired 的 Spring Boot,我的方法是使用 spring.factories 配置文件

  1. create a file named spring.factories in src/main/resources在 src/main/resources 中创建一个名为 spring.factories 的文件

  2. the file content is as following文件内容如下

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM