Spring启动教程

1.简介

如果您一直想使用一个Web框架,它使您能够快速开始API开发,而无须设置Web服务器,收集所有有线依赖项,安装各种工具的麻烦,那么您将拥有一个出色的框架,即Spring开机 Spring Boot的主要座右铭是约定优于配置

在本课程中,我们将研究设置Spring Boot项目并提供一些RESTful服务在不到20分钟的时间内与数据库进行交互的难易程度! 确实,Spring Boot就是这么简单。 Spring Boot使我们能够制作可以“运行”的基于生产级的基于Spring的应用程序。 Spring Boot应用程序最好的部分是它只需要很少的配置。

2.使用Maven制作Spring Boot项目

我们将使用许多Maven原型之一为我们的示例创建一个示例项目。 要创建项目,请在将用作工作空间的目录中执行以下命令:

mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-SpringBoot-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

如果您是第一次运行maven,则完成生成命令将花费几秒钟,因为maven必须下载所有必需的插件和工件才能完成生成任务。

创建项目后,请随时在您喜欢的IDE中打开它。 下一步是向项目添加适当的Maven依赖关系。 我们将在项目中处理四个依赖项:

  • spring-boot-starter-web :此依赖关系将该项目标记为Web项目,并且添加了依赖关系以创建控制器并创建与Web相关的类。
  • spring-boot-starter-data-jpa :此依赖关系为项目提供了JPA支持,我们可以在其中执行许多与数据库相关的操作
  • h2 :H2是一个内存数据库,在本课中我们将使用它来演示数据库操作
  • spring-boot-starter-test :此依赖项将所有与测试相关的JAR收集到项目中,例如JUnitMockito

这是带有适当依赖项的pom.xml文件:

pom.xml

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.10.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
</properties>

<dependencies>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>
  
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  
</dependencies>

要将代码打包到JAR中,我们将使用spring-boot-maven-plugin ,它是管理应用程序自身打包到JAR或WAR中的出色工具。 这是我们可以添加到依赖文件中的方法:

pom.xml

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Maven Central上找到最新的Maven依赖项。

最后,要了解添加此依赖项时添加到项目中的所有JAR,我们可以运行一个简单的Maven命令,当我们向其添加一些依赖项时,该命令使我们能够查看项目的完整依赖关系树。 这是我们可以使用的命令:

检查依赖树

mvn dependency:tree

当我们运行此命令时,它将向我们显示以下依赖关系树:

依赖树

