本文分享自华为云社区《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
运用程序发动时,以下步骤将会产生:
-
首要,
Spring
结构经过@ComponentScan
注解扫描类途径,找到了BookController
、BookService
和BookMapper
等类,并为它们创立Bean
界说,注册到Spring
的运用上下文中。 -
当一个恳求抵达并需求运用到
BookController
时,Spring
结构会尝试创立一个BookController
的Bean
实例。 -
在创立
BookController
的Bean
实例的进程中,Spring
结构发现BookController
类中需求一个BookService
的Bean
实例(经过@Autowired
注解指定),所以Spring
结构会先去创立一个BookService
的Bean
实例。 -
同样,在创立
BookService
的Bean
实例的进程中,Spring
结构发现BookService
类中需求一个BookMapper
的Bean
实例(经过@Autowired
注解指定),所以Spring
结构会先去创立一个BookMapper
的Bean
实例。 -
在一切依靠的
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
就能够主动注入这些实例。
点击重视,第一时间了解华为云新鲜技能~