Spring設計理念

Ho1d_F0rward發表於2024-08-17

AOP

基礎概念

AOP,也就是 Aspect-oriented Programming,譯為面向切面程式設計,我們可以簡單的把 AOP 理解為貫穿於方法之中,就好比我們今天的主題——日誌功能,就是一個典型的案例。

簡單使用

1)橫切關注點,從每個方法中抽取出來的同一類非核心業務。

2)切面(Aspect),對橫切關注點進行封裝的類,每個關注點體現為一個通知方法;通常使用 @Aspect 註解來定義切面。

3)通知(Advice),切面必須要完成的各個具體工作,比如我們的日誌切面需要記錄介面呼叫前後的時長,就需要在呼叫介面前後記錄時間,再取差值。通知的方式有五種:

  • @Before:通知方法會在目標方法呼叫之前執行
  • @After:通知方法會在目標方法呼叫後執行
  • @AfterReturning:通知方法會在目標方法返回後執行
  • @AfterThrowing:通知方法會在目標方法丟擲異常後執行
  • @Around:把整個目標方法包裹起來,在被呼叫前和呼叫之後分別執行通知方法

4)連線點(JoinPoint),通知應用的時機,比如介面方法被呼叫時就是日誌切面的連線點。

5)切點(Pointcut),通知功能被應用的範圍,比如本篇日誌切面的應用範圍是所有 controller 的介面。通常使用 @Pointcut 註解來定義切點表示式。

主要的註解有這幾種:

  • @Before:通知方法會在目標方法呼叫之前執行
  • @After:通知方法會在目標方法呼叫後執行
  • @AfterReturning:通知方法會在目標方法返回後執行
  • @AfterThrowing:通知方法會在目標方法丟擲異常後執行
  • @Around:把整個目標方法包裹起來,在被呼叫前和呼叫之後分別執行通知方法
  • @Pointcut 註解來定義切點表示式。
  • @Aspect-它用於定義一個切面(Aspect)類。切面類包含了一個或多個切入點(Pointcut)以及相關的增強(Advice)方法。

例子如下:

@Aspect
public class LoggingAspect {
    // 定義切入點
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void logPointcut() {}

    // 定義前置增強
    @Before("logPointcut()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("Calling method: " + joinPoint.getSignature().getName());
    }
}

// 目標類
@Service
public class UserService {
    public void createUser(String name) {
        System.out.println("Creating user: " + name);
    }

    public void deleteUser(String name) {
        System.out.println("Deleting user: " + name);
    }
}

IOC

基礎概念

控制反轉就是把建立和管理 bean 的過程轉移給了第三方。而這個第三方,就是 Spring IoC Container,對於 IoC 來說,最重要的就是容器。

容器或者說Bean 其實就是包裝了的 Object,無論是控制反轉還是依賴注入,它們的主語都是 object,而 bean 就是由第三方包裝好了的 object(想一下別人送禮物給你的時候都是要包裝一下的,自己造的就免了)。

通俗點講,因為專案中每次建立物件是很麻煩的,所以我們使用 Spring IoC 容器來管理這些物件,需要的時候你就直接用,不用管它是怎麼來的、什麼時候要銷燬,只管用就好了。

IOC容器

Spring 設計容器使用的是ApplicationContext,它是 BeanFactory 的子類,更好的補充並實現了 BeanFactory 的。

BeanFactory 簡單粗暴,可以理解為 HashMap:

  • Key - bean name
  • Value - bean object

但它一般只有 get, put 兩個功能,所以稱之為“低階容器”。

ApplicationContext 多了很多功能,因為它繼承了多個介面,可稱之為“高階容器”。在下文的搭建專案中,我們會使用它。

ApplicationContext 的裡面有兩個具體的實現子類,用來讀取配置配件的:

  • ClassPathXmlApplicationContext - 從 class path 中載入配置檔案,更常用一些;
  • FileSystemXmlApplicationContext - 從本地檔案中載入配置檔案,不是很常用,如果再到 Linux 環境中,還要改路徑,不是很方便。

程式碼實現

我們假定一個線上書店,透過BookService獲取書籍:

傳統程式碼編寫

public class BookService {
    private HikariConfig config = new HikariConfig();
    private DataSource dataSource = new HikariDataSource(config);

    public Book getBook(long bookId) {
        try (Connection conn = dataSource.getConnection()) {
            ...
            return book;
        }
    }
}

IoC模式下編寫

public class BookService {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

不直接new一個DataSource,而是注入一個DataSource,這個小小的改動雖然簡單,卻帶來了一系列好處:

  1. BookService不再關心如何建立DataSource,因此,不必編寫讀取資料庫配置之類的程式碼;
  2. DataSource例項被注入到BookService,同樣也可以注入到UserService,因此,共享一個元件非常簡單;
  3. 測試BookService更容易,因為注入的是DataSource,可以使用記憶體資料庫,而不是真實的MySQL配置。

因為IoC容器要負責例項化所有的元件,因此,有必要告訴容器如何建立元件,以及各元件的依賴關係.

在spring中就是使用的註解來進行配置,這裡簡單看一下就行,在sping使用中會詳細介紹。

// UserService Bean
@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(User user) {
        userRepository.save(user);
    }
}

// UserRepository Bean
@Repository
public class UserRepository {
    public void save(User user) {
        // 儲存使用者資料的邏輯
    }
}

// Spring 配置類
@Configuration
@ComponentScan("com.example")
public class AppConfig {
    // 其他 Bean 定義
}

// 使用 UserService
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        userService.createUser(user);
        return ResponseEntity.ok(user);
    }
}

幾個關鍵問題:

何為控制,控制的是什麼?

答:是 bean 的建立、管理的權利,控制 bean 的整個生命週期。

何為反轉,反轉了什麼?

答:把這個權利交給了 Spring 容器,而不是自己去控制,就是反轉。由之前的自己主動建立物件,變成現在被動接收別人給我們的物件的過程,這就是反轉。

舉個生活中的例子,主動投資和被動投資。

自己炒股、選股票的人就是主動投資,主動權掌握在自己的手中;而買基金的人就是被動投資,把主動權交給了基金經理,除非你把這個基金賣了,否則具體選哪些投資產品都是基金經理決定的。

參考連結

https://javabetter.cn/springboot/aop-log.html#一、關於-aop

相關文章