➜ Old React website
Chung Cheuk Hang MichaelJava Web Developer
Java 開發筆記(五)Java 開發筆記(三)

Java 開發筆記(四)

Continued from previous post:
Java 開發筆記(三)

Table of contents

4 Spring 基礎知識

4.1 簡介 - 認識 Spring 框架

最熱門、最強大既 Java framework,沒有「之一」。任何一份 Java developer report 都會見到 Spring framework 係 dominate 左個 market。
佢最重要既功能為:
  • 用 container 管理 application 既 beans(尤其是 singleton beans,即係有啲 class 喺成個 application 裡面只有一個 instance)
  • Dependency injection 依賴注入(透過使用 annotations 或 XML config)
  • Aspect-oriented programming (AOP),即係以不同切面去介入,簡單講就係當我地想監控某特定 packages 既 Spring beans 既 public methods, 當佢地一被 call 就落 log,log 低 parameter values 同 return values,方便日後 debug/troubleshoot 用,就可以用 AOP 實現。 好處係咁樣做就唔需要任何 inheritance 或者 每個 class 每個 target method 都要 自己寫一堆 code 去做 logging。 AOP 亦都被 Spring framework 自身好多功能所使用。
  • 寫 backend web server 用(Maven 既 spring-boot-starter-web dependency)
  • 整合 container(i.e. Tomcat),不必再拎住個 JAR 或 WAR file,再額外下載 container program 黎 deploy 個 web app。
用曬 annotation(如 @Component),好多野用曬 declarative programming 而唔係 imperative programming。

4.2 Spring 既項目

Spring 既項目眾多,包括:
  • Spring core framework(包括 Spring Web MVC)
  • Spring Boot
  • Spring Data
  • Spring Cloud
  • Spring Security
  • Spring Session
  • Spring Integration
  • Spring HATEOAS
  • Spring REST Docs
  • Spring Batch
  • Spring AMQP
  • Spring for Apache Kafka
  • Spring LDAP

4.3 Spring Boot 與 microservice 微服務背景

作為一個 back-end application,我地想以最快最簡單既方法去設計 HTTP APIs 畀 client 去使用。
有別於舊派做法,which is 將 front-end 同 Spring Web MVC 既 back-end 結合埋一齊,同時喺同一個 code base 裡面, 亦作為一個整體 couple 埋一齊去 deploy,我地而家用 Spring Boot 會想做到前後端分離(separating front-end and back-end), back-end 只負責提供 HTTP APIs 畀 front-end 去使用,而 front-end 將會係另一個獨立既 project,放喺唔同既 Git 或 SVN repository。
我地更可以做到將一個大既 back-end 以 business domain 去拆分,每一個 Spring Boot microservice 交由一個獨立既 development team 負責。 而上游 microservice development team 可以用 API contract 去話畀下游 microservice development team 知道點樣用佢既 HTTP APIs(endpoints)。
至於 DB,最好係每個 microservice application 各自有一個獨立既 DB,table schema 都係只有該 domain 既野。
Clients 可以包括:
  • 另一個 domain 既 back-end microservice application
  • Front-end web application(如 Angular、React、Vue 等 web projects)
HTTP APIs 一般都以 JSON 格式去交換資訊。以下為一個典型例子:
1{ 2 "departments": [ 3 { 4 "name": "dev", 5 "staff": [ 6 { 7 "name": "Ben", 8 "salary": 50000.0 9 }, 10 { 11 "name": "Ivan", 12 "salary": 45000.0 13 } 14 ] 15 } 16 ] 17}

4.4 Spring Boot 開發簡介

4.4.1 基本 project structure

一般至少會有以下 packages(或類似名):
  • <root package>
    • controller
    • entity
    • repository
    • service

4.4.2 基本 class 種類

