深入理解註解驅動配置與XML配置的融合與區別

来源:https://www.cnblogs.com/huaweiyun/archive/2023/06/07/17463536.html
-Advertisement-
Play Games

摘要:本文旨在深入探討Spring框架的註解驅動配置與XML配置,揭示兩者之間的相似性與差異。 本文分享自華為雲社區《Spring高手之路2——深入理解註解驅動配置與XML配置的融合與區別》,作者:磚業洋__ 。 本文旨在深入探討Spring框架的註解驅動配置與XML配置,揭示兩者之間的相似性與差異 ...


摘要:本文旨在深入探討Spring框架的註解驅動配置與XML配置,揭示兩者之間的相似性與差異。

本文分享自華為雲社區《Spring高手之路2——深入理解註解驅動配置與XML配置的融合與區別》,作者:磚業洋__ 。

本文旨在深入探討Spring框架的註解驅動配置與XML配置,揭示兩者之間的相似性與差異。我們首先介紹了配置類的編寫與Bean的註冊,然後比較了註解驅動的IOC依賴註入與XML依賴註入。文章進一步解析了Spring的組件註冊與組件掃描,包括使用@ComponentScan和XML啟用component-scan的情況,以及不使用@ComponentScan的場景。接下來,我們深入探討了其他相關的組件

1.配置類的編寫與Bean的註冊

XML配置中,我們通常採用ClassPathXmlApplicationContext,它能夠載入類路徑下的XML配置文件來初始化Spring應用上下文。然而,在註解驅動的配置中,我們則使用以Annotation開頭和ApplicationContext結尾的類,如AnnotationConfigApplicationContext。AnnotationConfigApplicationContext是Spring容器的一種,它實現了ApplicationContext介面。

對比於 XML 文件作為驅動,註解驅動需要的是配置類。一個配置類就可以類似的理解為一個 XML 。配置類沒有特殊的限制,只需要在類上標註一個 @Configuration 註解即可。

我們創建一個 Book 類:

public class Book {
 private String title;
 private String author;
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title = title;
 }
 public String getAuthor() {
 return author;
 }
 public void setAuthor(String author) {
 this.author = author;
 }
}

在 xml 中聲明 Bean 是通過 <bean> 標簽

<bean id="book" class="com.example.Book">
 <property name="title" value="Java Programming"/>
 <property name="author" value="Unknown"/>
</bean>

如果要在配置類中替換掉 <bean> 標簽,需要使用 @Bean 註解

我們創建一個配置類來註冊這個 Book bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LibraryConfiguration {
 @Bean
 public Book book() {
 Book book = new Book();
 book.setTitle("Java Programming");
 book.setAuthor("Unknown");
 return book;
 }
}

在這個配置中,我們使用了 @Configuration 註解來表示這是一個配置類,類似於一個 XML 文件。我們在 book() 方法上使用了 @Bean 註解,這意味著這個方法將返回一個由 Spring 容器管理的對象。這個對象的類型就是 Book,bean 的名稱id就是方法的名稱,也就是 “book”。

類似於 XML 配置的 <bean> 標簽,@Bean 註解負責註冊一個 bean。你可以把 @Bean 註解看作是 <bean> 標簽的替代品。

如果你想要更改這個 bean 的名稱,你可以在 @Bean 註解中使用 name 屬性:

 @Bean(name="mybook")
 public Book book() {
 Book book = new Book();
 book.setTitle("Java Programming");
 book.setAuthor("Unknown");
 return book;
 }

這樣,這個 Book bean 的名稱就變成了 “mybook”。

啟動並初始化註解驅動的IOC容器

@SpringBootApplication
public class DemoApplication {
 public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class);
 // 從容器中獲取 Book bean
 LibraryConfiguration libraryConfiguration = context.getBean(LibraryConfiguration.class);
 System.out.println(libraryConfiguration.book().getTitle());
 System.out.println(libraryConfiguration.book().getAuthor());
 }
}

ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class)這個語句創建了一個Spring的應用上下文,它是以配置類LibraryConfiguration.class作為輸入的,這裡明確指定配置類的Spring應用上下文,適用於更一般的Spring環境。

