主頁 > 後端開發 > 深入理解注解驅動配置與XML配置的融合與區別

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

2023-06-08 07:56:35 後端開發

摘要:本文旨在深入探討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=https://www.cnblogs.com/huaweiyun/archive/2023/06/07/"Java Programming"/>
 <property name="author" value=https://www.cnblogs.com/huaweiyun/archive/2023/06/07/"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=https://www.cnblogs.com/huaweiyun/archive/2023/06/07/"Java Programming"/>
 <property name="author" value=https://www.cnblogs.com/huaweiyun/archive/2023/06/07/"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在運行時被添加到了類路徑,所以JVM能找到這個檔案,

8. 思考總結

8.1 為什么我們需要注冊組件,這與Bean注冊有什么區別?

在Spring框架中,Bean物件是由Spring IoC容器創建和管理的,通常Bean物件是應用程式中的業務邏輯組件,如資料訪問物件(DAO)或其他服務類,

組件注冊,或者說在Spring中通過@Component或者其派生注解(@Service, @Controller, @Repository等)標記的類,是告訴Spring框架這個類是一個組件,Spring需要創建它的實體并管理它的生命周期,這樣當使用到這個類的時候,Spring就可以自動地創建這個類的實體并注入到需要的地方,

Bean注冊和組件注冊其實是非常類似的,都是為了讓Spring知道它需要管理哪些類的實體,區別在于Bean注冊通常發生在配置類中,使用@Bean注解來明確地定義每一個Bean,而組件注冊則是通過在類上使用@Component或者其派生注解來告訴Spring,這個類是一個組件,Spring應該自動地為其創建實體,

8.2 什么是組件掃描,為什么我們需要它,它是如何作業的?

組件掃描是Spring的一種機制,用于自動發現應用程式中的Spring組件,并自動地為這些組件創建Bean定義,然后將它們注冊到Spring的應用背景關系中,我們可以通過使用@ComponentScan注解來啟動組件掃描,

我們需要組件掃描是因為它可以大大簡化配置程序,我們不再需要為應用程式中的每個類都顯式地創建Bean,而是通過簡單地在類上添加@Component或者其派生注解,并啟動組件掃描,就可以讓Spring自動地為我們的類創建Bean并管理它們,

組件掃描的作業程序如下:使用@ComponentScan注解并指定一個或多個包路徑時,Spring會掃描這些包路徑及其子包中的所有類,對于標記了@Component或者其派生注解的類,Spring會在應用背景關系啟動時為它們創建Bean,并將這些Bean定義注冊到Spring的應用背景關系中,當需要使用這些類的實體時,Spring就可以自動注入這些實體,

 

點擊關注,第一時間了解華為云新鮮技術~

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554532.html

標籤:其他

上一篇:【python爬蟲實戰】用python爬取愛奇藝電視劇十大榜單的全部資料!

下一篇:返回列表

標籤雲
其他(160513) Python(38215) JavaScript(25478) Java(18209) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7235) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4585) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1983) 功能(1967) HtmlCss(1952) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 深入理解注解驅動配置與XML配置的融合與區別

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

    uj5u.com 2023-06-08 07:56:35 more
  • 【python爬蟲實戰】用python爬取愛奇藝電視劇十大榜單的全部資料

    [toc] # 一、爬取目標 本次爬取的目標是,愛奇藝電視劇類目下的10個榜單:[電視劇風云榜-愛奇藝風云榜](https://www.iqiyi.com/ranks1/2/0) ?![愛奇藝頁面](https://img2023.cnblogs.com/blog/2864563/202306/28 ......

    uj5u.com 2023-06-08 07:55:31 more
  • 【Python&RS】遙感影像的像素坐標轉地理坐標(仿射變換)

    ? GDAL(Geospatial Data Abstraction Library)是一個在X/MIT許可協議下的開源柵格空間資料轉換庫。它利用抽象資料模型來表達所支持的各種檔案格式。它還有一系列命令列工具來進行資料轉換和處理。 Python的GDAL庫作為柵格資料的處理轉換庫,其支持幾百種柵格數 ......

    uj5u.com 2023-06-08 07:55:17 more
  • Python&Excel辦公自動化

    操作作業簿 01 新建一個excel作業簿 #2023-4-17 import xlwings as xw # 啟動 excel,但不新建作業簿 app是什么,app是excel程式本身 app = xw.App(visible=True,add_book=True) #新建一個作業簿 workbo ......

    uj5u.com 2023-06-08 07:55:12 more
  • Python 串列推導式:簡潔、高效的資料操作藝術

    # Python 串列推導式:簡潔、高效的資料操作藝術 Python 的串列推導式,這個看似簡單的語法糖,實則內含無限威力。在 Python 代碼撰寫中,串列推導式的靈活性和簡潔性讓它成為了不可或缺的一部分。在這篇文章中,我們將更全面、更深入地探討串列推導式,從基礎的概念認識,到各類進階的用法和操作 ......

    uj5u.com 2023-06-08 07:55:07 more
  • 鏈家廣州二手房資料 2023

    還記得在2019年的夏天曾經用 R 爬過一份廣州在 lianjia.com 放盤資料 ([博客1](https://www.cnblogs.com/yukiwu/p/10975337.html),[博客2](https://www.cnblogs.com/yukiwu/p/11271515.html ......

    uj5u.com 2023-06-08 07:55:01 more
  • 【python基礎】回圈陳述句-while回圈

    # 1.初識while回圈 回圈陳述句主要的作用是在多次處理具有相同邏輯的代碼時使用。while回圈是Python提供的回圈陳述句之一。 while回圈的語法格式之一: ![image](https://img2023.cnblogs.com/blog/3179433/202306/3179433-20 ......

    uj5u.com 2023-06-08 07:54:50 more
  • 【pandas基礎】--資料統計

    在進行統計分析時,`pandas`提供了多種工具來幫助我們理解資料。 `pandas`提供了多個聚合函式,其中包括均值、標準差、最大值、最小值等等。 此外,`pandas`還可以進行基于列的統計分析,例如通過`groupby()`函式對資料進行聚合,并計算每組的統計分析結果。 除了基本的統計分析之外 ......

    uj5u.com 2023-06-08 07:54:42 more
  • 【技識訓累】Python中的Pandas庫【二】

    博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ......

    uj5u.com 2023-06-08 07:54:38 more
  • 【python基礎】if陳述句-處理串列

    通過結合使用if陳述句和串列,可以完成一些程式上的需求,比如,對串列中特定的值做特殊處理 # 1.測驗特殊值 比如我們有一個學員名單,其中'劉柏宏'入學比較晚,需要我們重點關注,撰寫程式如下所示: ![image](https://img2023.cnblogs.com/blog/3179433/20 ......

    uj5u.com 2023-06-08 07:54:29 more