[INFO] --------------------
[INFO] Building JCG-SpringBoot-example 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ JCG-SpringBoot-example ---
[INFO] com.javacodegeeks.example:JCG-SpringBoot-example:jar:1.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:1.5.10.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.10.RELEASE:compile
[INFO] |  |  |  +- ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO] |  |  |  |  \- ch.qos.logback:logback-core:jar:1.1.11:compile
[INFO] |  |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] |  |  |  \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
[INFO] |  |  \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] |  +- org.springframework.boot:spring-boot-starter-aop:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-aop:jar:4.3.14.RELEASE:compile
[INFO] |  |  \- org.aspectj:aspectjweaver:jar:1.8.13:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-jdbc:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.apache.tomcat:tomcat-jdbc:jar:8.5.27:compile
[INFO] |  |  |  \- org.apache.tomcat:tomcat-juli:jar:8.5.27:compile
[INFO] |  |  \- org.springframework:spring-jdbc:jar:4.3.14.RELEASE:compile
[INFO] |  +- org.hibernate:hibernate-core:jar:5.0.12.Final:compile
[INFO] |  |  +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile
[INFO] |  |  +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final:compile
[INFO] |  |  +- org.javassist:javassist:jar:3.21.0-GA:compile
[INFO] |  |  +- antlr:antlr:jar:2.7.7:compile
[INFO] |  |  +- org.jboss:jandex:jar:2.0.0.Final:compile
[INFO] |  |  +- dom4j:dom4j:jar:1.6.1:compile
[INFO] |  |  \- org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final:compile
[INFO] |  +- org.hibernate:hibernate-entitymanager:jar:5.0.12.Final:compile
[INFO] |  +- javax.transaction:javax.transaction-api:jar:1.2:compile
[INFO] |  +- org.springframework.data:spring-data-jpa:jar:1.11.10.RELEASE:compile
[INFO] |  |  +- org.springframework.data:spring-data-commons:jar:1.13.10.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-orm:jar:4.3.14.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-context:jar:4.3.14.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-tx:jar:4.3.14.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-beans:jar:4.3.14.RELEASE:compile
[INFO] |  |  +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] |  |  \- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile
[INFO] |  \- org.springframework:spring-aspects:jar:4.3.14.RELEASE:compile
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:1.5.10.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.5.10.RELEASE:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.27:compile
[INFO] |  |  |  \- org.apache.tomcat:tomcat-annotations-api:jar:8.5.27:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.27:compile
[INFO] |  |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.27:compile
[INFO] |  +- org.hibernate:hibernate-validator:jar:5.3.6.Final:compile
[INFO] |  |  +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] |  |  \- com.fasterxml:classmate:jar:1.3.4:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.10:compile
[INFO] |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO] |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.8.10:compile
[INFO] |  +- org.springframework:spring-web:jar:4.3.14.RELEASE:compile
[INFO] |  \- org.springframework:spring-webmvc:jar:4.3.14.RELEASE:compile
[INFO] |     \- org.springframework:spring-expression:jar:4.3.14.RELEASE:compile
[INFO] +- com.h2database:h2:jar:1.4.196:runtime
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:1.5.10.RELEASE:test
[INFO]    +- org.springframework.boot:spring-boot-test:jar:1.5.10.RELEASE:test
[INFO]    +- org.springframework.boot:spring-boot-test-autoconfigure:jar:1.5.10.RELEASE:test
[INFO]    +- com.jayway.jsonpath:json-path:jar:2.2.0:test
[INFO]    |  \- net.minidev:json-smart:jar:2.2.1:test
[INFO]    |     \- net.minidev:accessors-smart:jar:1.1:test
[INFO]    |        \- org.ow2.asm:asm:jar:5.0.3:test
[INFO]    +- junit:junit:jar:4.12:test
[INFO]    +- org.assertj:assertj-core:jar:2.6.0:test
[INFO]    +- org.mockito:mockito-core:jar:1.10.19:test
[INFO]    |  \- org.objenesis:objenesis:jar:2.1:test
[INFO]    +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO]    +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO]    +- org.skyscreamer:jsonassert:jar:1.4.0:test
[INFO]    |  \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO]    +- org.springframework:spring-core:jar:4.3.14.RELEASE:compile
[INFO]    \- org.springframework:spring-test:jar:4.3.14.RELEASE:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

注意到了什么? 通过向项目中添加四个依赖项,添加了如此多的依赖项。 Spring Boot本身会收集所有相关的依赖项,因此在此方面不做任何事情。 最大的优势在于, 所有这些依赖项都保证相互兼容

3.构建文件的等效Gradle

尽管Maven是一个出色的构建系统,但如果您更喜欢Gradle,则以下是pom.xml构建文件的Gradle等效项:

build.gradle

buildscript {
	ext {
		springBootVersion = '1.5.10.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'com.javacodegeeks.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
	mavenCentral()
}


dependencies {
	compile('org.springframework.boot:spring-boot-starter-data-jpa')
	compile('org.springframework.boot:spring-boot-starter-web')
	runtime('com.h2database:h2')
	testCompile('org.springframework.boot:spring-boot-starter-test')
}

我们展示了完全相同的依赖项和版本,以提供完全相同的依赖项。

4.项目结构

在继续进行并开始处理该项目的代码之前,让我们在此介绍一下将所有代码添加到项目后将拥有的projet结构:

Spring Boot项目结构

我们将项目分为多个包,以便遵循关注分离的原则,并且代码保持模块化。

5.定义实体

让我们看看如何做到这一点:

人.java

package com.javacodegeeks.example.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int age;

    //standard getters and setters
    
    @Override
    public String toString() {
        return String.format("Person{id=%d, name='%s', age=%d}", id, name, age);
    }
}

为了简洁起见,我们省略了标准的getter和setter方法,但是由于Jackson在对象的序列化和反序列化过程中使用它们,因此必须将它们制成。

@Entity注释将该POJO标记为对象,该对象将由Spring Data API管理,并且其字段将被视为表列(除非标记为transient )。

最后,我们为toString()方法添加了一个自定义实现,以便在测试应用程序时可以打印相关数据。

6.定义JPA存储库以访问H2数据库

JPA为我们提供了一种非常简单的定义JPA存储库接口的方法。

在了解如何定义JPA信息库之前,我们需要记住,只有在利用与JPA相关的功能时,才使每个JPA接口与数据库表的单个实体进行交互。 如果我们看一下接口定义,我们将对此有深刻的理解:

PersonRepository.java

package com.javacodegeeks.example.repository;

import com.javacodegeeks.example.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {  }