對比一下ApplicationContext context = SpringApplication.run(DemoApplication.class, args);這個語句則是Spring Boot應用的入口,啟動一個Spring Boot應用。SpringApplication.run()方法會創建一個Spring Boot應用上下文(也就是一個SpringApplication對象),這個上下文包含了Spring Boot應用所有的Bean和配置類,還有大量的預設配置。這個方法之後,Spring Boot的自動配置就會起作用。你可以把SpringApplication.run()創建的Spring Boot上下文看作是更加功能豐富的Spring上下文。

列印結果:

Java Programming和Unknown被列印,執行成功。

註意:@SpringBootApplication是一個複合註解,它等效於同時使用了@Configuration,@EnableAutoConfiguration和@ComponentScan。這三個註解的作用是:

  • @Configuration:指明該類是一個配置類,它可能會有零個或多個@Bean註解,方法產生的實例由Spring容器管理。
  • @EnableAutoConfiguration:告訴Spring Boot根據添加的jar依賴自動配置你的Spring應用。
  • @ComponentScan:Spring Boot會自動掃描該類所在的包以及子包,查找所有的Spring組件,包括@Configuration類。

在非Spring Boot的傳統Spring應用中,我們通常使用AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext等來手動創建和初始化Spring的IOC容器。

"非Spring Boot的傳統Spring應用"是指在Spring Boot項目出現之前的Spring項目,這些項目通常需要手動配置很多東西,例如資料庫連接、事務管理、MVC控制器等。這種類型的Spring應用通常需要開發者對Spring框架有深入的瞭解,才能做出正確的配置。

Spring Boot是Spring項目的一個子項目,它旨在簡化Spring應用的創建和配置過程。Spring Boot提供了一系列的"起步依賴",使得開發者只需要添加少量的依賴就可以快速開始項目的開發。此外,Spring Boot還提供了自動配置的特性,這使得開發者無需手動配置資料庫連接、事務管理、MVC控制器等,Spring Boot會根據項目的依賴自動進行配置。

因此,"非Spring Boot的傳統Spring應用"通常需要手動創建和初始化Spring的IOC容器,比如使用AnnotationConfigApplicationContext或ClassPathXmlApplicationContext等。在Spring Boot應用中,這個過程被自動化了,開發者只需要在main方法中調用SpringApplication.run方法,Spring Boot就會自動創建和初始化Spring的IOC容器。SpringApplication.run(Application.class, args);語句就是啟動Spring Boot應用的關鍵。它會啟動一個應用上下文,這個上下文會載入所有的Spring組件,並且也會啟動Spring的IOC容器。在這個過程中,所有通過@Bean註解定義的bean都會被創建,並註冊到IOC容器中。

有人說,那學習Spring Boot就好了,學什麼Spring和Spring MVC啊,這不是落後了嗎

Spring Boot並不是Spring框架的替代品,而是建立在Spring框架之上的一種工具,它內部仍然使用Spring框架的很多核心技術,包括Spring MVC。所以,當我們在使用Spring Boot時,我們實際上仍然在使用Spring MVC來處理Web層的事務。

簡而言之,Spring MVC是一個用於構建Web應用程式的框架,而Spring Boot是一個用於簡化Spring應用程式開發的工具,它內部仍然使用了Spring MVC。你在Spring Boot應用程式中使用的@Controller、@Service、@Autowired等註解,其實都是Spring框架提供的,所以,原理性的東西還是需要知道。

2. 註解驅動IOC的依賴註入與XML依賴註入對比

我們就以上面的例子來說,假設配置類註冊了兩個bean,並設置相關的屬性:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LibraryConfiguration {
 @Bean
 public Book book() {
 Book book = new Book();
 book.setTitle("Java Programming");
 book.setAuthor("Unknown");
 return book;
 }
 @Bean
 public Library library() {
 Library library = new Library();
 library.setBook(book());
 return library;
 }
}

