Spring Boot @Autowired -CheckStyle / PMD / Spotbugs规则在非接口使用情况下警告

发布于 2025-01-21 02:28:24 字数 895 浏览 1 评论 0 原文

最近被 Spring 咬住了Spring Boot 应用程序中的 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
}

Having recently been bit by Spring CGLib vs JDK Dynamic proxying issues in a Spring Boot application (that when run on a Mac seems to use CGLib where as the same app when run on Linux is using JDK dynamic proxying).

I'm looking for a linter tool config / plugin (PMD, Checkstyle, Spotbugs etc) that can identify where an @Autowired annotated field / parameter is being used and the Class being autowired is defined with a class instance rather than an interface.

For example:

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绝對不後悔。 2025-01-28 02:28:24

我认为这是不可能与PMD,CheckStyle,Spotbugs等进行检查。但是有一个解决方案可以检查此问题。
Archunit 提供编写自定义规则,可以为此编写自定义规则。假设您使用Junit5或Junit4进行测试,则可以做类似的事情 -

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

可以将其纳入您的测试中,您必须阅读ARCH单元的文档并在工作环境中设置,它非常容易集成。您可以为设定器或方法创建类似的规则 -

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

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

I don't think this will be possible to check with PMD, Checkstyle, Spotbugs, etc. But there is a solution to check this.
ArchUnit offers writing custom rules, a custom rule can be written for this. Assuming you are using JUnit5 or JUnit4 for testing, you could do something like-

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

This can be incorporated within your tests, you would have to read the documentation of arch unit and set up in your work environment, it super easy to integrate. You can create similar rules for setters or methods-

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

As you integrate this into your code base, you can create custom rules for yourself. Using ArchUnit for spring projects is generally a great idea, they also have pre configured templates that you can use in your project. Hope this helps.

刘备忘录 2025-01-28 02:28:24

除了上面的 @vyomydv的答案外,我还偶然发现了春季的这个问题:

本质上是用 @validated @configurationproperties 和实现接口将是在运行时成为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);
        }
    });

In addition to @VyomYdv's answer above, I stumbled on this github issue for spring: https://github.com/spring-projects/spring-boot/issues/8277

Essentially a bean that is annotated with @Validated and @ConfigurationProperties and also implements an interface will be a candidate for being a JDK proxy instance at runtime, and you can't autowire the class instance, you have to autowire as the interface.

So amend YyomYdv's rule as follows:

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);
        }
    });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文