尽管上面的接口定义是空的,但我们仍然需要了解一些要点:

  • @Repository批注将此接口标记为Spring Bean,该Bean在应用程序启动时初始化。 有了这个注释,Spring可以很好地管理异常数据库的交互抛出
  • 我们使用Person作为参数来表示此JPA接口将管理Person实体
  • 最后,我们还传递了数据类型Long作为参数。 这表示Person实体包含一个唯一的标识符,该标识符的类型为Long

7.制作一个RESTful控制器

一个RESTful控制器,我们将应用程序的数据公开给客户端。 我们将使用GET,POST,PUT和DELETE等几个HTTP动词来支持与它们相关的功能。

首先,让我们定义一个PersonController类,该类标记为@RestController@RestController注释向Spring容器发出信号, @RestController中引发的任何异常都可以传递给客户端本身。 与存储库bean相比,这是不同的行为。

PersonController.java

package com.javacodegeeks.example.controller;

import com.javacodegeeks.example.model.Person;
import com.javacodegeeks.example.repository.PersonRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class PersonController {

    private final Logger LOG = LoggerFactory.getLogger(getClass().getName());

    private final PersonRepository personRepository;

    @Autowired
    public PersonController(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    ...
}

让我们通过添加执行实际操作的特定API为此Controller提供一些功能。

7.1使用POST插入数据

我们将首先添加一个API,通过该API我们可以将数据添加到H2数据库。 由于此方法在其@RequestBody接受一个Person对象,因此Person对象的JSON必须传递到请求中的API。

PersonController.java

@RequestMapping(value = "", method = RequestMethod.POST)
public Person createPerson(@RequestBody Person person) {
    LOG.info("Saving Person: {}.", person);
    personRepository.save(person);
    LOG.info("Person saved: {}.", person);
    return person;
}

我们使用Personrepository Bean访问一种方法,该方法在我们先前定义的用于处理Person Entity的JpaRepository接口中预定义。

在接下来的部分中,我们将在REST客户端Postman中尝试此API。

7.2构造一个GET API

现在我们有了将数据插入数据库的API,我们可以构造一个API以获取ID为ID的Person对象。 在这里, personId作为PathVariable传递:

PersonController.java

@RequestMapping(value = "/{personId}", method = RequestMethod.GET)
public Person getPerson(@PathVariable Long personId) {
    Person person = personRepository.findOne(personId);
    LOG.info("Got person from DB: {}.", person);
    return person;
}

为了获取数据库中存在的所有数据,我们将使用另一个GET API从数据库中获取所有数据:

PersonController.java

@RequestMapping(value = "/all", method = RequestMethod.GET)
public List<Person> getAllPerson() {
    List<Person> persons = personRepository.findAll();
    LOG.info("Getting all Data: {}.", persons);
    return persons;
}

它只是利用JPA Repository的findAll方法来获取数据库中Person所有数据并将其收集到一个List中。

7.3使用PUT更新数据

现在,我们将允许客户端更新数据库中的现有数据。 为此,我们再次使用save方法。 当save方法看到JSON对象中填充了id字段时,它首先找到具有该ID的Object,然后更新提供的字段。

PersonController.java

@RequestMapping(value = "", method = RequestMethod.PUT)
public Person editPerson(@RequestBody Person person) {
    LOG.info("Updating Person: {}.", person);
    personRepository.save(person);
    LOG.info("Person updated: {}.", person);
    return person;
}

7.4使用DELETE删除数据

现在,我们将完成最后的操作,即当一个人的ID作为PathVariable传递时删除数据:

PersonController.java

@RequestMapping(value = "/{personId}", method = RequestMethod.DELETE)
public void deletePerson(@PathVariable Long personId) {
    LOG.info("Deleting Person with ID {}.", personId);
    personRepository.delete(personId);
    LOG.info("Person deleted.");
}

8.包含请求拦截器

尽管并非严格要求这样做,但在此示例中我们还将包括一个请求拦截器。 通过请求拦截器,我们可以在请求对象到达控制器之前对其进行处理。 一旦Controller完成请求并返回响应,该响应对象将再次通过Request Interceptor。 让我们在这里定义请求拦截器:

JCGRequestInterceptor.java

package com.javacodegeeks.example.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JCGRequestInterceptor extends HandlerInterceptorAdapter {

    private final Logger LOG = LoggerFactory.getLogger(getClass().getName());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOG.info("Incoming request.");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LOG.info("Outgoing request.");
        super.postHandle(request, response, handler, modelAndView);
    }
}

9.使用Maven运行项目

在运行项目之前,我们还需要为项目定义一个主类。 这是Request Interceptor bean的主要类定义:

运行项目

package com.javacodegeeks.example;

