Spring Boot 入门教程

【注】本文译自:https://springframework.guru/best-practices-for-dependency-injection-with-spring/

  在本文中,我将向你展示如何在 Spring Framework 的依赖项注入中使用 Project Lombok 以获得最佳实践。   Spring 框架本身具有多种执行依赖项注入的方式。选项的灵活性是 Spring 框架的优势。但是,并非所有的依赖项注入选项都被视为最佳实践。有些实际上不太好。

依赖注入示例

  我提供了一些设置示例,供我们查看必须使用的各种依赖项注入选项。   让我们以 Spring Service 为例。就我们的目的而言,该服务具有一种返回字符串的方法。我们将使用“service”,并使用 Spring将其注入一些模拟控制器中。请记住,我们只是在探索如何使用 Spring Framework 进行依赖项注入。

示例 Service

public class MyService {
    public String getHello(){
        return "Hello";
    }
}

  我们的 Field 拥有一项公有属性的服务。我们可以注解该字段,Spring 将注入该服务的实例。

属性注入

Field Controller

@Controller
public class FieldController {
    @Autowired
    MyService myService;
    public String saySomething(){
        return myService.getHello();
    }
}

  这只是一个公有属性,没有 setter。显然,这不是一个好的实践,不推荐这样做。   我们可以对此进行一些改进,将该字段的访问权限设为私有。Spring Framework 确实允许你自动装配私有字段。你确实看到有人这样做。Spring 将执行一些反射魔术来执行依赖项注入

私有 Field Controller

public class PrivateFieldController {
    @Autowired
    private MyService myService;
    public String saySomething(){
        return myService.getHello();
    }
}

  尽管只使用私有字段比较好,但是测试却成了一个令人头痛。你要么需要启动 Spring Context,要么使用一些 Spring 实用程序来执行依赖注入以进行测试。不是世界末日,但总的来说很烦人。   我们可以通过为私有属性提供 setter 来改善这一点。Getter 和 Setter 通常被认为是面向对象编程中的最佳实践。通过注解 setter 方法,指示 Spring 使用 setter 进行依赖项注入很简单。

方法注入

Setter Controller

@Controller
public class SetterController {
    private MyService myService;
    @Autowired
    public void setMyService(MyService myService) {
        this.myService = myService;
    }
    public String saySomething(){
        return myService.getHello();
    }
}

  这是使用私有字段时的明显改进。有人会抱怨这要写太多代码。但是实际上,自 South Park 的第一季以来,此类任务已在现代 IDE 中实现了自动化。

构造函数注入

  下一个选项是使用构造函数。到目前为止,这是我们研究过的最佳方法。 使用构造函数设置注入的属性时,不必提供自动装配注释。这是一个很好的功能,可以节省一些键入时间。从 Spring Framework 版本4.2开始,用于依赖项注入的构造函数的注释是可选的。

Constructor Controller

@Controller
public class ConstructorController {
    private MyService myService;
    public ConstructorController(MyService myService) {
        this.myService = myService;
    }
    public String saySomething(){
        return myService.getHello();
    }
}

  基于构造函数的依赖注入无疑被认为是最佳实践。曾经有一段时间我个人偏爱基于 setter 的注入,但是后来又转向基于构造函数的注入。   我们仍然可以改善我们的示例。现在有两个主要问题。 第一,我们的服务类型是具体类型。硬类型的依赖注入不是最佳实践。   第二个问题是,我们要注入的属性未声明为final。因此,从理论上讲,该类可以在实例化注入的属性后对其进行修改。依# 赖注入最佳实践   依赖项注入的最佳实践是利用接口,构造函数和 final 属性。   我已经设置了“最佳实践”服务接口,并提供了服务实现—使用了 Spring Service 注解。

最佳实践服务接口

public interface BpService {
    String getHello();
}

最佳实践服务实现

@Service
public class BpServiceImpl implements BpService {
    @Override
    public String getHello() {
        return "The Best Hello!";
    }
}

使用 Project Lombok

  现在,使用 Project Lombok 进行依赖注入的最佳实践的秘诀在于:

  • 声明一个 final 属性接口
  • 为类添加 Project Lomboc 注解 @RequiredArgsConstructor

  现在,Project Lombok 将为声明为 final 的所有属性生成一个构造函数。Spring 会自动使用 Lombok 提供的构造函数来自动装配该片段。