以下主要幾類 classes,佢地幾時 construct(new)、被 dependency inject 到第啲 beans,都係交由 Spring 負責,所以又可以叫做 Spring beans。
  • Controller layer
    • 啲 classes 會被 annotate @Controller@RestController
    • 用黎 expose HTTP APIs(endpoints)
    • 增減 classes/methods 可以增減 HTTP APIs
    • 一般為 singleton
  • Service layer
    • 啲 classes 會被 annotate @Service,甚或 @Transactional 黎增添事務處理去確保 DB 既 ACID
    • 用黎放置 business logic
    • 會 depends on 各個 repositories
    • 增減 classes/methods 可以增減 business 情境
    • 一般為 singleton
  • Repository layer
    • 啲 classes 會被 annotate @Repository
    • 用黎同 DB 溝通
    • 增減 classes 可以增減 tables 既 CRUD,一般黎講至少每個 entity 都會有對應既一個 repository
    • 增減 methods 可以增減對應 table 既 CRUD SQLs(參考
    • 一般為 singleton
  • Entity
    • 啲 classes 會被 annotate @Entity@Table
    • 用黎做同 DB 之間既 Object-Relational Mapping (ORM),class 就對應返 DB 既 table,property 就對應返該 table 既 columns
    • 增減 classes 可以增減 table 既 ORM
    • 增減 properties 可以增減 table 既 column mappings

4.4.3 基本 Spring Boot classes 既例子

4.4.3.1 Controller 既例子

1@RestController 2@RequestMapping("sales") 3public void SalesController { 4 5 @Autowired 6 SalesService salesService; 7 8 /** 9 * <p>Endpoint:/sales/total</p> 10 * <p>將返回 JSON 格式:{ "salesTotal": 1000.00 }</p> 11 */ 12 @GetMapping("total") 13 public Map<String, Object> getTotal() { 14 final Map<String, Object> result = new HashMap<>(); 15 result.put("salesTotal", salesService.calculateTotal()); 16 return result; 17 } 18}

4.4.3.2 Service 既例子

1@Service 2public class SalesService { 3 4 @Autowired 5 SalesRepository salesRepository; 6 7 public BigDecimal calculateTotal() { 8 return salesRepository.findAll() 9 .stream() 10 .map(Sales::getAmount) 11 .reduce(BigDecimal.ZERO, BigDecimal::add); 12 } 13}

4.4.3.3 Repository 既例子

@Repository public interface SalesRepository extends JpaRepository<Sales, Long> { List<Sales> findAll(); }

4.4.3.4 Entity 既例子

1@Entity 2@Table(name = "sales") 3public class Sales { 4 5 @Id 6 @GeneratedValue(strategy = IDENTITY) 7 @Column(name = "id") 8 private Long id; 9 10 @Column(name = "amount") 11 private BigDecimal amount; 12 13 public Long getId() { 14 return id; 15 } 16 17 public void setId(Long id) { 18 this.id = id; 19 } 20 21 public BigDecimal getAmount() { 22 return amount; 23 } 24 25 public void setAmount(BigDecimal amount) { 26 this.amount = amount; 27 } 28}

4.5 Application 配置

Spring Boot 啟動既時候,會讀取特定既配置檔,如 application.propertiesapplication.yml 或包含環境名既 application-dev.yml
由於 Spring Boot 已經整合左 Tomcat container,所有 Tomcat 相關既配置都需要放喺上述檔案。 呢個 application 行喺咩 port 位都一樣。
另外,如果需要連接 DB,相關既配置如 JDBC connection URL、username、password 以及用既 driver class 都需要喺上述檔案列出。
application.yml 例子:
1server: 2 port: 8080 3 4# MySQL 5spring: 6 datasource: 7 url: jdbc:mysql://localhost:3306/learnjpa?useSSL=false 8 username: root 9 password: 10 driver-class-name: com.mysql.cj.jdbc.Driver 11 tomcat: # Spring Boot 1 12 test-while-idle: true 13 validation-query: SELECT 1 14 time-between-eviction-runs-millis: 360000 15 hikari: # Spring Boot 2、3 16 max-lifetime: 30000 17 jpa: 18 show-sql: true 19 open-in-view: false 20 hibernate: 21 ddl-auto: update 22 properties: 23 hibernate: 24 dialect: org.hibernate.dialect.MySQLDialect 25 jackson: 26 default-property-inclusion: non_null 27 serialization: 28 indent-output: true 29 fail-on-empty-beans: false 30 write-dates-as-timestamps: true 31 visibility.field: any 32 visibility.getter: none 33 visibility.setter: none 34 visibility.is-getter: none 35 36# SQL Server 37spring: 38 datasource: 39 url: jdbc:sqlserver://localhost:1433;databaseName=learnjpa 40 username: michael 41 password: 42 driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver 43 jpa: 44 show-sql: true 45 open-in-view: false 46 hibernate: 47 ddl-auto: update 48 properties: 49 hibernate: 50 dialect: org.hibernate.dialect.SQLServer2012Dialect 51 jackson: 52 default-property-inclusion: non_null 53 serialization: 54 indent-output: true 55 fail-on-empty-beans: false 56 write-dates-as-timestamps: true 57 visibility.field: any 58 visibility.getter: none 59 visibility.setter: none 60 visibility.is-getter: none 61 62# H2 63spring: 64 datasource: 65 url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4 66 username: sa 67 password: 68 driver-class-name: org.h2.Driver 69 h2: 70 console: 71 enabled: true 72 path: /h2-console 73 jpa: 74 show-sql: true 75 open-in-view: false 76 hibernate: 77 ddl-auto: update 78 properties: 79 hibernate: 80 dialect: org.hibernate.dialect.H2Dialect 81 jackson: 82 default-property-inclusion: non_null 83 serialization: 84 indent-output: true 85 fail-on-empty-beans: false 86 write-dates-as-timestamps: true 87 visibility.field: any 88 visibility.getter: none 89 visibility.setter: none 90 visibility.is-getter: none
註:實際情況下不應重覆配置,除非需要連接多過一個 database(需要特別處理)。
application.properties 例子:
1server.port=8080 2 3# MySQL 4spring.datasource.url=jdbc:mysql://localhost:3306/learnjpa?useSSL=false 5spring.datasource.username=root 6spring.datasource.password= 7spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 8# Spring Boot 1 9spring.datasource.tomcat.test-while-idle=true 10spring.datasource.tomcat.validation-query=SELECT 1 11spring.datasource.tomcat.time-between-eviction-runs-millis=360000 12# Spring Boot 2、3 13spring.datasource.hikari.max-lifetime=30000 14spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect 15 16# SQL Server 17spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=learnjpa 18spring.datasource.username=michael 19spring.datasource.password= 20spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver 21spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect 22 23# H2 24spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4 25spring.datasource.username=sa 26spring.datasource.password= 27spring.datasource.driver-class-name=org.h2.Driver 28spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect 29spring.h2.console.enabled=true 30spring.h2.console.path=/h2-console 31 32# Common 33spring.jpa.show-sql=true 34spring.jpa.open-in-view=false 35spring.jpa.hibernate.ddl-auto=update 36spring.jpa.properties.hibernate.show_sql=true 37spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 38 39# Jackson 40spring.jackson.default-property-inclusion=non_null 41spring.jackson.serialization.indent-output=true 42spring.jackson.serialization.fail-on-empty-beans=false 43spring.jackson.serialization.write-dates-as-timestamps=true 44spring.jackson.visibility.field=any 45spring.jackson.visibility.getter=none 46spring.jackson.visibility.setter=none 47spring.jackson.visibility.is-getter=none