import com.javacodegeeks.example.interceptor.JCGRequestInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class BootApp {

    public static void main(String[] args) {
        SpringApplication.run(BootApp.class, args);
    }

    @Bean
    public JCGRequestInterceptor requestInterceptor() {
        return new JCGRequestInterceptor();
    }
}

现在已经完成,我们可以运行我们的项目了。 使用maven可以轻松运行应用程序,只需使用以下命令:

运行项目

mvn spring-boot:run

现在该项目正在运行,我们可以使用Postman工具访问API,并查看它们是否按预期工作。

10.在Postman中访问API

我们将从使用我们制作的POST API将一些数据插入H2数据库开始:

将数据插入H2

现在,我们可以为该Person对象分配ID JPA,即1:

通过ID获取对象

我们还可以尝试获取所有数据API,以查看是否返回了此对象:

获取所有数据

让我们更新我们创建的对象的字段之一:

更新资料

最后,我们尝试通过将URL中的ID作为PathVariable传递来删除数据:

删除资料

11.包含和运行单元测试

没有至少一个单元测试用例,任何Spring应用程序都是不完整的。 在此示例中,我们将包括一个已准备就绪的单个单元测试用例。 它去了:

ControllerTests.java

package com.javacodegeeks.example;

import com.javacodegeeks.example.controller.PersonController;
import com.javacodegeeks.example.repository.PersonRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
public class ControllerTest {

    private MockMvc mockMvc;

    @InjectMocks
    private PersonController controller;

    @Mock
    private PersonRepository repository;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        controller = new PersonController(repository);
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void getAllPerson_Pass() throws Exception {
        mockMvc.perform(
                get("/all"))
                .andExpect(status().is2xxSuccessful());
    }
}

我们在上述示例测试用例中做了几件事。 让我们一次了解这些:

  • 我在@RunWith(SpringJUnit4ClassRunner.class)标记了@RunWith(SpringJUnit4ClassRunner.class)批注,该批注为测试用例提供了运行程序。
  • 使用Mockito时,需要启用其注释。 这是通过initMocks(...)方法调用完成的。
  • 我们使用了mockMvc对象的get mockMvc来检查/all GET API返回的状态码,并将其与任何2XX状态码进行比较。

当我们在IntelliJ中运行此测试用例时,我们看到以下输出:

运行Spring Boot测试用例

看到一个带有绿色的测试用例是一种很棒的感觉,不是吗?

12.使用Spring Boot CLI

Spring Boot命令行界面是一种软件,我们可以使用它从命令行运行和测试Spring Boot应用程序。 Spring Boot CLI在内部使用Spring Boot启动器和自动配置组件来收集所需的依赖关系并运行应用程序。

要开始使用CLI,最快的方法是下载ZIP并更改为bin目录,然后检查命令,如下所示:

下载和使用Spring CLI

要从任何地方使用Spring CLI,请将此JAR添加到PATH。

现在,让我们快速展示一下Spring Boot CLI的强大功能。 CLI可用于执行基于单个Groovy的nad运行脚本,而无需提供任何依赖关系。 我们将制作一个文件并将其命名为“ HelloWorld.groovy”。 请在此处注意文件扩展名,因为有必要使该文件仅是groovy类型的。 在文件中,我们将放置简单的代码片段:

HelloWorld.groovy

@RestController
class HelloWorld {
  @RequestMapping("/")
  String hello() {
    "Hello World!"
  }
}

现在,移至创建此脚本的文件夹,然后运行以下命令:

HelloWorld.groovy

spring run HelloWorld.groovy

此命令将在默认端口8080上运行上述Grovvy脚本。让我们现在尝试访问此端口:

CLI的Hello World

容易吗? 请注意,编写Groovy脚本时没有依赖关系,没有配置,也没有导入语句。 这是因为Spring Boot核心组件,Groovy编译器( groovyc )和Groovy Grape(Groovy的JAR依赖管理器)承担了这一责任。

13.结论

在本课程中,我们研究了使用Spring Boot构建生产级API的便捷性。 我们设法制作了一些功能齐全的API,这些API可以与数据库通信并可以运行单元测试用例。

我们在生产级RESTful客户端Postman中试用了API,并看到我们的API可以按预期响应调用。 我们在本课中使用的H2数据库很容易被MySQL,MongoDB或任何其他数据库之类的真实数据库替换。

14.下载源代码

在这个例子中,我们研究了如何开始一个基本的Spring Boot项目。

下载
您可以在此处下载此示例的完整源代码: JCG-SpringBoot-Example

翻译自: https://www.javacodegeeks.com/2018/04/spring-boot-tutorial.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值