1. 配置類的編寫與Bean的注冊
??XML
配置中,我們通常采用ClassPathXmlApplicationContext
,它能夠加載類路徑下的XML
配置文件來初始化Spring
應用上下文。然而,在注解驅(qū)動的配置中,我們則使用以Annotation
開頭和ApplicationContext
結(jié)尾的類,如AnnotationConfigApplicationContext
。AnnotationConfigApplicationContext
是Spring
容器的一種,它實現(xiàn)了ApplicationContext
接口。
??對比于 XML
文件作為驅(qū)動,注解驅(qū)動需要的是配置類。一個配置類就可以類似的理解為一個 XML
。配置類沒有特殊的限制,只需要在類上標注一個 @Configuration
注解即可。
我們創(chuàng)建一個 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
注解
我們創(chuàng)建一個配置類來注冊這個 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
”。
啟動并初始化注解驅(qū)動的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)
這個語句創(chuàng)建了一個Spring
的應用上下文,它是以配置類LibraryConfiguration.class
作為輸入的,這里明確指定配置類的Spring
應用上下文,適用于更一般的Spring
環(huán)境。
??對比一下ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
這個語句則是Spring Boot
應用的入口,啟動一個Spring Boot
應用。SpringApplication.run()
方法會創(chuàng)建一個Spring Boot
應用上下文(也就是一個SpringApplication
對象),這個上下文包含了Spring Boot
應用所有的Bean
和配置類,還有大量的默認配置。這個方法之后,Spring Boot
的自動配置就會起作用。你可以把SpringApplication.run()
創(chuàng)建的Spring Boot
上下文看作是更加功能豐富的Spring
上下文。
打印結(jié)果:

