2014-11-21 94 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方法或代码中的字段读访问来间接地匹配字段来解决此问题。如果你自己无法弄清楚,请随时询问如何做到这一点。