2014-11-21 63 views
0

我們完全由annotation驅動,並且不使用XML文件進行spring配置。如何在spring beans上強制執行/驗證spring scope註釋

Spring bean的默認範圍是單例,許多開發人員忘記了這個範例,並最終創建應該有不同範圍的bean。增加了各種範圍的bean的混合和匹配問題的複雜性。

是否有任何maven插件可以檢查是否有任何具有@Component註解的類也具有@Scope註釋並且在構建失敗時會失敗。這將迫使開發人員考慮範圍和使用模式。如果類似的東西不存在,我可以編寫插件或者有一個定製工具,可以在jenkins構建過程中檢查這個和火災。春季的代碼可以幫助我做到這一點嗎?

另外,如果Spring bean中有@Autowire註解,有沒有一種方法可以驗證被注入的bean是否有正確的範圍。如果你在singleton scoped bean中注入原型scoped bean,我正在處理這個假設,很可能這不是你想要的。雖然這可能是開發人員想要的用例,但在我們的情況中,迄今爲止,這主要是開發人員的錯誤。

+0

我不知道用Spring本身進行驗證。您可以使用自定義PMD或FindBugs規則來執行此操作。 – user944849 2014-11-21 20:28:23

+0

你可以看看maven-enforcer-plugin,你可以實現自己的規則,這可能是你的一條路徑...... – khmarbaise 2014-11-22 17:05:43

回答

0

您可以使用AspectJ基於切入點聲明錯誤和/或警告的能力。

免責聲明:我從來沒有使用過春節,所以我不是專家那裏,只是做了一個例子沒有多大意義的示範。

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) && [email protected](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"; 
} 

Maven的POM使用AspectJ編譯器:

<?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無法在單純的字段聲明(不帶任務)上匹配。因此,無論您的開發人員何時依賴字段註釋而不是註釋的構造函數或設置方法,即您的代碼中沒有任何明確的字段分配,都不會出現編譯錯誤。您可以通過匹配getter方法或代碼中的字段讀訪問來間接地匹配字段來解決此問題。如果你自己無法弄清楚,請隨時詢問如何做到這一點。