簡體   English   中英

@Aspect 方面的 Spring 自動裝配 bean 為空

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

我有以下彈簧配置:

<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. 上下文:組件掃描是否應該拾取@Aspect? 如果是,那么它肯定是一個彈簧管理的 bean,因此自動裝配應該工作?
  2. 如果 context:component-scan 不是用於創建方面,我的方面是如何創建的? 我以為 aop:aspectj-autoproxy 只是創建了一個 beanPostProcessor 來代理我的 @Aspect 類? 如果它不是 spring 管理的 bean,它會怎么做?

顯然,您可以看出我不了解事情應該如何從頭開始。

該方面是一個單例對象,是在 Spring 容器之外創建的。 XML 配置的解決方案是使用 Spring 的工廠方法來檢索方面。

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

使用此配置,方面將被視為任何其他 Spring bean,並且自動裝配將正常工作。

您還必須在 Enum 對象和其他沒有構造函數的對象或在 Spring 容器外部創建的對象上使用工廠方法。

另一種選擇是將@Configurable添加到您的方面類而不是搞亂 XML。

為了讓 Spring Boot 在 AspectJ 中使用 @Autowired,我找到了以下方法。 在配置類中添加您的方面:

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

僅使用 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 是靜態使用的。

我沒有 50 位代表對一個問題發表評論,所以這里是另一個與@ Jitendra Vispute答案相關的答案。 官方 Spring 文檔提到:

您可以在 Spring XML 配置中將方面類注冊為常規 bean,或者通過類路徑掃描自動檢測它們 - 就像任何其他 Spring 管理的 bean 一樣。 但是,請注意 @Aspect 注釋對於類路徑中的自動檢測是不夠的:為此,您需要添加一個單獨的 @Component 注釋(或者,根據 Spring 組件掃描器的規則,您需要添加一個符合條件的自定義構造型注釋)。 來源:Spring '4.1.7.Release' 文檔

這意味着在您的配置上添加@Component 注釋和@ComponentScan 將使@Jitendra Vispute 的示例工作。 對於 spring boot aop 示例,它起作用了,盡管我沒有搞亂上下文刷新。 Spring Boot aop 示例

應用

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

這篇博文很好地解釋了它。 由於方面單例是在 spring 容器之外創建的,因此您需要使用 factory-method="aspectOf" ,它只有在被 AspectJ(而不是 Spring AOP)編織后才可用:

注意 factory-method="aspectOf" 告訴 Spring 使用真正的 AspectJ(而不是 Spring AOP)方面來創建這個 bean。 因此,在編織方面之后,它有一個“aspectOf”方法。

以便 :

找不到匹配的工廠方法:工廠方法 'aspectOf()' - 這意味着該方面不是由 AspectJ 編織者編織的。

根據我對 spring 3.1 的經驗,如果我不使用 @Autowired 而是使用傳統的 setter 進行依賴項注入,它會被注入並在沒有 aspectJ weaver 的情況下按預期工作。 盡管我遇到了單例方面的問題......它導致了'perthis'實例化模型。 .

將@Component 添加到方面類,您的依賴項應該會自動注入。 並為您的方面在 spring 上下文文件中的包添加上下文:組件掃描。

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

使用編譯時編織,插件示例見: 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" />
@Configurable(autowire = Autowire.BY_TYPE)

將此注釋添加到您的Aspectj類。 然后由Spring IOC處理。

對於在@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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM