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 91011
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.properties
、application.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