這裡的方法有@Bean註解,這個註解告訴Spring,這個方法返回的對象需要被註冊到Spring的IOC容器中。

如果不用註解,要實現相同功能的話,對應的XML配置如下:

<bean id="book" class="com.example.Book">
 <property name="title" value="Java Programming"/>
 <property name="author" value="Unknown"/>
</bean>
<bean id="library" class="com.example.Library">
 <property name="book" ref="book"/>
</bean>

在這個XML配置中,我們定義了兩個<bean>元素,分別用來創建Book對象和Library對象。在創建Book對象時,我們使用了<property>元素來設置title和author屬性。在創建Library對象時,我們也使用了<property>元素,但是這次我們使用了ref屬性來引用已經創建的Book對象,這就相當於將Book對象註入到Library對象中。

3. Spring中組件的概念

在Spring框架中,當我們說 “組件” 的時候,我們通常指的是被Spring管理的各種Java對象,這些對象在Spring的應用上下文中作為Bean存在。這些組件可能是服務層的類、數據訪問層的類、控制器類、配置類等等。

@ComponentScan註解會掃描指定的包(及其子包)中的類,如果這些類上標註了@Component、@Controller、@Service、@Repository、@Configuration等註解,那麼Spring就會為這些類創建Bean定義,並將這些Bean定義註冊到Spring的應用上下文中。因此,我們通常說@ComponentScan進行了"組件掃描",因為它掃描的是標註了上述註解的類,這些類在Spring中都被視為組件。

而這些註解標記的類,最終在Spring的應用上下文中都會被創建為Bean,因此,你也可以理解@ComponentScan為"Bean掃描"。但是需要註意的是,@ComponentScan只負責掃描和註冊Bean定義,Bean定義就是元數據描述,包括瞭如何創建Bean實例的信息。

總結一下,@ComponentScan註解會掃描並註冊的"組件"包括:

  • 標註了@Component註解的類
  • 標註了@Controller註解的類(Spring MVC中的控制器組件)
  • 標註了@Service註解的類(服務層組件)
  • 標註了@Repository註解的類(數據訪問層組件)
  • 標註了@Configuration註解的類(配置類)

這些組件最終都會在Spring的應用上下文中以Bean的形式存在。

4. 組件註冊

這裡Library 標註 @Configuration 註解,即代表該類會被註冊到 IOC 容器中作為一個 Bean。

@Component
public class Library {
}

相當於 xml 中的:

<bean id="library" class="com.example.demo.configuration.Library">

如果想指定 Bean 的名稱,可以直接在 @Configuration 中聲明 value 屬性即可

@Component("libra")
public class Library {
}

@Component("libra")就將這個bean的名稱改為了libra,如果不指定 Bean 的名稱,它的預設規則是 “類名的首字母小寫”(例如Library預設名稱是 library )

5. 組件掃描

如果我們只寫了@Component, @Configuration 這樣的註解,IOC容器是找不到這些組件的。

5.1 使用@ComponentScan的組件掃描

忽略掉之前的例子,在這裡我們需要運行的代碼如下:

@Component
public class Book {
 private String title = "Java Programming";
 private String author = "Unknown";
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title = title;
 }
 public String getAuthor() {
 return author;
 }
 public void setAuthor(String author) {
 this.author = author;
 }
}
@Component
public class Library {
 @Resource
 private Book book;
 public Book getBook() {
 return book;
 }
 public void setBook(Book book) {
 this.book = book;
 }
}

如果不寫@ComponentScan,而且@Component註解標識的類不在當前包或者子包,那麼就會報錯。

難道@Component註解標識的類在當前包或者當前包的子包,主程式上就可以不寫@ComponentScan了嗎?

是的!前面說了,@SpringBootApplication 包含了 @ComponentScan,其實已經幫我們寫了!只有組件和主程式不在一個共同的根包下,才需要顯式地使用 @ComponentScan 註解。由於 Spring Boot 的設計原則是“約定優於配置”,所以推薦將主應用類放在根包下。