Lombok Controller

@RequiredArgsConstructor
@Controller
public class BpFinalConstructorController {
    private final BpService bpService;
    public String saySomething(){
        return bpService.getHello();
    }
}

  这是执行此操作的真正好方法。您的代码保持非常干净。使用 Spring时,通常需要多个自动装配属性。   当您需要添加另一个 bean 时,只需声明一个 final 属性。   如果您重构并且不再需要 Spring 托管的依赖项,则只需删除 final 属性。   你不再需要维护设置器或构造函数代码。Project Lombok 减轻了您的日常工作。   我在日常编码中一直使用这种技术。绝对是节省时间。并导致更干净的代码。未使用的属性和未使用的构造函数参数已一去不复返了。重构现在不那么痛苦了!   本文的源代码可在 GitHub 上找到。

【注】本文译自:https://www.tutorialspoint.com/spring_boot/spring_boot_service_registration_with_eureka.htm

  本文将带你深入学习如何将 Spring Boot 微服务应用注册到 Eureka 服务器中。在注册应用前,请确保 Eureka Server 已经运行在 8761 端口或者先构建 Eureka 服务器并运行起来。有关搭建 Eureka 服务器的信息,可以参考本系列教程的相关部分。
  首先,你需要在构建配置文件中加入以下依赖,以注册微服务到 Eureka 服务器。
  Maven 用户可以加上下面的依赖到 pom.xml 文件:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

  Gradle 用户可以加上下面的依赖到 build.gradle 文件:

compile('org.springframework.cloud:spring-cloud-starter-eureka')

  现在,我们需要在 Spring Boot 应用类文件中加上 @EnableEurekaClient 注解。@EnableEurekaClient 注解可以使你的 Spring Boot 应用作为 Eureka 客户端。
  主 Spring Boot 就用如下所示:

package com.tutorialspoint.eurekaclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class EurekaclientApplication {
   public static void main(String[] args) {
      SpringApplication.run(EurekaclientApplication.class, args);
   }
}

  要注册 Spring Boot 应用到 Eureka 服务器中,我们要加上以下配置到 application.properties 或 application.yml 文件,并指定 Eureka 服务器的 URL。
  application.yml 文件的代码如下:

eureka:
  client:
     serviceUrl:
        defaultZone: http://localhost:8761/eureka
     instance:
     preferIpAddress: true
spring:
  application:
     name: eurekaclient

  application.properties 文件的代码如下:
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
eureka.client.instance.preferIpAddress = true
spring.application.name = eurekaclient
  现在,在主 Spring Boot 应用中加上 Rest 端点以返回字符串,在构建配置文件中要加上相应的应用描述。示例代码如下:

package com.tutorialspoint.eurekaclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaclientApplication {
   public static void main(String[] args) {
      SpringApplication.run(EurekaclientApplication.class, args);
   }
   @RequestMapping(value = "/")
   public String home() {
      return "Eureka Client application";
   }
}

  整个配置文件如下:
  Maven – pom.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>eurekaclient</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>eurekaclient</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.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>
      <spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-eureka</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>

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

</project>

  Gradle – build.gradle

