在接口默认方法中使用 spring 管理的 bean?

Rut*_*ulV 5 java spring java-8

警告:我意识到这可能是对 spring bean 和/或 Java 8 的默认接口方法背后意图的滥用。我正在寻找的是对为什么这可能是一种我未能认识到的不安全方法的具体且合理的批评。

我定义了一个类,它使我可以静态访问正在运行的应用程序上下文:

@Component
public class BeanAccessor implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static <T> T getSingleton(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }

    public static <T> T getSingleton(String beanName, Class<T> clazz){
        return applicationContext.getBean(beanName, clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanAccessor.applicationContext = applicationContext;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后我可以在默认接口方法中使用这些 BeanAccessor 方法,以便从方法内部访问 spring 管理的 bean。

我意识到可以通过实现其他类和服务来实现此功能,而不是觉得需要将它混合到当前的类和服务中(至少对于下面的示例,我实际上并没有对“this”做任何事情)。这在一定程度上是一种“艺术”偏好,我相信我会继续寻找其他用例。

请注意,我并没有像这里描述的那样尝试保留每个实例的状态https://kerflyn.wordpress.com/2012/07/09/java-8-now-you-have-mixins/我确实认识到存在危险在这样做。

这是一个示例用法:

public interface ClientAware {

    String TENANT_NAME = "TENANT_NAME";

    default ClientDetails clientDetails() {
        ClientDetailsService service = BeanAccessor.getSingleton(ClientDetailsService.class);
        return service.loadClientByClientId(SecurityUtil.getClientId());
    }

    default Map<String, Object> clientInfo() {
        return clientDetails().getAdditionalInformation();
    }

    default String tenant() {
        return (String) clientInfo().get(TENANT_NAME);
    }

}
Run Code Online (Sandbox Code Playgroud)

然后我使用该接口的实际类(以及另一个执行类似操作以向响应添加元信息的接口):

@RestController
@RequestMapping("/documents")
public class Documents implements WrapAware, ClientAware {

        @Autowired
        private DocumentService docService;

        @RequestMapping(method = RequestMethod.GET)
        public Object byPathAndTenant(@RequestParam("path") String path) {
            return ok(docService.getDocumentsByPathAndTenant(path, tenant()));
        }

}
Run Code Online (Sandbox Code Playgroud)

请注意,它确实正确解析了 bean,并且似乎可以正常工作。在我开始使用这样的模式之前,我想了解风险。

6to*_*ton 1

关于远离静力学的注意事项已经讨论过

在您的情况下,如果在初始化 spring 上下文之前引用接口,您可能会遇到问题。因此静态ClientDetailsService service将为空,导致 NPE。

通常,随着代码的增长,您可能会失去对应用程序启动期间何时引用此接口的控制。

  • 请使用“默认”*方法*。“默认”方法的存在不会改变“接口”的含义,因此不存在“默认接口”。如果您想要“静态”方法,您可以将“静态”方法添加到“接口”(这不会使其成为“静态接口”)。但是“默认”方法只是非“抽象”方法,就像具体“类”上的其他非“抽象”实例方法一样。 (2认同)