在應用中,我們的組件(帶有 @Component、@Service、@Repository、@Controller 等註解的類)和主配置類位於不同的包中,並且主配置類或者啟動類沒有使用 @ComponentScan 指定掃描這些包,那麼在運行時就會報錯,因為Spring找不到這些組件。

主程式:

@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
 Library library = context.getBean(Library.class);
 System.out.println(library.getBook().getTitle());
 System.out.println(library.getBook().getAuthor());
 }
}

@ComponentScan 不一定非要寫在主程式(通常是指 Spring Boot 的啟動類)上,它可以寫在任何配置類(標記有 @Configuration 註解的類)上。@ComponentScan 註解會告訴 Spring 從哪些包開始進行組件掃描。

為了簡化配置,我們通常會將 @ComponentScan 放在主程式上,因為主程式一般會位於根包下,這樣可以掃描到所有的子包。這裡為了演示,並沒有把主程式放在根目錄。

我們上面說過,@ComponentScan只負責掃描和註冊Bean定義,只有需要某個Bean時,這個Bean才會實例化。

那怎麼才能知道是不是需要這個Bean呢?

我來給大家舉例子,並且還會說明Bean的創建順序問題,"需要某個Bean"通常體現在以下幾個方面:

  • 依賴註入(Dependency Injection): 如果一個BeanA的欄位或者構造方法被標註為@Autowired或者@Resource,那麼Spring就會嘗試去尋找類型匹配的BeanB並註入到BeanA中。在這個過程中,如果BeanB還沒有被創建,那麼Spring就會先創建BeanB的實例。
@Component
public class BeanA {
 @Autowired
 private BeanB beanB;
}
@Component
public class BeanB {
}

BeanA依賴於BeanB。在這種情況下,當你嘗試獲取BeanA的實例時,Spring會首先創建BeanB的實例,然後把這個實例註入到BeanA中,最後創建BeanA的實例。在這個例子中,BeanB會先於BeanA被創建。

這種方式的一個主要優點是,我們不需要關心Bean的創建順序,Spring會自動解決這個問題。這是Spring IoC容器的一個重要特性,也是為什麼它能夠使我們的代碼更加簡潔和易於維護的原因。

  • Spring框架調用: 有些情況下,Spring框架的一些組件或者模塊可能需要用到你定義的Bean。比如,如果你定義了一個@Controller,那麼在處理HTTP請求時,Spring MVC就會需要使用到這個@Controller Bean。如果這個時候Bean還沒有被創建,那麼Spring也會先創建它的實例。

假設我們有一個名為BookController的類,該類需要一個BookService對象來處理一些業務邏輯。

@Controller
public class BookController {
 @Autowired
 private BookService bookService;
 // 其他的控制器方法
}

BookService類

@Service
public class BookService {
 @Autowired
 private BookMapper bookMapper;
 // 一些業務邏輯方法
}

當Spring Boot應用程式啟動時,以下步驟將會發生:

  1. 首先,Spring框架通過@ComponentScan註解掃描類路徑,找到了BookController、BookService和BookMapper等類,併為它們創建Bean定義,註冊到Spring的應用上下文中。
  2. 當一個請求到達並需要使用到BookController時,Spring框架會嘗試創建一個BookController的Bean實例。
  3. 在創建BookController的Bean實例的過程中,Spring框架發現BookController類中需要一個BookService的Bean實例(通過@Autowired註解指定),於是Spring框架會先去創建一個BookService的Bean實例。
  4. 同樣,在創建BookService的Bean實例的過程中,Spring框架發現BookService類中需要一個BookMapper的Bean實例(通過@Autowired註解指定),於是Spring框架會先去創建一個BookMapper的Bean實例。
  5. 在所有依賴的Bean都被創建並註入之後,BookController的Bean實例最終被創建完成,可以處理來自用戶的請求了。

