Groovy + Spring - DI没有样板(构造函数)代码

时间:2017-11-04 17:23:37

标签: spring groovy

我创建了基于Groovy而不是Java的spring-boot项目。

现在我关注 @RestController

@RestController
class HelloRest {

    private final HelloService helloService

    @GetMapping("hello")
    String hello(@RequestParam("name") String name) {
        helloService.createHelloMessage(name)
    }
}

问题是如何注入

@Service
class HelloService {...} 

以最简单的方式避免样板(在本例中为构造函数)代码?

在Java中我会使用: @lombok.RequiredArgsConstructor ,实际上如果我在我的groovy项目中使用它也可以。

另一方面,例如 groovy.transform 中的 @Immutable 注释并不起作用,因为它实际上创建的不仅仅是单个构造函数。而Spring期望单个构造函数能够自动 @Autowired 依赖项。

到目前为止,我看到了两个解决方案:

  • 生成构造函数
  • 使用带有注释的lombok

是否有任何可以在这里使用的Groovy解决方案?

2 个答案:

答案 0 :(得分:1)

此时没有Groovy机制与@lombok.RequiredArgsConstructor做同样的事情。在您的情况下,主要问题是Groovy总是为所有当前已知的功能(如@Immutable注释)生成no-args默认构造函数。最接近(但不准确)的方法是使用@TupleConstructor,如:

@RestController
@TupleConstructor(includes = ['helloService'], includeFields = true, includeProperties = false, force = true)
class HelloRest {

    private final HelloService helloService

    @GetMapping("hello")
    String hello(@RequestParam("name") String name) {
        return helloService.createHelloMessage(name)
    }
}

这个Groovy代码将产生类似于这个Java代码的字节码:

@RestController
@TupleConstructor(
    includeFields = true,
    force = true,
    includeProperties = false,
    includes = {"helloService"}
)
public class HelloRest implements GroovyObject {
    private final HelloService helloService;

    public HelloRest(HelloService helloService) {
        CallSite[] var2 = $getCallSiteArray();
        MetaClass var3 = this.$getStaticMetaClass();
        this.metaClass = var3;
        this.helloService = (HelloService)ScriptBytecodeAdapter.castToType(helloService, HelloService.class);
    }

    public HelloRest() {
        CallSite[] var1 = $getCallSiteArray();
        this((HelloService)null);
    }

    @GetMapping({"hello"})
    public String hello(@RequestParam("name") String name) {
        CallSite[] var2 = $getCallSiteArray();
        return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
    }
}

除了生成的默认构造函数之外,它几乎就是您所需要的。

使用@Immutable注释时情况变得更加复杂,因为此版本将生成3个构造函数:

  • public HelloRest(HelloService helloService)
  • public HelloRest()
  • public HelloRest(HashMap args)

当然,在这种情况下,您必须在private final字段定义前删除HelloService,因为此AST转换仅适用于尚未最终的字段。

在这种情况下,您找到的两个选项(手动创建构造或使用Lombok)可能是解决您问题的最佳方案。

替代解决方案

还有一个"脏"允许您编写较少量代码的解决方案,但通过反射促进注入。请考虑以下代码:

@RestController
class HelloRest {

    @Autowired
    private final HelloService helloService

    @GetMapping("hello")
    String hello(@RequestParam("name") String name) {
        return helloService.createHelloMessage(name)
    }
}

它将生成类似于以下Java代码的字节码:

@RestController
public class HelloRest implements GroovyObject {
    @Autowired
    private final HelloService helloService;

    public HelloRest() {
        CallSite[] var1 = $getCallSiteArray();
        MetaClass var2 = this.$getStaticMetaClass();
        this.metaClass = var2;
    }

    @GetMapping({"hello"})
    public String hello(@RequestParam("name") String name) {
        CallSite[] var2 = $getCallSiteArray();
        return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
    }
}

即使只有一个默认构造函数甚至没有触及我们的helloService字段,Spring bean也会被反射注入。我分享这个选项只是为了显示所有选择,尽管你最初使用构造函数注入的本能是在实践中使用依赖注入的最佳方式。

答案 1 :(得分:0)

您可以在课堂上使用@Cannonical或@Immutable。这样就会为你创建构造函数