最近在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);
}
});