在這個過程中,BookController、BookService和BookMapper這三個Bean的創建順序是有嚴格要求的,必須按照他們之間的依賴關係來創建。只有當一個Bean的所有依賴都已經被創建並註入後,這個Bean才能被創建。這就是Spring框架的IoC(控制反轉)和DI(依賴註入)的機制。

  • 手動獲取: 如果你在代碼中手動通過ApplicationContext.getBean()方法獲取某個Bean,那麼Spring也會在這個時候創建對應的Bean實例,如果還沒有創建的話。

總的來說,"需要"一個Bean,是指在運行時有其他代碼需要使用到這個Bean的實例,這個"需要"可能來源於其他Bean的依賴,也可能來源於框架的調用,或者你手動獲取。在這種需要出現時,如果對應的Bean還沒有被創建,那麼Spring就會根據之前通過@ComponentScan等方式註冊的Bean定義,創建對應的Bean實例。

5.2 xml中啟用component-scan組件掃描

對應於 @ComponentScan 的 XML 配置是 <context:component-scan> 標簽

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
 <context:component-scan base-package="com.example" />
</beans>

在這段 XML 配置中,<context:component-scan> 標簽指定了 Spring 需要掃描 com.example 包及其子包下的所有類,這與 @ComponentScan 註解的功能是一樣的。

註意:在使用 <context:component-scan> 標簽時,需要在 XML 配置文件的頂部包含 context 命名空間和相應的 schema 位置(xsi:schemaLocation)。

5.3 不使用@ComponentScan的組件掃描

如果我們不寫@ComponentScan註解,那麼這裡可以把主程式改為如下:

@SpringBootApplication
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
 Library library = context.getBean(Library.class);
 System.out.println(library.getBook().getTitle());
 System.out.println(library.getBook().getAuthor());
 }
}

AnnotationConfigApplicationContext 的構造方法中有一個是填寫basePackages路徑的,可以接受一個或多個包的名字作為參數,然後掃描這些包及其子包。

運行結果如下:

在這個例子中,Spring 將會掃描 com.example 包及其所有子包,查找並註冊所有的 Bean,達到和@ComponentScan註解一樣的效果。

我們也可以手動創建一個配置類來註冊bean,那麼想要運行得到一樣的效果,需要的代碼如下:

@Component
public class Book {
 private String title = "Java Programming";
 private String author = "Unknown";
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title = title;
 }
 public String getAuthor() {
 return author;
 }
 public void setAuthor(String author) {
 this.author = author;
 }
}
@Component
public class Library {
 private Book book;
 public Book getBook() {
 return book;
 }
 public void setBook(Book book) {
 this.book = book;
 }
}
@Configuration
public class LibraryConfiguration {
 @Bean
 public Book book() {
 Book book = new Book();
 book.setTitle("Java Programming");
 book.setAuthor("Unknown");
 return book;
 }
 @Bean
 public Library library() {
 Library library = new Library();
 library.setBook(book());
 return library;
 }
}

主程式:

@SpringBootApplication
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class);
 Library library = context.getBean(Library.class);
 System.out.println(library.getBook().getTitle());
 System.out.println(library.getBook().getAuthor());
 }
}

我們創建了一個配置類LibraryConfiguration,用於定義Book和Library這兩個bean。然後以配置類LibraryConfiguration.class作為輸入的來創建Spring的IOC容器(Spring應用上下文就是Spring IOC容器)。

運行結果和前面一樣。

註意,在這個例子里,如果你寫@ComponentScan,並且SpringApplication.run(Application.class, args);作為Spring上下文,那麼這裡運行配置類需要去掉Book和Library類的@Component註解,不然會報錯A bean with that name has already been defined。這是因為如果同時在 Book 和Library 類上使用了 @Component 註解,而且配置類LibraryConfiguration上使用了@Configuration註解,這都會被 @ComponentScan 掃描到,那麼 Book 和 Library的實例將會被創建並註冊兩次。正確的做法是,要麼在配置類中通過 @Bean 註解的方法創建Book 和 Library的實例,要麼在 Book 和 Library 類上寫 @Component 註解。如果不是第三方庫,我們一般選擇後者。

