【问题标题】:Spring autowired bean for @Aspect aspect is null@Aspect 方面的 Spring 自动装配 bean 为空
【发布时间】:2012-03-26 21:23:12
【问题描述】:

我有以下弹簧配置:

<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 为空。不幸的是,我找不到关于为什么会这样的明确文档。 (作为记录,我的 simpleEmailSender bean 存在并正确连接到其他类)以下事情让我感到困惑:

  1. context:component-scan 是否应该选择@Aspect?如果是,那么它肯定是一个 spring 管理的 bean,因此 autowired 应该可以工作吗?
  2. 如果 context:component-scan 不是用于创建方面,我的方面是如何创建的?我以为 aop:aspectj-autoproxy 只是创建一个 beanPostProcessor 来代理我的 @Aspect 类?如果它不是 Spring 托管的 bean,它会如何做到这一点?

显然,您可以说我不了解事情应该如何从头开始。

【问题讨论】:

  • 你能在@Aspect旁边加@Service吗?
  • 它仍然为空,但我现在收到 INFO : Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@76faf7d6: defined beans [..., SyncLoggingAspect in the logs
  • setter 注入或注释私有字段应该没有区别
  • 是否有可能 必须存在于应用程序上下文中才能检测@Autowired 注释?
  • @kclaes 和context:component-scan 没有必要,因为这也会注册AutowiredAnnotationBeanPostProcessorFurthermore, the AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both included implicitly when you use the component-scan element. -- 来自static.springsource.org/spring/docs/3.0.x/…

标签: java spring aop aspectj spring-aop


【解决方案1】:

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

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

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

您还必须对 Enum 对象和其他没有构造函数的对象或在 Spring 容器之外创建的对象使用工厂方法。

【讨论】:

  • 从我在文档中看到的内容来看,这是在您使用 AspectJ 时使用的。我应该使用spring AOP。我的配置中是否有一些我遗漏/需要添加的东西才能做到这一点?
  • 试用后确实给了我解决方案,但现在我仍然因为我的上述观点而感到困惑
  • spring 文档很庞大。可配置用于非单例。
  • 对于那些使用 JavaConfig 来配置你的方面的人,这对我有用:stackoverflow.com/a/22872161/1366367
【解决方案2】:

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

【讨论】:

  • 不错。请记住 XML 中的
  • 谢谢! @Configurable 为我做了诀窍。不需要任何带有 @Configurable 注释的单独 JavaConfig 或 XML factory-method='aspect-of'。
  • 我刚刚遇到了一个失败的情况;当 Spring 上下文刷新时,Spring 不会自动更新引用。我在stackoverflow.com/q/22826526/827480 上发布了一个问题,正在寻找解决此问题的方法。
  • weaven(with aspectj) on compile time 这对我不起作用。你有这方面的经验吗?
【解决方案3】:

为了让 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;

【讨论】:

    【解决方案4】:

    仅使用 java config 配置 @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 SpringApplicationContextHolder.getApplicationContext().getBean(SomeAspect.class);
        }
    

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

    【讨论】:

    • 感谢您,因为这是我能找到的唯一基于注释的示例。
    • CheckServicesPermissionsAspect.class?我不明白这是什么。
    • 这是一个现有项目的副本,但我忘了概括这一点,现在已修复:)
    • 我认为@Component 注释将是SomeAspect 类的正确注释。您只希望它被发现,但您没有在该类中创建 bean,这将是 @Configuration 的语义。
    • 你可能是对的,我大约在 1.5-2 年前创建了这个,所以我不记得我为什么选择 @Configuration。
    【解决方案5】:

    我没有 50 位代表评论一个问题,所以这里有另一个与 @ Jitendra Vispute 答案相关的答案。 Spring 官方文档提到:

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

    这意味着添加@Component 注释并在您的配置中添加@ComponentScan 将使@Jitendra Vispute 的示例工作。对于 spring boot 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:

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

    【讨论】:

      【解决方案6】:

      这个blog post 解释得很好。由于方面单例是在 spring 容器之外创建的,因此您需要使用 factory-method=”aspectOf” ,它仅在由 AspectJ(而不是 Spring AOP)编织后才可用:

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

      这样:

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

      根据我对 spring 3.1 的经验,如果我不使用 @Autowired 而是使用传统的 setter 进行依赖注入,它会被注入并在没有 aspectJ weaver 的情况下按预期工作。虽然我遇到了单例方面的问题......它导致了“perthis”实例化模型。 .

      【讨论】:

        【解决方案7】:

        将@Component 添加到方面类,您的依赖项应该会自动注入。 并为你的方面在 spring 上下文文件中的包添加 context:component-scan。

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

        【讨论】:

          【解决方案8】:

          使用编译时编织,插件示例见: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" />
          

          【讨论】:

            【解决方案9】:
            @Configurable(autowire = Autowire.BY_TYPE)
            

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

            【讨论】:

              【解决方案10】:

              对于在 @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

              【讨论】:

                猜你喜欢
                • 2014-09-08
                • 2017-03-14
                • 2014-11-17
                • 2013-09-28
                • 2018-04-16
                • 1970-01-01
                • 1970-01-01
                • 2016-09-27
                相关资源
                最近更新 更多