buildscript {
   ext {
      springBootVersion = '1.5.9.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.tutorialspoint'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
   mavenCentral()
}
ext {
   springCloudVersion = 'Edgware.RELEASE'
}
dependencies {
   compile('org.springframework.cloud:spring-cloud-starter-eureka')
   testCompile('org.springframework.boot:spring-boot-starter-test')
   compile('org.springframework.boot:spring-boot-starter-web')   
}
dependencyManagement {
   imports {
      mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
   }
}

  你可以使用 Maven 或 Gradle 命令创建可执行 executable JAR 文件并运行 Spring Boot 应用:
  Maven 命令如下:

mvn clean install

  在 “BUILD SUCCESS” 之后,你可以在 target 目录下找到 JAR 文件。
  Gradle 可以使用以下命令:

gradle clean build

  在 “BUILD SUCCESSFUL” 之后,你可以在 build/libs 目录下找到 JAR 文件。
  使用以下命令运行 JAR 文件:

java –jar <JARFILE>

  现在,应用已经在 Tomcat 8080 端口启动,且 Eureka 客户端应用已经被注册到 Eureka 服务器,如下所示:

  在 Web 浏览器中输入 URL http://localhost:8761/,可以看到 Eureka 客户端应用已经被注册到 Eureka 服务器。

  在 Web 浏览器中输入 URL http://localhost:8080/,可以看到 Rest 端点输出。

【注】本文译自: https://www.tutorialspoint.com/spring_boot/spring_boot_eureka_server.htm

  Eureka 服务器是一个应用,它包含所有客户端服务应用的信息。每个微服务都会注册到 Eureka 服务器并且 Eureka 服务器知道所有客户端应用的端口和 IP 地址。Eureka 服务器也被称为发现服务器。
  本文将带你学习如何搭建 Eureka 服务器。搭建 Eureka 服务器
  Eureka 服务器与 Spring Cloud 打包发布。基于此,我们需要开发 Eureka 服务器并将它运行于缺省的 8761 端口上。
  访问 Spring 初始化器主页 https://start.spring.io/ 并下载 Spring Boot 工程的 Eureka 服务器依赖。如下图所示:

  工程下载之后,在主 Spring Boot 应用类文件中,我们要加上 @EnableEurekaServer 注解。@EnableEurekaServer 注解可使你的 Spring Boot 应用用作 Eureka 服务器。
  主 Spring Boot 应用类文件如下所示:

package com.tutorialspoint.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaserverApplication {
   public static void main(String[] args) {
      SpringApplication.run(EurekaserverApplication.class, args);
   }
}

  确保你的构建配置文件中已经加入了 Spring cloud Eureka 服务器依赖。
  Maven 用户的依赖代码如下:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

  Gradle 用户的依赖代码如下:

compile('org.springframework.cloud:spring-cloud-starter-eureka-server')

  完整的构建配置代码文件如下所示:
Maven pom.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>eurekaserver</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>eurekaserver</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.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>
      <spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-eureka-server</artifactId>
      </dependency>

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

   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>

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

</project>

Gradle – build.gradle

buildscript {
   ext {
      springBootVersion = '1.5.9.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.tutorialspoint'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
   mavenCentral()
}
ext {
   springCloudVersion = 'Edgware.RELEASE'
}
dependencies {
   compile('org.springframework.cloud:spring-cloud-starter-eureka-server')
   testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
   imports {
      mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
   }
}

  缺省情况下,Eureka 服务器会将自身注册到发现中。你应当加入以下配置到 application.properties 或 application.yml 文件中:
application.properties 文件如下所示:

eureka.client.registerWithEureka = false
eureka.client.fetchRegistry = false
server.port = 8761

application.yml 文件如下所示:

eureka:
  client:
     registerWithEureka: false
     fetchRegistry: false
server:
  port: 8761

  现在可以使用 Maven 或 Gradle 命令创建可执行 executable JAR 文件并运行 Spring Boot 应用了:
  Maven 命令如下:

mvn clean install

  在 “BUILD SUCCESS” 之后,你可以在 target 目录下找到 JAR 文件。
  Gradle 可以使用以下命令:

gradle clean build

  在 “BUILD SUCCESSFUL” 之后,你可以在 build/libs 目录下找到 JAR 文件。
  使用以下命令运行 JAR 文件:

java –jar <JARFILE>

  应用已在 Tomcat 8761 端口启动,如下图所示:

  接下来,在 web 浏览器中单击 URL http://localhost:8761/,可以发现 Eureka 服务器已经运行在 8761 端口,如下所示:

【注】本文译自: https://www.tutorialspoint.com/spring_boot/spring_boot_enabling_https.htm

  Spring Boot 应用默认以 HTTP 8080 端口启动。

  你要在 Spring Boot 应用中执行以下步骤来配置 HTTPS 和端口 443:

  • 获取 SSL 证书:创建自签名证书或者从一个证书认证机构申请一个。
  • 启用 HTTPS 和 443 端口

    自签名证书

      Java 运行时环境已经内置了证书管理实用工具,可用于创建自签名证书。示例代码如下:

keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
Enter keystore password:
   Re-enter new password:
   What is your first and last name?
   [Unknown]:
   What is the name of your organizational unit?
   [Unknown]:
   What is the name of your organization?
   [Unknown]:
   What is the name of your City or Locality?
   [Unknown]:
   What is the name of your State or Province?
   [Unknown]:
   What is the two-letter country code for this unit?
   [Unknown]:
   Is CN = Unknown, OU=Unknown, O = Unknown, L = Unknown, ST = Unknown, C = Unknown correct?
   [no]: yes

  这段代码将产生一个名为 keystore.p12 的PKCS12 keystore 文件,证书别名为 tomcat。

配置 HTTPS

  我们要在 application.properties 文件中写入:服务端口为 443、key-store 文件路径、key-store-password、key-store-type 和 密钥别名。如下所示:

server.port: 443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: springboot
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat

  如果你使用 YAML 属性的话,你也可以使用下面的 application.yml 的代码:

server:
  port: 443
  ssl:
     key-store: keystore.p12
     key-store-password: springboot
     keyStoreType: PKCS12
     keyAlias: tomcat

  现在可以使用 Maven 或 Gradle 命令创建可执行 executable JAR 文件并运行 Spring Boot 应用了:
  Maven 命令如下:

mvn clean install

  在 “BUILD SUCCESS” 之后,你可以在 target 目录下找到 JAR 文件。
  Gradle 可以使用以下命令:

gradle clean build

  在 “BUILD SUCCESSFUL” 之后,你可以在 build/libs 目录下找到 JAR 文件。
  使用以下命令运行 JAR 文件:

java –jar <JARFILE>

  应用已在 Tomcat 443 端口启动,如下图所示:

【注】本文译自: https://www.tutorialspoint.com/spring_boot/spring_boot_scheduling.htm

  调度用来处理特定时间周期的任务。Spring Boot 为 Spring 应用编写调度器提供了良好的支持。

Java Cron 表达式

  Java Cron 表达式用于配置 CronTrigger 实例,是 org.quartz.Trigger 的子类。关于 Java cron 表达式的更多信息可参考:https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm
  @EnableScheduling 注解用于使你的应用能够使用调度器。这个注解应当被加在主 Spring Boot 应用类文件中。

@SpringBootApplication
@EnableScheduling

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

  @Scheduled 注解用于触发一个特定时间周期的调度器。

@Scheduled(cron = "0 * 9 * * ?")
public void cronJobSch() throws Exception {
}

  以下代码展示了如何在每天的早上 9:00 到 9:59 之间每分钟执行任务:

package com.tutorialspoint.demo.scheduler;

import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class Scheduler {
   @Scheduled(cron = "0 * 9 * * ?")
   public void cronJobSch() {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
      Date now = new Date();
      String strDate = sdf.format(now);
      System.out.println("Java cron job expression:: " + strDate);
   }
}

  以下截图展示了应用在 09:03:23 启动之后如何每隔一分钟执行一次:

固定频度

  固定频度调度器被用于在特定时间执行任务。它不等待前一个任务完成,时间单位为毫秒。示例代码如下:

@Scheduled(fixedRate = 1000)
public void fixedRateSch() { 
}

  以下代码示例是应用启动后的每秒钟执行一个任务:

package com.tutorialspoint.demo.scheduler;

import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class Scheduler {
   @Scheduled(fixedRate = 1000)
   public void fixedRateSch() {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

      Date now = new Date();
      String strDate = sdf.format(now);
      System.out.println("Fixed Rate scheduler:: " + strDate);
   }
}

  观看以下截屏,可以看出应用在 09:12:00 启动后以每隔一秒钟的固定频度执行任务:

固定延迟

  固定延迟调度器用于在指定时间执行任务。它应当等待上一个任务完成,单位为毫秒。示例代码如下:

@Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void fixedDelaySch() {
}

  这里,initialDelay 是在初始化之后到首次执行间的延迟值。
  下面的例子中,是从应用启动完成后 3 秒后执行每秒一次的任务:

package com.tutorialspoint.demo.scheduler;

import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class Scheduler {
   @Scheduled(fixedDelay = 1000, initialDelay = 3000)
   public void fixedDelaySch() {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
      Date now = new Date();
      String strDate = sdf.format(now);
      System.out.println("Fixed Delay scheduler:: " + strDate);
   }
}

  下面看到的截屏显示的是应用在 09:18:39 启动完成 3 秒后,固定延迟调度器任务每秒执行一次的情况。