為什麼要有配置類出現?所有的Bean上面使用@Component,用@ComponentScan註解掃描不就能解決了嗎?

我們在使用一些第三方庫時,需要對這些庫進行一些特定的配置。這些配置信息,我們可能無法直接通過註解或者XML來完成,或者通過這些方式完成起來非常麻煩。而配置類可以很好地解決這個問題。通過配置類,我們可以在Java代碼中完成任何複雜的配置邏輯。

假設你正在使用 MyBatis,在這種情況下可能需要配置一個SqlSessionFactory,在大多數情況下,我們無法(也不應該)直接修改第三方庫的代碼,所以無法直接在SqlSessionFactory類或其他類上添加@Configuration、@Component等註解。為了能夠在Spring中使用和配置這些第三方庫,我們需要創建自己的配置類,併在其中定義@Bean方法來初始化和配置這些類的實例。這樣就可以靈活地控制這些類的實例化過程,並且可以利用Spring的依賴註入功能。

下麵是一個使用@Configuration和@Bean來配置MyBatis的例子:

@Configuration
@MapperScan("com.example.demo.mapper")
public class MyBatisConfig {
 @Bean
 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
 SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(
 new PathMatchingResourcePatternResolver().getResources("classpath*:com/example/demo/mapper/*Mapper.xml")
 );
 return factoryBean.getObject();
 }
}

sqlSessionFactory方法創建一個SqlSessionFactoryBean對象,並使用DataSource(Spring Boot預設為你配置的一個Bean)進行初始化。然後,它指定MyBatis mapper XML文件的位置,最後返回SqlSessionFactory對象。

通過這種方式,你可以靈活地配置MyBatis,並將其整合到Spring應用中。這是一種比使用XML配置文件或僅僅依賴於自動配置更為靈活和強大的方式。

6. 組件註冊的其他註解

@Controller, @Service, @Repository和@Component 一樣的效果,它們都會被 Spring IoC 容器識別,並將類實例化為 Bean。讓我們來看這些註解:

  • @Controller:這個註解通常標註在表示表現層(比如 Web 層)的類上,如Spring MVC 中的控制器。它們處理用戶的 HTTP 請求並返迴響應。雖然 @Controller 與 @Component 在功能上是類似的,但 @Controller 註解的使用表示了一種語義化的分層結構,使得控制層代碼更加清晰。
  • @Service:這個註解通常用於標註業務層的類,這些類負責處理業務邏輯。使用 @Service 註解表明該類是業務處理的核心類,使得代碼更具有語義化。
  • @Repository:這個註解用於標記數據訪問層,也就是數據訪問對象或DAO層的組件。在資料庫操作的實現類上使用 @Repository 註解,這樣Spring將自動處理與資料庫相關的異常並將它們轉化為Spring的DataAccessExceptions。

在實際開發中,幾乎很少看到@Repository,而是利用 MyBatis 的 @Mapper 或 @MapperScan 實現數據訪問,通常做法是,@MapperScan 註解用於掃描特定包及其子包下的介面,這些介面被稱為 Mapper 介面。Mapper 介面方法定義了 SQL 查詢語句的簽名,而具體的 SQL 查詢語句則通常在與介面同名的 XML 文件中定義。

@MapperScan("com.example.**.mapper") 會掃描 com.example 包及其所有子包下的名為 mapper 的包,以及 mapper 包的子包。 ** 是一個通配符,代表任意深度的子包。

舉個例子,以下是一個 Mapper 介面的定義:

package com.example.demo.mapper;
public interface BookMapper {
 Book findBookById(int id);
}

對應的 XML 文件(通常位於 resources 目錄下,並且與介面在相同的包路徑中)

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.BookMapper">
 <select id="findBookById" parameterType="int" resultType="com.example.demo.Book">
        SELECT title, author FROM book WHERE id = #{id}
 </select>
</mapper>

