Spring Boot@Autowired-检查样式/pmd/spotbugs规则以警告非接口使用



最近在Spring Boot应用程序中遇到了Spring CGLib与JDK的动态代理问题(在Mac上运行时似乎使用CGLib,而在Linux上运行时,相同的应用程序使用JDK动态代理(。

我正在寻找一个linter工具配置/插件(PMD、Checkstyle、Spotbugs等(,它可以识别@Autowired注释字段/参数的使用位置,并且要自动连接的类是用类实例而不是接口定义的。

例如:

public interface MyService {
}

public class MyServiceImpl implements MyService {
}

@RestController
public class MyController {
@Autowired
private MyServiceImpl serviceImpl;   // this should raise a warning in a linter as
// class is being used for injection type
@Autowired
private MyService service;           // this should not raise a warning, as interface
// is used for type
}

我认为这不可能用PMD、Checkstyle、Spotbugs等进行检查。但有一个解决方案可以检查这一点。ArchUnit提供编写自定义规则,可以为此编写自定义规则。假设您使用JUnit5或JUnit4进行测试,您可以执行以下操作-

@ArchTest                                         
public static final ArchRule archRule = noFields()
.that()                                        
.areAnnotatedWith(Autowired.class)             
.should()                                      
.haveNameEndingWith("Impl");                  

这可以包含在你的测试中,你必须阅读arch单元的文档,并在你的工作环境中进行设置,它非常容易集成。您可以为setter或方法创建类似的规则-

@ArchTest                                                         
public static final ArchRule archRule1 = noMethods()               
.that()                                                       
.areAnnotatedWith(Autowired.class)                       
.should()                                                     
.haveRawParameterTypes(MyServiceImpl.class, MyDaoImpl.class);

当您将其集成到代码库中时,您可以为自己创建自定义规则。将ArchUnit用于春季项目通常是一个好主意,它们也有预先配置的模板,您可以在项目中使用。希望这能有所帮助。

除了@VyomYdv上面的回答之外,我在春季偶然发现了这个github问题:https://github.com/spring-projects/spring-boot/issues/8277

本质上,一个用@Validated@ConfigurationProperties注释并实现接口的bean将在运行时成为JDK代理实例的候选者,并且你不能自动连接类实例,你必须自动连接作为接口。

因此,修改YyomYdv的规则如下:

public static final ArchRule AUTOWIRE_PROXY_RULE = noFields()
.that()
.areAnnotatedWith(Autowired.class)
.should()
.haveRawType(new DescribedPredicate<>("that does not autowire an interface") {
@Override
public boolean apply(final JavaClass input) {
return !input.isInterface();
}
})
.andShould().haveRawType(new DescribedPredicate<>("class is not a JDK proxy candidate") {
@Override
public boolean apply(final JavaClass input) {
return !input.getAllRawInterfaces().isEmpty()
&& input.getPackageName().startsWith("com.mypackage")
&& input.isAnnotatedWith(Validated.class)
&& input.isAnnotatedWith(ConfigurationProperties.class);
}
});

相关内容

最新更新