[英]How to enforce/verify spring scope annotation on spring beans
我們完全由注釋驅動,並且不使用XML文件進行彈簧配置。
spring bean的默認范圍是單例,許多開發人員忘記了這些,最終創建了應該改變范圍的bean。 問題的復雜性增加了各種作用域bean的混合和匹配。
是否有任何Maven插件可以檢查具有@Component
批注的任何類是否也具有@Scope
批注,如果缺少,則構建失敗。 這將迫使開發人員考慮范圍和使用模式。 如果不存在類似的東西,我可以編寫插件或使用一個自定義工具檢查該問題並在jenkins構建過程中觸發。 春季代碼中有什么可以幫助我做到這一點嗎?
另外,如果spring bean中有@Autowire
注釋,是否有一種方法可以驗證注入的bean具有正確的作用域。 我正在假設您將原型作用域bean注入單例作用域bean中,最有可能的不是您想要的。 盡管在某些情況下這可能是開發人員想要的,但就我們而言,到目前為止,這主要是開發人員的錯誤。
您可以使用AspectJ的功能根據切入點聲明錯誤和/或警告。
免責聲明: 我從來沒有使用過Spring,所以我不是那里的專家,並且只是舉了一個沒有太多示范意義的示例。
具有原型范圍的Spring bean:
package de.scrum_master.app;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class ScopedBean {}
缺少范圍聲明的Spring bean:
package de.scrum_master.app;
import org.springframework.stereotype.Component;
@Component
public class UnscopedBean {}
使用不同類型的自動裝配的Spring bean:
該bean使用構造函數和setter方法連接。 如果您取消注釋字段聲明上的注釋,您甚至可以使用其他類型的接線。 這沒有道理,但是我們稍后會在一個方面引發編譯錯誤。
package de.scrum_master.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class BeanWithAutowire {
//@Autowired
private ScopedBean scopedBean;
@Autowired
public BeanWithAutowire(ScopedBean scopedBean) {
this.scopedBean = scopedBean;
}
@Autowired
public void setScopedBean(ScopedBean scopedBean) {
this.scopedBean = scopedBean;
}
}
靜態注釋一致性檢查的方面:
package de.scrum_master.aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
public aspect BeanAnnotationChecker {
declare error :
@annotation(Component) && !@annotation(Scope) :
"Spring component without scope declaration found";
declare error :
execution(@Autowired *.new(.., @Scope("prototype") *, ..)) && within(@Scope("singleton") *) :
"singleton bean auto-wired into prototype container via constructor";
declare error :
execution(@Autowired * *(.., @Scope("prototype") *, ..)) && within(@Scope("singleton") *) :
"singleton bean auto-wired into prototype container via setter method";
declare error :
set(@Autowired * *) && within(@Scope("singleton") *) :
"singleton bean auto-wired into prototype container via field assignment";
}
使用AspectJ編譯器的Maven POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>aspectj-fail-build</artifactId>
<version>1.0-SNAPSHOT</version>
<name>AspectJ - fail build for wrong/missing annotations</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>1.7</java.source-target.version>
<aspectj.version>1.8.4</aspectj.version>
<main-class>de.scrum_master.app.ScopedBean</main-class>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>UTF-8</encoding>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<!-- IMPORTANT -->
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
</dependencies>
</project>
mvn clean package
控制台輸出:
(...)
[INFO] ------------------------------------------------------------------------
[INFO] Building AspectJ - fail build for wrong/missing annotations 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
(...)
[ERROR] singleton bean auto-wired into prototype container via constructor
C:\Users\Alexander\Documents\java-src\SO_AJ_MavenFailBuildOnWrongAnnotation\src\main\java\de\scrum_master\app\BeanWithAutowire.java:14
public BeanWithAutowire(ScopedBean scopedBean) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ERROR] singleton bean auto-wired into prototype container via setter method
C:\Users\Alexander\Documents\java-src\SO_AJ_MavenFailBuildOnWrongAnnotation\src\main\java\de\scrum_master\app\BeanWithAutowire.java:19
public void setScopedBean(ScopedBean scopedBean) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ERROR] Spring component without scope declaration found
C:\Users\Alexander\Documents\java-src\SO_AJ_MavenFailBuildOnWrongAnnotation\src\main\java\de\scrum_master\app\UnscopedBean.java:6
public class UnscopedBean {}
^^^^^^^^^^^
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
(...)
我認為該示例除了AspectJ語法以外,在某種程度上是不言自明的,但是您可以在AspectJ手冊或教程中閱讀更多有關該示例的信息。 如果取消注釋字段聲明中的@Autowired
注釋,則對於顯式字段分配,您還會看到更多錯誤。 但是,AspectJ不能僅在字段聲明(沒有分配)上匹配。 因此,只要您的開發人員依靠字段注釋而不是帶注釋的構造方法或setter方法,即您的代碼中沒有任何顯式的字段分配,就不會出現編譯錯誤。 您可以通過在代碼中匹配getter方法或字段讀取訪問以間接匹配字段來解決此問題。 如果您自己無法解決,請隨時詢問該怎么做。
最近,我還需要驗證@Autowired
bean的范圍,並且找不到任何合適的即用型解決方案。 因此,我創建了一個小型項目 ,該項目允許在運行時驗證bean范圍。 默認情況下,它允許進行以下注入:
如果要允許將bean注入另一個作用域,則需要使用相應的注釋明確允許它:
@Bean
@Scope("prototype")
@InjectableInto("singleton")
MyBean getMyBean(){
//...
}
如果bean在運行時使用了不允許范圍的依賴關系,則它可以記錄它,拋出異常(從而阻止bean的創建)或執行任何自定義操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.