註意:在 XML 文件中的 namespace 屬性值必須與 Mapper 介面的全限定類名相同,<select> 標簽的 id 屬性值必須與介面方法名相同。

然後,在 Spring Boot 的主類上,我們使用 @MapperScan 註解指定要掃描的包:

@SpringBootApplication
@MapperScan("com.example.**.mapper")
public class Application {
 public static void main(String[] args) {
 SpringApplication.run(Application.class, args);
 }
}

這樣,MyBatis 就會自動為 UserMapper 介面創建一個實現類(實際上是一個代理對象),並將其註冊到 Spring IOC 容器中,你就可以在你的服務類中直接註入 BookMapper 並使用它。

可能有小伙伴註意到了,這幾個註解中都有這麼一段代碼

 @AliasFor(
        annotation = Component.class
 )
 String value() default "";

@AliasFor 是 Spring 框架的註解,它允許你在一個註解屬性上聲明別名。在 Spring 的許多核心註解中,@AliasFor 用於聲明一個或多個別名屬性。

舉個例子,在 @Controller, @Service, @Repository註解中,value() 方法上的 @AliasFor 聲明瞭一個別名屬性,它的目標註解是 @Component,具體的別名屬性是 value。也就是說,當我們在 @Controller, @Service, @Repository 註解上使用 value() 方法設置值時,實際上也就相當於在 @Component 註解上設置了 name 屬性的值。同時,這也表明瞭 @Controller, @Service, @Repository註解本身就是一個特殊的 @Component。

7. 將註解驅動的配置與XML驅動的配置結合使用

有沒有這麼一種可能,一個舊的Spring項目,裡面有很多舊的XML配置,現在你接手了,想要全部用註解驅動,不想再寫XML配置了,那應該怎麼相容呢?

假設我們有一個舊的Spring XML配置文件 old-config.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
 <bean id="oldBean" class="com.example.OldBean" />
</beans>

這個文件定義了一個名為 “oldBean” 的bean。

然後,我們編寫一個新的註解驅動的配置類:

package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource("classpath:old-config.xml")
public class NewConfig {
 @Bean
 public NewBean newBean() {
 return new NewBean();
 }
}

在這個新的配置類中,我們使用 @ImportResource 註解來引入舊的XML配置文件,並定義了一個新的bean “newBean”。@ImportResource("classpath:old-config.xml")告訴Spring在初始化AppConfig配置類時,去類路徑下尋找old-config.xml文件,並載入其中的配置。

當我們啟動應用程式時,Spring會創建一個 ApplicationContext,這個 ApplicationContext 會包含 old-config.xml 文件中定義的所有beans(例如 “oldBean”),以及 NewConfig 類中定義的所有beans(例如 “newBean”)。

package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(NewConfig.class);
 OldBean oldBean = context.getBean("oldBean", OldBean.class);
 NewBean newBean = context.getBean("newBean", NewBean.class);
 System.out.println(oldBean);
 System.out.println(newBean);
 }
}

在以上的main方法中,我們通過使用AnnotationConfigApplicationContext並傳入NewConfig.class作為參數,初始化了一個Spring上下文。在這個上下文中,既包含了從old-config.xml導入的bean,也包含了在NewConfig配置類中使用@Bean註解定義的bean。

所以,通過使用 @ImportResource,可以在新的註解配置中引入舊的XML配置,這樣就可以在不打斷舊的XML配置的基礎上逐步遷移至新的註解配置。

上面我們說到類路徑,什麼是類路徑?

resources目錄就是類路徑(classpath)的一部分。所以當我們說"類路徑下"的時候,實際上也包含了"resources“目錄。JVM在運行時,會把”src/main/resources"目錄下的所有文件和文件夾都添加到類路徑中。

例如有一個XML文件位於"src/main/resources/config/some-context.xml",那麼可以用以下方式來引用它:

@Configuration
@ImportResource("classpath:config/some-context.xml")
public class AppConfig {
 //...
}

這裡可以描述為在類路徑下的’config‘目錄中查找’some-context.xml'文件。

