---
# System prepended metadata

title: Spring核心理論
tags: [外訓, SP]

---

# Spring核心理論基礎

- 控制權轉置 IoC (Inversion of Control)
- 依賴注入 DI (Dependency Injection)
- 剖面導向程式設計AOP (Aspect Oriented Programming)

## Framework Reuse
### Template Method (Design Pattern)
核心概念:
- 共用的主流程寫在父類別 (template method)
- 可變動(客製化)部分挖空變成抽象方式(hook method)

Framework reuse 便是套用這個概念

### Framework Reuse vs Library Reuse
![](https://tomasp.net/blog/2015/library-frameworks/diagram.png)

- Library Reuse: 開發者控制主流程依需求使用library提供的function
- Framework Reuse: 框架決定主流程，開發者依需求實作callback function
![](https://nikestech.files.wordpress.com/2018/05/framework-vs-library.png?w=640)
- 主流程控制權由開發者移轉到框架=>所以是主流程控制權倒置(IoC)

### Application Framework

- 定義:可重用的半製品,客製化後成為一個可用的系統
    - High level (完成度高 彈性低 特定使用): ERP framework, Insurance framework
    - Low level (完成度低，彈性高 可開發任意應用): Spring framework, Hibernate
- 好處:
    - 共同領域抽象化機制
    - 節省開發時間 (不用從底層實作)
    - **應用程式整體有一致性** (每個人風格寫法不同，日後解讀維護困難)
- Template Method: 
    - 內含主流程的父類別=>framework
    - 實作hook method子類別=>element，可抽換不同element

## Dependency Injection


將物件控制權從主程式反轉給Spring Container，由它負責物件的建立，銷毀並管理物件間的關聯性

### Dependency
- 狹隘的dependency
```mermaid
classDiagram 
classK ..> classL : Dependency
```

- 廣義的dependency
```mermaid

classDiagram

classA --|> classB : Inheritance
classM ..|> classN : Realization
classG --> classH : Association
classE --o classF : Aggregation
classC --* classD : Composition
classK ..> classL : Dependency
```  
- 依賴方式:
    - parameter
    - local variable
    - global variable

參考：[快速搞懂 UML 類別圖 6 種關係線（ Dependency、Association、Aggregation、Composition、Generalization、Realization ）](https://dongstudio.medium.com/%E5%BF%AB%E9%80%9F%E6%90%9E%E6%87%82-uml-%E9%A1%9E%E5%88%A5%E5%9C%96-6-%E7%A8%AE%E9%97%9C%E4%BF%82%E7%B7%9A-dependency-association-aggregation-composition-generalization-realization-bf0670370c3d)

### Dependency 對程式碼可重用性的影響
- Dependency和程式碼可重用性是反比關係
- Liskov代換法則：降低Dependency的手法
    - 將Client與實作類別中間用Interface隔開，去除Client與實作類別的依賴性
    - **Interface保持簡單及穩定**

### Spring 如何降低依賴性
- 提倡以POJO為基礎程式設計
>POJO (Plain Old Java Object): 普通的Java物件，不受任何特殊條件限制
- Spring在執行時期會作為元件的中間人將元件間依賴關係注入
- Spring透過AOP將特定的技術邏輯（非功能面需求 ex: log, transaction）注入元件中，而不是透過傳統的繼承方式(EJB)

### 物件生成與相依關係建立
- 生成：使用new建立物件
- 建立關係：透過setter或是construct建立關係
```java=
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAdress(from));
msg.setSubset(subset);
msg.setText("My Message");
msg.setHeader("X-Mailer", "abc");
msg.sendDate(new Date());
transport.send(msg);
```
- 可以透過Factory將物件生成過程抽象化並封裝，將生成的責任委派給Factory
```java=
Message msg = myMessageFactory.create(session);
transport.send(msg);
```
將這種模式推展到極致，可以設計一種泛用的Factory專門用來生成並組合物件，也就是**輕量級容器**

### 輕量級容器與IoC
在使用輕量級容器，實質上就是物件生成主控權從程式倒置給容器。所以有人會稱之為IoC Container。
- frame reuse IoC: 主流程主控權倒置
- IoC container:物件生成主控權倒置
[為了避免混淆主流程主控權與物件生成主控權倒置，建議使用DI(Dependency Injection)指稱IoC container](https://martinfowler.com/articles/injection.html)

### DI 類型
1. setter
2. constructor
3. interface

Spring只有支援 **setter** 和 **constructor injection**
因為interface injection有較強的侵入性，後續要移植到其他framework會付出更多成本修改。

參考：[interface-injection-example](https://stackoverflow.com/questions/10248000/spring-interface-injection-example)

## Spring 如何實現 DI
### Bean, Bean 容器 與Bean設定檔
1. Bean: Spring frameworkm元件(廣義一般class)
2. Bean設定檔:對Bean屬性或行為做設定(現在都用annotation)
3. Bean 容器:管理Bean生命週期及建立關聯

### Bean vs JavaBean
#### JavaBean:
- 規定:
    1. 具有一個無參數建構子
    2. 使用getter,setter存取private variable且命名須為Camel命名慣例
    3. 若要支援遠端傳送或永續性(DB)必須為可序列化(Serializable)

```java=
//Serializable => marker interface:沒有定義任何虛實作方法
public class Product implements Serializable{
    private int productId;
    public void setProductId(int id){
        productId = id;
}
    public int getProductId(){
        return productId;
}
}
```
- 缺點
    - 無法一次設多個屬性
    - 在JDK1.4前無法存取巢狀屬性
    - **要取得Bean事件的類別必須實作java.beans.PropertyChangeSupport(產生Dependency)**
#### Bean
- 承襲JavaBean優點可以從外部改變屬性及行為 (ex: 從Bean設定檔set屬性)
- 沒有任何規範，任何POJO都可以設為Bean

### 開發Spring應用程式標準步驟
1. 規劃與設計Bean(使用OOAD物件導向分析設計)
2. 寫出Bean設定檔
3. 寫出使用Bean的Client端程式

### Bean容器
- 負責管理1~多個Bean
- init時會讀入Bean設定檔
- 維護Bean的註冊表格(Map)，一筆Bean紀錄=>BeanDefinition
    
![](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/images/container-magic.png)
    
當Client需要時可用id,Class向容器取得Bean實例來使用

### Bean 設定檔
- 由3個部分組成：
1. XML宣告區段
2. 標頭宣告區段
3. Bean宣告區段

- xml結構與型別：
    - DTD （比較舊，沒有data type）
    - XML Schema (有data type) <- 盡可能採用

### Bean 設定檔放置路徑

```mermaid
flowchart LR
src---spring---unit2---Product.java
unit2---beans-config.xml
```
編譯後，beans-config.xml會被複製到預設Class Path: bin/spring/unit2/跟Product.class放在一起

:::info
通常團隊合作開發會拆功能套件，每一個功能套件會維護一個bean config file並放在同一個套件資料夾方便管理
:::


### 取得Bean元件
- BeanFactory（已被標注要淘汰）
```java=
Resource resource = new ClassPathResource("beans-config.xml");
BeanFactor bf = new XmlBeanFactory(resource);
Product product = (Product)bf.getBean("product");
```
- ApplicationContext (推薦使用)
```java=
ApplicationContext context= new ClassPathXmlApplicationContext("beans-config.xml");
Product product = (Product)context.getBean("product");
```
如果有多個bean config檔要載入
```java=
String[] configs = {"beans-config.xml","beans-config2.xml"};
ApplicationContext context= new ClassPathXmlApplicationContext(configs);
```

 
:::warning
:warning: 設定檔裡面Bean的id不可以重複，否則容器無法知道要生成哪一個。
- IDE可以檢查同一個設定檔內是否有重複id
- 但IDE無法檢查多個設定檔內是否有重複id，必須依靠專案會議大家先對齊
:::

### 黏合用程式碼(Glue Code)
- 開發者自行建構BeanFactory或ApplicationContext並取得Bean的程式稱為**黏合用程式碼(Glue Code)**
- Spring 應用程式只有在黏合用程式碼(Glue Code)所在的類別才會相依於Spring Framework
```java=
/* Product.java */
public class Product{
    private String productId;
    private String name;
    ...
}


/* Client.java */
public class Client{
    public static void main(String[] args){
        //glue code
        ApplicationContext context= new ClassPathXmlApplicationContext("beans-config.xml");
        Product product = (Product)context.getBean("product");
        ...
    }
}

```
### ApplicationContext vs BeanFactory
一般情況下建議使用ApplicationContext作為Bean容器
- ApplicationContext包含BeanFactory功能
- 具有去除Glue Code功能

:::info
Web Application 用 ContextLoaderListener或ContextLoaderServlet在初始化載入ApplicationContext，開發者不用寫glue code
```xml
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- or use the ContextLoaderServlet instead of the above listener
<servlet>
  <servlet-name>context</servlet-name>
  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
-->
```
:::

### Bean實體化
```xml
<bean id="myProduct" class="myPackage.Product"/>
```
- id: Bean元件的唯一識別名稱，在同一個Bean Container不同Bean，**id不可重複**
- class: 指定元件類別，必須包含完整套件名稱的類別全稱（fully qualified name）

### 設定Bean屬性
透過 property標籤要求容器經由對應的setter方式設值

- 基本型別直接設值（int, double, boolean, String）
    Product.java:
    ```java=
    class Product {
        private String productId;
        public void setProductId(String productId){
            this.productId = productId;
        }
    }
    ```
    beans_config.xml:
    ```xml=
    <bean id = "myProduct" class="Product">
        <property name="productId" value="10001" />
    </bean>
    ```
- 如果是設定參考型別物件必須透過依賴注入的技術達成
    - setter
    - constructor



### Setter Injection
```mermaid
classDiagram 
ProductViewer --> Product : Association
```
ProductViewer.java
```java=
class ProductViewer{
    private Product product;
    public void setProduct(Product product){
        this.product = product;
    }
}
```
beans_config.xml:
```xml=
    <bean id = "myProduct" class="Product">
        <property name="productId" value="10001" />
    </bean>

    <bean id = "myProductViewer" class="ProductViewer">
        <property name="product" ref="myProduct" />
    </bean>
```

:::info
p-namespace：在標題加上xmlns:p="http://www.springframework.org/schema/p" 就可以使用p-namespace簡便語法。（可以在IDE透過設定方式加入，不要自己key）
 
```xml!
<bean id="coderTanu" class="com.gfg.scripter.Coder"> 
      <property name="id" value=100/> 
      <property name="name" value="Tanu Jain"/> 
      <property name="qualification" value="B.Tech - CSE"/> 
      <property name="dob" value="27-07-1996"/> 
</bean> 


<bean id="coderTanu" class="com.gfg.scripter.Coder" p:id=100 p:name="Tanu Jain" p:qualification="B.Tech - CSE" p:dob="27-07-1996"/> 
```

 


:::

### Constructor Injection
相較於association，aggreation的關係更緊密，需要在建構子就建立關聯。
```mermaid
classDiagram
ProductViewer --> Product : Aggreation
```
ProductViewer.java
```java=
class ProductViewer{
    private Product product;
    
    public ProductViewer(Product product){
        this.product = product;
    }
}
```
beans_config.xml:
```xml=
    <bean id = "myProduct" class="Product">
        <property name="productId" value="10001" />
    </bean>

    <bean id = "myProductViewer" class="ProductViewer">
        <constructor-arg ref="myProduct" />
</bean>
```

:::info
- 多個參數
```xml
    <bean id = "myProductViewer" class="ProductViewer">
        <constructor-arg ref="product1" />
        <constructor-arg ref="product2" />
    </bean>
```
- 多個**有序**參數
```xml
    <bean id = "myProductViewer" class="ProductViewer">
        <constructor-arg index="1" ref="product1" />
        <constructor-arg index="2" ref="product2" />
    </bean>
```
:::


### Bean Scope
- Singleton
    - 確保在整個容器中只有一個實體，適合放共用資料
    ```mermaid
    flowchart LR;
    singleton[singleton bean]
    b1[bean1]
    b2[bean2]
    b3[bean3]
    
    subgraph Bean容器
        singleton
    end
    
    b1 & b2 & b3 -->|ref| singleton
    ```
    - xml:
    ```xml
    <bean id = "myProductViewer" scope="singleton" class="ProductViewer">
        ...
    </bean>
    ```
    - **預設scope**
- Prototype
    - 每一個getBean() 要求都會送一個Bean元件的copy給Client端
    ```mermaid
    flowchart LR;
    b1[bean1]
    b2[bean2]
    b3[bean3]
    
    subgraph Bean容器
            bean
            copy1
            copy2
            copy3
    end
    
    b1 -->|ref| copy1
    b2 -->|ref| copy2
    b3 -->|ref| copy3
    ```
    - xml:
    ```xml
    <bean id = "myProductViewer" scope="prototype" class="ProductViewer">
        ...
    </bean>
    ```
    由於prototype會建立新的instance如果是跟DB連線等資源相關，需要注意效能是否會被影響。

:::info
 不同singelton定義：
 1. GoF Singleton(C++):在整個作業系統只有一個實例
  ```mermaid
 flowchart TB;
 subgraph OS
 singelton
 end
 ```
 2. Java Singleton:在整個JVM上只有一個實例
  ```mermaid
     flowchart TB;
     subgraph OS
     subgraph JVM1
         singleton1
     end
      subgraph JVM2
     singleton2
     end
     end
 ```
 5. Spring Singleton:在一個IoC容器只有一個實例
  ```mermaid
     flowchart LR;
     subgraph OS
     subgraph JVM1
     subgraph IoC container1
         singleton1
     end
      subgraph IoC container2
     singleton2
     end
     end
     end
 ```
:::

:::info
除了預設的singleton和prototype之外，針對web service有其他的scope。

詳見：[Bean Scopes](https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html)

[scope相關開發問題](https://medium.com/@HsuanChenLin/spring-annotation-scope-362b43705595)

:::

### 注入Array
```java
public class Order{
    private Product[] lineItems;
    public void setLineItems(Product[] products){
        this.lineItems = products;
    }
}
```
```xml
<bean id="order" class="Order">
<property name="lineItems">
    <list>
        <ref bean="product1"/>
        <ref bean="product2"/>
    </list>
</property>
</bean>
```
### 注入List
```java
public class Order{
    private List<Product> lineItems;
    public void setLineItems(List<Product> products){
        this.lineItems = products;
    }
}
```
```xml
<bean id="order" class="Order">
<property name="lineItems">
    <list>
        <ref bean="product1"/>
        <ref bean="product2"/>
    </list>
</property>
</bean>
```
### 注入Set
```java
public class Order{
    private Set<Product> lineItems;
    public void setLineItems(Set<Product> products){
        this.lineItems = products;
    }
}
```
```xml
<bean id = "product1" class="Product">
    ...
</bean>
<bean id = "product2" class="Product">
    ...
</bean>
<bean id="order" class="Order">
<property name="lineItems">
    <set>
        <ref bean="product1"/>
        <ref bean="product2"/>
    </set>
</property>
</bean>
```
:::info
Product需要實作hashcode跟equal才能在新增到set時判斷是否重複
參考：[equals() & hashCode() in Java](https://medium.com/joe-tsai/equals-hashcode-4480f4580be4)
:::
### 注入Properties
```xml
<props>
<prop key="id" value="john" />
</props>
```
### 注入Map


| key | value|
| -------- | -------- |
| name    | "John"    |
| product | product1  |


```xml
<bean id = "product1" class="Product">
    ...
</bean>
<bean id="order" class="Order">
<property name="lineItems">
    <map>
        <entry>
            <key>
                <value>name</value>
            </key>
            <value>John</value>
        </entry>
        <entry>
            <key>
                <value>product</value>
            </key>
            <ref bean="product1"/>
        </entry>
    </map>
</property>
</bean>
```
簡寫：
```xml
<bean id = "product1" class="Product">
    ...
</bean>
<bean id="order" class="Order">
<property name="lineItems">
    <map>
        <entry key="name" value="John" />
        <entry key="product" value-ref="product1"/>
    </map>
</property>
</bean>
```
### Factory Method
- 適用：無法透過xml設定bean(ex: 需要讀取DB或API)可以用程式解決。
```java=
public class Product{
    public static Product getInstance(){
        //特有的Product實體建構邏輯
    }
}
```
```xml!
<bean id="product" class="Product" factory-method="getInstance"/>
```

### Factory Bean

使用工廠模式產生Object

```java=
public class ProductFactory{
    public static Product getInstance(){
        //特有的Product實體建構邏輯
    }
}
```
```xml!
<bean id="productFactory" class="ProductFactory" />
<bean id="product" class="Product" 
      factory-bean="productFactory"
      factory-method="getInstance"/>
```
--- 

## AOP

### 開發應用程式流程
```mermaid
flowchart LR;
    U[Mental model]
    R[Requirement model]
    A[Architecture model & AOP]
    D[Design model]
    S[Solution model]
    I[Implementation]
    T[Testing]
    Dev[Deployment]
    M[Maintain]
    
    U-->R
    R-->|NFR|A
    R-->|FR|D
    A-->S
    D-->S
    S-->I-->T-->Dev-->M
```
FR: functional requirement
NFR: non functional requirement

從require model到solution model可以用OOAD方式設計系統

### 使用AOP動機
AOP要解決的是**商務邏輯跟非商務邏輯程式混雜的問題**
系統開發過程中往往要考量許多跟主要業務流程無關的橫切面考量(Cross-Cutting Concerns)
- 交易(transaction)
- 安全性
- 例外處理
- 記錄檔(log)

但若沒有仔細切分，容易讓程式碼中的商務流程跟系統考量交雜在一起，後續**維護困難**

### 將剖面(Aspect)單獨考量
基於重用的原則，將這些橫切面的處理邏輯抽出成一個Aspect單獨開發。
開發完再一次套用到有此需求的商務流程。
![截圖 2023-11-25 下午4.30.50](https://hackmd.io/_uploads/r1IAxNyHa.png)

### Aspect-Oriented Programming (AOP)

 - 分別專注在商務領域問題跟橫切面問題開發，個別完成後再進行縫合(Weaving)
 - 透過AOP的思維，Aspect可以獨立於應用程式之外
     - 必要時透過設定，可介入應用程式提供服務
     - 不需要時，也可以將Aspect直接抽離，應用程式本身不需要修改程式碼。
- 因為命名關係，有些人會將AOP與OOP(Object-Oriented Programming)拿來比較。但它們並不互相抵觸是相輔相成的兩個設計模型。

### AOP 觀念術語
- Aspect
    - 代表一個Cross-Cutting Concerns
    - 由Pointcut與Advice二類的元件組成
- Advise
    - Aspect的具體實作
- Join Points
    - Advise在切入商務流程的點稱之為Joint Point
    - 實作上來說就是Advice在應用程式裡**被執行的時機點**
    - Spring只支援**類別方法**的Join Points
    - Spring**不支援Field**的Join Points，因為Sping開發人員認為這樣會破壞物件的封裝性。
- Pointcut
    - 用來定義一群Joint Points
    - 當呼叫的方法符合Pointcut的表示式時，系統會自動將Advice與方法縫合
- Target
    - 一個Advice被應用的對象或目標物件
- Weave  
    - Advice被應用到物件上的過程稱之為縫合(Weave)
    - 在AOP中縫合的時間點：
        1. 編譯時期
        2. 類別載入時期
        3. 執行時期(大部分應用屬於此類)
### Pointcut宣告語法範例

```
execution(
            modifiers-pattern? //修飾子：（public, protected, default）
            return-type-pattern? //回傳型別
            declaring-type-pattern?//指定套件，類別
            name-pattern(parameter-pattern)?//方法
            throws-pattern?//例外
         )
```

- execution(public * *(\.\.))
    - 符合任何的公開方法
- execution(\* hello\*(\.\.))
    - 符合任何名稱是hello開頭的方法
- execution(\* somepackage.service.MyService\.\*(\.\.))
    - 符合MyService任何方法
- execution(\* somepackage.service\.\*\.\*(\.\.))
    - 符合somapackage.service套件中任何方法
- execution(\* somepackage\.\.\*.\*(\.\.))
    - 符合somepackage套件或子套件中任何方法

### Advice介入Joint Point時機
- Before advice
    - Advice會在joint point執行之前被觸發
    ```java
        public void before(JointPoint jointpoint){
            ...
        }
    ```
- After returning advice
    - 如果joint point所在方被成功執行並return，advice會緊接著執行
    ```java
        public void afterReturning(JointPoint jointpoint){
            ...
        }
    ```
- After advice
    - 無論是否成功return，advice都會緊接著執行
    ```java
        public void after(JointPoint jointpoint){
            ...
        }
    ```
- After throwing
    - 方法拋出例外時執行
    ```java
        public void afterThrowing(JointPoint jointpoint){
            ...
        }
    ```
- **Around**
    - 從joint point觸發前到return後的過程完全掌握
    - 適合用在交易,觀測
    - 跟其他advice參數和回傳型別不同
    ```Java
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //方法執行前
        Object result = proceedingJoinPoint.proceed();
        //方法執行後
        return result;
    }
    ```
    因為要掌握完整流程需要暫時取得物件控制權，執行後要把物件(Object)回傳IoC Container，讓容器繼續管理物件。所以如果回傳null，Spring會無法正常執行發出error
    
### 使用XML設定Spring AOP
- 寫作Advice類別
- 在bean設定檔加入AOP命名空間
- 在bean設定檔宣告AOP相關設定
    
### 寫作Advice類別
```java=
public class LogAdvice{
    ...
    public void before(JointPoint jointPoint){
        ...(要插入的邏輯)
    }
}
```
### 在bean設定檔加入AOP命名空間
可以使用IDE自動產生
```xml!
<?xml version="1.0" encoding="UTF-8"?>    
<beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
</beans>
</xml>
```
### 在bean設定檔宣告AOP相關設定
```xml!
<bean id="logAdvice" class="mypackage.LogAdvice" />
<bean id="product" class="mypackage.Product" />

<aop:config>
    <aop:aspect id="logging" ref="logAdvice">
        <aop:before pointcut="execution(*mypackage.Product.*(..))" method="before"
    </aop:aspect>
</aop:config>
```
在Product任何類別方法執行前插入LogAdvice.before的方法


### 改成Annotation
1. 在Bean設定檔中加入AOP命名空間
2. 在Bean設定檔中加入\<aop:aspectj-autoproxy>
3. 寫作Target類別並加入Annotations

### 在Bean設定檔中加入\<aop:aspectj-autoproxy>
修改前：
```xml!
<bean id="logAdvice" class="mypackage.LogAdvice" />
<bean id="product" class="mypackage.Product" />

<aop:config>
    <aop:aspect id="logging" ref="logAdvice">
        <aop:before pointcut="execution(*mypackage.Product.*(..))" method="before"
    </aop:aspect>
</aop:config>
```

修改後，透過Annotion讓系統自行加入Advice：
```xml!
<bean id="logAdvice" class="mypackage.LogAdvice" />
<bean id="product" class="mypackage.Product" />

<aop:aspectj-autoproxy>
```
### 寫作Target類別並加入Annotations
```java=
@Aspect
public class LogAdvice{
    ...
    @Before("excution(* mypackage.Product.*(..))")
    public void before(JointPoint jointPoint){
        ...(要插入的邏輯)
    }
}
```
:::info
小秘訣：
pointcut語法容易key錯，可以考慮將常用的pointcut語法放到一個類別去管理。
```java
//管理pointcut類別
@Aspect
public class SystemArchitecture {

  /**
   * A join point is in the web layer if the method is defined
   * in a type in the com.xyz.someapp.web package or any sub-package
   * under that.
   */
  @Pointcut("within(com.xyz.someapp.web..*)")
  public void inWebLayer() {}

}


//定義Targer類別
@Aspect
public class LogAdvice{
    ...
    @Before(pointcut = "inWebLayer()")
    public void before(JointPoint jointPoint){
        ...(要插入的邏輯)
    }
}
```
:::

### Aspect vs Filter
Aspect跟Web Application的Filter相似
- Filter
    web.xml用URL pattern指定哪些URL的request 會套上Filter
- Aspect 
    beans-config.xml指定哪些方法會觸發Advice

### AOP & Proxy Design Pattern
有注意到 \<aop:aspectj-autoproxy\>裡出現的aspectj和proxy嗎？

- Spring 從Spring 2.0開始支援IBM的AspectJ並受到啟發，在語法操作上盡量和AspectJ一致。
- 而proxy則是暗示AOP是實作Proxy Design Pattern

### 什麼是Proxy Design Pattern

![proxy pattern](https://refactoring.guru/images/patterns/content/proxy/proxy.png?id=efece4647fb11e3f7539291796327666)

一種結構設計模式，創造一個代理者（proxy)和其他物件互動。由於代理者會掌握被代理者流程，可以在被代理者流程執行前後插入其他流程，適合用來身份驗證或是埋入log等操作。

1. 靜態代理（較簡單）：
    被代理者切出一個共用介面，讓代理者(proxy)跟被代理者實作同一個介面，並讓代理者擁有被代理者控制權。

![UML](https://refactoring.guru/images/patterns/diagrams/proxy/structure.png?id=f2478a82a84e1a1e512a8414bf1abd1c)

2. 動態代理:
    代理者不限定只代理特定類別，通常由底層系統自動產生代理物件。
    Spring AOP支援3種代理方式：
    1. 執行時期產生代理物件
        - Java Dynamic Proxy
        - cglib（Code Generation Library）
    2. 編譯或類別載入時期產生代理物件
        - AspectJ




## 參考連結

[SPF doc 核心概念](https://docs.spring.io/spring-framework/reference/core.html)

[AOP參考](https://openhome.cc/Gossip/SpringGossip)

[關於AspectJ](https://openhome.cc/Gossip/Spring/AspectJ.html)

[從動態代理到Sping AOP](https://www.ithome.com.tw/voice/127388)

[Spring AOP - Java Dynamic Proxy and CGLib](https://www.tpisoftware.com/tpu/articleDetails/457)