Java Programming
和Unknown
被打印,執(zhí)行成功。
注意:@SpringBootApplication
是一個復合注解,它等效于同時使用了@Configuration
,@EnableAutoConfiguration
和@ComponentScan
。這三個注解的作用是:
-
@Configuration
:指明該類是一個配置類,它可能會有零個或多個@Bean
注解,方法產(chǎn)生的實例由Spring
容器管理。 -
@EnableAutoConfiguration
:告訴Spring Boot
根據(jù)添加的jar
依賴自動配置你的Spring
應用。 -
@ComponentScan
:Spring Boot
會自動掃描該類所在的包以及子包,查找所有的Spring
組件,包括@Configuration
類。
??在非Spring Boot
的傳統(tǒng)Spring
應用中,我們通常使用AnnotationConfigApplicationContext
或者ClassPathXmlApplicationContext
等來手動創(chuàng)建和初始化Spring
的IOC
容器。
??"非Spring Boot
的傳統(tǒng)Spring
應用"是指在Spring Boot
項目出現(xiàn)之前的Spring
項目,這些項目通常需要手動配置很多東西,例如數(shù)據(jù)庫連接、事務管理、MVC
控制器等。這種類型的Spring
應用通常需要開發(fā)者對Spring
框架有深入的了解,才能做出正確的配置。
??Spring Boot
是Spring
項目的一個子項目,它旨在簡化Spring
應用的創(chuàng)建和配置過程。Spring Boot
提供了一系列的"起步依賴",使得開發(fā)者只需要添加少量的依賴就可以快速開始項目的開發(fā)。此外,Spring Boot
還提供了自動配置的特性,這使得開發(fā)者無需手動配置數(shù)據(jù)庫連接、事務管理、MVC
控制器等,Spring Boot
會根據(jù)項目的依賴自動進行配置。
??因此,"非Spring Boot
的傳統(tǒng)Spring
應用"通常需要手動創(chuàng)建和初始化Spring
的IOC
容器,比如使用AnnotationConfigApplicationContext
或ClassPathXmlApplicationContext
等。在Spring Boot
應用中,這個過程被自動化了,開發(fā)者只需要在main
方法中調(diào)用SpringApplication.run
方法,Spring Boot
就會自動創(chuàng)建和初始化Spring
的IOC
容器。SpringApplication.run(Application.class, args);
語句就是啟動Spring Boot
應用的關(guān)鍵。它會啟動一個應用上下文,這個上下文會加載所有的Spring
組件,并且也會啟動Spring
的IOC
容器。在這個過程中,所有通過@Bean
注解定義的bean
都會被創(chuàng)建,并注冊到IOC
容器中。
??有人說,那學習Spring Boot就好了,學什么Spring和Spring MVC啊,這不是落后了嗎
??Spring Boot
并不是Spring
框架的替代品,而是建立在Spring
框架之上的一種工具,它內(nèi)部仍然使用Spring
框架的很多核心技術(shù),包括Spring MVC
。所以,當我們在使用Spring Boot
時,我們實際上仍然在使用Spring MVC
來處理Web
層的事務。
??簡而言之,Spring MVC
是一個用于構(gòu)建Web
應用程序的框架,而Spring Boot
是一個用于簡化Spring
應用程序開發(fā)的工具,它內(nèi)部仍然使用了Spring MVC
。你在Spring Boot
應用程序中使用的@Controller
、@Service
、@Autowired
等注解,其實都是Spring
框架提供的,所以,原理性的東西還是需要知道。
2. 注解驅(qū)動IOC的依賴注入與XML依賴注入對比
我們就以上面的例子來說,假設配置類注冊了兩個bean
,并設置相關(guān)的屬性:
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
容器中。
如果不用注解,要實現(xiàn)相同功能的話,對應的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>
元素,分別用來創(chuàng)建Book
對象和Library
對象。在創(chuàng)建Book
對象時,我們使用了<property>
元素來設置title
和author
屬性。在創(chuàng)建Library
對象時,我們也使用了<property>
元素,但是這次我們使用了ref
屬性來引用已經(jīng)創(chuàng)建的Book
對象,這就相當于將Book
對象注入到Library
對象中。
3. Spring中組件的概念
??在Spring
框架中,當我們說 “組件” 的時候,我們通常指的是被Spring
管理的各種Java
對象,這些對象在Spring
的應用上下文中作為Bean
存在。這些組件可能是服務層的類、數(shù)據(jù)訪問層的類、控制器類、配置類等等。
??@ComponentScan
注解會掃描指定的包(及其子包)中的類,如果這些類上標注了@Component
、@Controller
、@Service
、@Repository
、@Configuration
等注解,那么Spring
就會為這些類創(chuàng)建Bean
定義,并將這些Bean
定義注冊到Spring
的應用上下文中。因此,我們通常說@ComponentScan
進行了"組件掃描",因為它掃描的是標注了上述注解的類,這些類在Spring
中都被視為組件。
??而這些注解標記的類,最終在Spring
的應用上下文中都會被創(chuàng)建為Bean
,因此,你也可以理解@ComponentScan
為"Bean
掃描"。但是需要注意的是,@ComponentScan
只負責掃描和注冊Bean
定義,Bean
定義就是元數(shù)據(jù)描述,包括了如何創(chuàng)建Bean
實例的信息。
總結(jié)一下,@ComponentScan
注解會掃描并注冊的"組件"包括:
- 標注了
@Component
注解的類 - 標注了
@Controller
注解的類(Spring MVC
中的控制器組件) - 標注了
@Service
注解的類(服務層組件) - 標注了
@Repository
注解的類(數(shù)據(jù)訪問層組件) - 標注了
@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
的名稱,它的默認規(guī)則是 “類名的首字母小寫”(例如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
,其實已經(jīng)幫我們寫了!只有組件和主程序不在一個共同的根包下,才需要顯式地使用 @ComponentScan
注解。由于 Spring Boot
的設計原則是“約定優(yōu)于配置”,所以推薦將主應用類放在根包下。
??在應用中,我們的組件(帶有 @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
的創(chuàng)建順序問題,"需要某個Bean
"通常體現(xiàn)在以下幾個方面:
-
依賴注入(
Dependency Injection
): 如果一個BeanA
的字段或者構(gòu)造方法被標注為@Autowired
或者@Resource
,那么Spring
就會嘗試去尋找類型匹配的BeanB
并注入到BeanA
中。在這個過程中,如果BeanB
還沒有被創(chuàng)建,那么Spring
就會先創(chuàng)建BeanB
的實例。
@Component
public class BeanA {
@Autowired
private BeanB beanB;
}
@Component
public class BeanB {
}
??BeanA
依賴于BeanB
。在這種情況下,當你嘗試獲取BeanA
的實例時,Spring
會首先創(chuàng)建BeanB
的實例,然后把這個實例注入到BeanA
中,最后創(chuàng)建BeanA
的實例。在這個例子中,BeanB
會先于BeanA
被創(chuàng)建。
??這種方式的一個主要優(yōu)點是,我們不需要關(guān)心Bean
的創(chuàng)建順序,Spring
會自動解決這個問題。這是Spring IoC
容器的一個重要特性,也是為什么它能夠使我們的代碼更加簡潔和易于維護的原因。
-
Spring
框架調(diào)用: 有些情況下,Spring
框架的一些組件或者模塊可能需要用到你定義的Bean
。比如,如果你定義了一個@Controller
,那么在處理HTTP
請求時,Spring MVC
就會需要使用到這個@Controller Bean
。如果這個時候Bean
還沒有被創(chuàng)建,那么Spring
也會先創(chuàng)建它的實例。
假設我們有一個名為BookController
的類,該類需要一個BookService
對象來處理一些業(yè)務邏輯。
@Controller
public class BookController {
@Autowired
private BookService bookService;
// 其他的控制器方法
}
BookService
類
@Service
public class BookService {
@Autowired
private BookMapper bookMapper;
// 一些業(yè)務邏輯方法
}
當Spring Boot
應用程序啟動時,以下步驟將會發(fā)生:
-
首先,
Spring
框架通過@ComponentScan
注解掃描類路徑,找到了BookController
、BookService
和BookMapper
等類,并為它們創(chuàng)建Bean
定義,注冊到Spring
的應用上下文中。 -
當一個請求到達并需要使用到
BookController
時,Spring
框架會嘗試創(chuàng)建一個BookController
的Bean
實例。 -
在創(chuàng)建
BookController
的Bean
實例的過程中,Spring
框架發(fā)現(xiàn)BookController
類中需要一個BookService
的Bean
實例(通過@Autowired
注解指定),于是Spring
框架會先去創(chuàng)建一個BookService
的Bean
實例。 -
同樣,在創(chuàng)建
BookService
的Bean
實例的過程中,Spring
框架發(fā)現(xiàn)BookService
類中需要一個BookMapper
的Bean
實例(通過@Autowired
注解指定),于是Spring
框架會先去創(chuàng)建一個BookMapper
的Bean
實例。 -
在所有依賴的
Bean
都被創(chuàng)建并注入之后,BookController
的Bean
實例最終被創(chuàng)建完成,可以處理來自用戶的請求了。
在這個過程中,BookController
、BookService
和BookMapper
這三個Bean
的創(chuàng)建順序是有嚴格要求的,必須按照他們之間的依賴關(guān)系來創(chuàng)建。只有當一個Bean
的所有依賴都已經(jīng)被創(chuàng)建并注入后,這個Bean
才能被創(chuàng)建。這就是Spring
框架的IoC
(控制反轉(zhuǎn))和DI
(依賴注入)的機制。
-
手動獲?。?/strong> 如果你在代碼中手動通過
ApplicationContext.getBean()
方法獲取某個Bean
,那么Spring
也會在這個時候創(chuàng)建對應的Bean
實例,如果還沒有創(chuàng)建的話。
??總的來說,"需要"一個Bean
,是指在運行時有其他代碼需要使用到這個Bean
的實例,這個"需要"可能來源于其他Bean
的依賴,也可能來源于框架的調(diào)用,或者你手動獲取。在這種需要出現(xiàn)時,如果對應的Bean
還沒有被創(chuàng)建,那么Spring
就會根據(jù)之前通過@ComponentScan
等方式注冊的Bean
定義,創(chuàng)建對應的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
的構(gòu)造方法中有一個是填寫basePackages
路徑的,可以接受一個或多個包的名字作為參數(shù),然后掃描這些包及其子包。

運行結(jié)果如下:

??在這個例子中,Spring
將會掃描 com.example
包及其所有子包,查找并注冊所有的 Bean
,達到和@ComponentScan
注解一樣的效果。
??我們也可以手動創(chuàng)建一個配置類來注冊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());
}
}
??我們創(chuàng)建了一個配置類LibraryConfiguration
,用于定義Book
和Library
這兩個bean
。然后以配置類LibraryConfiguration.class
作為輸入的來創(chuàng)建Spring
的IOC
容器(Spring
應用上下文就是Spring IOC
容器)。
運行結(jié)果和前面一樣。
??注意,在這個例子里,如果你寫
@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
的實例將會被創(chuàng)建并注冊兩次。正確的做法是,要么在配置類中通過@Bean
注解的方法創(chuàng)建Book
和Library
的實例,要么在Book
和Library
類上寫@Component
注解。如果不是第三方庫,我們一般選擇后者。
為什么要有配置類出現(xiàn)?所有的Bean上面使用@Component,用@ComponentScan注解掃描不就能解決了嗎?
??我們在使用一些第三方庫時,需要對這些庫進行一些特定的配置。這些配置信息,我們可能無法直接通過注解或者XML
來完成,或者通過這些方式完成起來非常麻煩。而配置類可以很好地解決這個問題。通過配置類,我們可以在Java
代碼中完成任何復雜的配置邏輯。
??假設你正在使用 MyBatis
,在這種情況下可能需要配置一個SqlSessionFactory
,在大多數(shù)情況下,我們無法(也不應該)直接修改第三方庫的代碼,所以無法直接在SqlSessionFactory
類或其他類上添加@Configuration
、@Component
等注解。為了能夠在Spring
中使用和配置這些第三方庫,我們需要創(chuàng)建自己的配置類,并在其中定義@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
方法創(chuàng)建一個SqlSessionFactoryBean
對象,并使用DataSource
(Spring Boot
默認為你配置的一個Bean
)進行初始化。然后,它指定MyBatis mapper XML
文件的位置,最后返回SqlSessionFactory
對象。
??通過這種方式,你可以靈活地配置MyBatis
,并將其整合到Spring
應用中。這是一種比使用XML
配置文件或僅僅依賴于自動配置更為靈活和強大的方式。
6. 組件注冊的其他注解
??@Controller
, @Service
, @Repository
和@Component
一樣的效果,它們都會被 Spring IoC
容器識別,并將類實例化為 Bean
。讓我們來看這些注解:
-
@Controller
:這個注解通常標注在表示表現(xiàn)層(比如Web
層)的類上,如Spring MVC
中的控制器。它們處理用戶的HTTP
請求并返回響應。雖然@Controller
與@Component
在功能上是類似的,但@Controller
注解的使用表示了一種語義化的分層結(jié)構(gòu),使得控制層代碼更加清晰。