為什麼說JVM在運行時,會把"src/main/resources"目錄下的所有文件和文件夾都添加到類路徑中?

當你編譯並運行一個Java項目時,JVM需要知道去哪裡查找.class文件以及其他資源文件。這個查找的位置就是所謂的類路徑(Classpath)。類路徑可以包含文件系統上的目錄,也可以包含jar文件。簡單的說,類路徑就是JVM查找類和資源的地方。

在一個標準的Maven項目結構中,Java源代碼通常在src/main/java目錄下,而像是配置文件、圖片、靜態網頁等資源文件則放在src/main/resources目錄下。

當你構建項目時,Maven(或者其他的構建工具,如Gradle)會把src/main/java目錄下的.java文件編譯成.class文件,並把它們和src/main/resources目錄下的資源文件一起複制到項目的輸出目錄(通常是target/classes目錄)。

然後當你運行程式時,JVM會把target/classes目錄(即編譯後的src/main/java和src/main/resources)添加到類路徑中,這樣JVM就可以找到程式運行所需的類和資源了。

如果有一個名為application.properties的文件在src/main/resources目錄下,就可以使用類路徑來訪問它,就像這樣:classpath:application.properties。在這裡classpath:首碼告訴JVM這個路徑是相對於類路徑的,所以它會在類路徑中查找application.properties文件。因為src/main/resources在

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 前言 在C語言中,巨集定義是一種預處理指令,用於在代碼中定義和使用常量、函數或代碼片段的替代。 巨集定義使用`#define`關鍵字來定義,併在代碼中進行替換。巨集定義具有以下優點: 1. **簡化代碼**:巨集定義可以將一些常用的、重覆出現的代碼片段簡化為一個巨集名稱,提高代碼的可讀性和簡潔性。 2. ...
  • # 列印流 PrintStream 和 PrintWriter ![](https://img2023.cnblogs.com/blog/3008601/202306/3008601-20230604103522664-997405676.png) ![](https://img2023.cnblo ...
  • 操作工作簿 01 新建一個excel工作簿 #2023-4-17 import xlwings as xw # 啟動 excel,但不新建工作簿 app是什麼,app是excel程式本身 app = xw.App(visible=True,add_book=True) #新建一個工作簿 workbo ...
  • # Python 列表推導式:簡潔、高效的數據操作藝術 Python 的列表推導式,這個看似簡單的語法糖,實則內含無限威力。在 Python 代碼編寫中,列表推導式的靈活性和簡潔性讓它成為了不可或缺的一部分。在這篇文章中,我們將更全面、更深入地探討列表推導式,從基礎的概念認識,到各類進階的用法和操作 ...
  • 還記得在2019年的夏天曾經用 R 爬過一份廣州在 lianjia.com 放盤數據 ([博客1](https://www.cnblogs.com/yukiwu/p/10975337.html),[博客2](https://www.cnblogs.com/yukiwu/p/11271515.html ...
  • 在Java虛擬機中,類和對象是程式的基本組成單元。類定義了一組對象的共性特征和行為,是Java程式中最基本的代碼單元。而對象則是具體的實例,有自己獨特的狀態和行為。在JVM中,類和對象都需要進行存儲,因此瞭解類和對象的存儲基礎知識對於Java程式員來說是非常重要的。 ...
  • # 1.初識while迴圈 迴圈語句主要的作用是在多次處理具有相同邏輯的代碼時使用。while迴圈是Python提供的迴圈語句之一。 while迴圈的語法格式之一: ![image](https://img2023.cnblogs.com/blog/3179433/202306/3179433-20 ...
  • 1、gateway相關介紹 在微服務架構中,系統往往由多個微服務組成,而這些服務可能部署在不同機房、不同地區、不同功能變數名稱下。這種情況下,客戶端(例如瀏覽器、手機、軟體工具等)想要直接請求這些服務,就需要知道它們具體的地址信息,例如 IP 地址、埠號等。這種客戶端直接請求服務的方式存在很多的複雜問題。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...