《Spring Boot in Action》【5. Groovy】

5. Groovy

Spring Boot CLI可以很方便地使用Groovy编写Spring应用程序。

5.2 创建CLI项目

首先创建一个项目目录:

mkdir readinglist

进去创建静态资源目录和Thymeleaf模板目录:

$ cd readinglist
$ mkdir static
$ mkdir templates

分别将之前的style.css和readingList.html放进去。

在根目录下添加Boot.groovy,无需访问修饰符、行末分号、getter和setter:

class Book {
  Long id
  String reader
  String isbn
  String title
  String author
  String description
}

还有一个区别是这里没有Spring Data JPA的注解,事实上我们将使用Spring的JdbcTemplate,而不是Spring Data JPA,因为JPA需要.class文件来实现repository接口,当你通过命令行运行Groovy脚本的时候,CLI在内存中编译脚本且不会生成.class文件,所以使用CLI开发的时候JPA不太好用。不过你可以使用CLI的jar命令打包成JAR文件,JAR包里面就有.class文件了,不过开发的时候为了方便,就用JdbcTemplate了。

接下来写ReadingListRepository接口:

interface ReadingListRepository {
  List<Book> findByReader(String reader)
  void save(Book book)
}

写对应的接口实现:

@Repository
class JdbcReadingListRepository implements ReadingListRepository {

  @Autowired
  JdbcTemplate jdbc

  List<Book> findByReader(String reader) {
    jdbc.query(
        "select id, reader, isbn, title, author, description from Book where reader=?",
        { rs, row ->
            new Book(id: rs.getLong(1),
                reader: rs.getString(2),
                isbn: rs.getString(3),
                title: rs.getString(4),
                author: rs.getString(5),
                description: rs.getString(6))
        } as RowMapper,
        reader)
  }

  void save(Book book) {
    jdbc.update("insert into Book " +
                "(reader, isbn, title, author, description) " +
                "values (?, ?, ?, ?, ?)",
        book.reader,
        book.isbn,
        book.title,
        book.author,
        book.description)
  }
}

创建schema.sql:

create table Book (
  id identity,
  reader varchar(20) not null,
  isbn varchar(10) not null,
  title varchar(50) not null,
  author varchar(50) not null,
  description varchar(2000) not null
);

创建ReadingListController.groovy:

@Controller
@RequestMapping("/")
class ReadingListController {

  String reader = "Craig"

  @Autowired
  ReadingListRepository readingListRepository

  @RequestMapping(method=RequestMethod.GET)
  def readersBooks(Model model) {
    List<Book> readingList = readingListRepository.findByReader(reader)
    if (readingList) {
      model.addAttribute("books", readingList)
    }
    "readingList"
  }

  @RequestMapping(method=RequestMethod.POST)
  def addToReadingList(Book book) {
    book.setReader(reader)
    readingListRepository.save(book)
    "redirect:/"
  }
}

创建Grabs.groovy:

@Grab("h2")
@Grab("spring-boot-starter-thymeleaf")
class Grabs {}

下面就可以运行了,到项目根目录:

$ spring run .

发生了什么?

当你用CLI运行应用的时候,CLI试图使用内置的Groovy编译器编译Groovy代码,不过因为有些类型不认识(如JdbcTemplate, Controller, RequestMapping),所以失败了,不过它没放弃,它知道JdbcTemplate可以通过Spring Boot JDBC starter得到,Spring MVC的类型可以通过Spring Boot web starter得到,所以它会从Maven仓库(默认是Maven Central)获取这些依赖。此时还是失败,因为没有import,不过CLI知道许多常用类型所在的包,它会添加到Groovy编译器的默认包列表,这时候如果没有别的语法错误,编译通过,并且CLI通过内部的启动方法来运行应用,此时,Spring Boot自动配置介入。

5.2 获取依赖

@Grab注解来自于Groovy’s Grape(Groovy Adaptable Packaging Engine or Groovy Advanced Packaging Engine),用法:

@Grab(group="com.h2database", module="h2", version="1.4.190")
@Grab("com.h2database:h2:1.4.185")

Spring Boot CLI扩展了@Grab,使它用起来更简洁,许多依赖都不用指定版本(版本根据CLI的版本来确定),对一些常用的依赖,甚至都不用指定group:

@Grab("com.h2database:h2")
@Grab("h2")

覆盖默认依赖版本

Spring Boot引入了@GrabMetadata注解:

@GrabMetadata(“com.myorg:custom-versions:1.0.0”)

如上,它会从Maven仓库的com/myorg目录加载一个名为custom-versions.properties的属性文件,每行的键是group ID和module ID,值是版本号:

com.h2database:h2=1.4.186

Spring IO platform提供了一套兼容的依赖版本集,它是Spring Boot依赖版本集的超集:

@GrabMetadata('io.spring.platform:platform-versions:1.0.4.RELEASE')

添加依赖仓库

@GrabResolver注解可以添加依赖仓库,比如:

@GrabResolver(name='jboss', root='https://repository.jboss.org/nexus/content/groups/public-jboss')

5.3 运行测试

CLI提供了test命令来运行测试,测试可以放在项目的任何位置,不过建议放在一起,比如tests目录下:

$ mkdir tests

创建ReadingListControllerTest.groovy:

import org.springframework.test.web.servlet.MockMvc
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import static org.mockito.Mockito.*

class ReadingListControllerTest {

  @Test
  void shouldReturnReadingListFromRepository() {
    List<Book> expectedList = new ArrayList<Book>()
    expectedList.add(new Book(
        id: 1,
        reader: "Craig",
        isbn: "9781617292545",
        title: "Spring Boot in Action",
        author: "Craig Walls",
        description: "Spring Boot in Action is ..."
    ))

    def mockRepo = mock(ReadingListRepository.class)
    when(mockRepo.findByReader("Craig")).thenReturn(expectedList)

    def controller = new ReadingListController(readingListRepository: mockRepo)
    MockMvc mvc = standaloneSetup(controller).build()
    mvc.perform(get("/"))
        .andExpect(view().name("readingList"))
        .andExpect(model().attribute("books", expectedList))
  }
}

运行测试:

$ spring test tests/ReadingListControllerTest.groovy

如果有多个测试,你也可以只提供一个目录:

$ spring test tests

如果你更倾向于写Spock测试,而不是JUnit,test命令也可以执行Spock测试:

import org.springframework.test.web.servlet.MockMvc
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import static org.mockito.Mockito.*

class ReadingListControllerSpec extends Specification {

  MockMvc mockMvc
  List<Book> expectedList

  def setup() {
    expectedList = new ArrayList<Book>()
    expectedList.add(new Book(
      id: 1,
      reader: "Craig",
      isbn: "9781617292545",
      title: "Spring Boot in Action",
      author: "Craig Walls",
      description: "Spring Boot in Action is ..."
    ))

    def mockRepo = mock(ReadingListRepository.class)
    when(mockRepo.findByReader("Craig")).thenReturn(expectedList)

    def controller = new ReadingListController(readingListRepository: mockRepo)
    mockMvc = standaloneSetup(controller).build()
  }

  def "Should put list returned from repository into model"() {
    when:
      def response = mockMvc.perform(get("/"))

    then:
      response.andExpect(view().name("readingList"))
              .andExpect(model().attribute("books", expectedList))
  }
}

5.4 创建可部署的包

在项目根目录下,执行:

$ spring jar ReadingList.jar .

然后就可以运行了:

$ java -jar ReadingList.jar
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值