-
@Service
:這個注解通常用于標注業(yè)務層的類,這些類負責處理業(yè)務邏輯。使用@Service
注解表明該類是業(yè)務處理的核心類,使得代碼更具有語義化。

-
@Repository
:這個注解用于標記數(shù)據(jù)訪問層,也就是數(shù)據(jù)訪問對象或DAO
層的組件。在數(shù)據(jù)庫操作的實現(xiàn)類上使用@Repository
注解,這樣Spring
將自動處理與數(shù)據(jù)庫相關(guān)的異常并將它們轉(zhuǎn)化為Spring
的DataAccessExceptions
。

??在實際開發(fā)中,幾乎很少看到@Repository
,而是利用 MyBatis
的 @Mapper
或 @MapperScan
實現(xiàn)數(shù)據(jù)訪問,通常做法是,@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
接口創(chuàng)建一個實現(xiàn)類(實際上是一個代理對象),并將其注冊到 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. 將注解驅(qū)動的配置與XML驅(qū)動的配置結(jié)合使用
??有沒有這么一種可能,一個舊的Spring
項目,里面有很多舊的XML
配置,現(xiàn)在你接手了,想要全部用注解驅(qū)動,不想再寫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
。
然后,我們編寫一個新的注解驅(qū)動的配置類:
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
會創(chuàng)建一個 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
作為參數(shù),初始化了一個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
)。類路徑可以包含文件系統(tǒng)上的目錄,也可以包含jar
文件。簡單的說,類路徑就是JVM
查找類和資源的地方。
??在一個標準的Maven
項目結(jié)構(gòu)中,Java
源代碼通常在src/main/java
目錄下,而像是配置文件、圖片、靜態(tài)網(wǎng)頁等資源文件則放在src/main/resources
目錄下。
??當你構(gòu)建項目時,Maven
(或者其他的構(gòu)建工具,如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. 思考總結(jié)
8.1 為什么我們需要注冊組件,這與Bean注冊有什么區(qū)別?
??在Spring
框架中,Bean
對象是由Spring IoC
容器創(chuàng)建和管理的。通常Bean
對象是應用程序中的業(yè)務邏輯組件,如數(shù)據(jù)訪問對象(DAO
)或其他服務類。
??組件注冊,或者說在Spring
中通過@Component
或者其派生注解(@Service
, @Controller
, @Repository
等)標記的類,是告訴Spring
框架這個類是一個組件,Spring
需要創(chuàng)建它的實例并管理它的生命周期。這樣當使用到這個類的時候,Spring
就可以自動地創(chuàng)建這個類的實例并注入到需要的地方。
??Bean
注冊和組件注冊其實是非常類似的,都是為了讓Spring
知道它需要管理哪些類的實例。區(qū)別在于Bean
注冊通常發(fā)生在配置類中,使用@Bean
注解來明確地定義每一個Bean
,而組件注冊則是通過在類上使用@Component
或者其派生注解來告訴Spring
,這個類是一個組件,Spring
應該自動地為其創(chuàng)建實例。
8.2 什么是組件掃描,為什么我們需要它,它是如何工作的?
??組件掃描是Spring
的一種機制,用于自動發(fā)現(xiàn)應用程序中的Spring
組件,并自動地為這些組件創(chuàng)建Bean
定義,然后將它們注冊到Spring
的應用上下文中,我們可以通過使用@ComponentScan
注解來啟動組件掃描。
??我們需要組件掃描是因為它可以大大簡化配置過程,我們不再需要為應用程序中的每個類都顯式地創(chuàng)建Bean
。而是通過簡單地在類上添加@Component
或者其派生注解,并啟動組件掃描,就可以讓Spring
自動地為我們的類創(chuàng)建Bean
并管理它們。文章來源:http://www.zghlxwxcb.cn/news/detail-460717.html
??組件掃描的工作過程如下:使用@ComponentScan
注解并指定一個或多個包路徑時,Spring
會掃描這些包路徑及其子包中的所有類。對于標記了@Component
或者其派生注解的類,Spring
會在應用上下文啟動時為它們創(chuàng)建Bean
,并將這些Bean
定義注冊到Spring
的應用上下文中。當需要使用這些類的實例時,Spring
就可以自動注入這些實例。文章來源地址http://www.zghlxwxcb.cn/news/detail-460717.html
歡迎一鍵三連~
有問題請留言,大家一起探討學習
----------------------Talk is cheap, show me the code-----------------------
到了這里,關(guān)于Spring高手之路——深入理解注解驅(qū)動配置與XML配置的融合與區(qū)別的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!