一、前言
今天,我们来学习基于注解的配置方法,代替XML配置文件进行Bean的管理。包括介绍如何使用注解来声明Bean以及实现依赖注入,同时记录如何使用配置类替代XML文件进行全注解开发。
二、内容
2.1 回顾
之前讲过,Spring的IoC(Inversion of Control)容器通过XML配置文件的方式,管理应用程序中的对象(也就是Bean)的生命周期和依赖关系。IoC容器负责实例化、配置、组装和管理这些Bean,开发者则不再需要手动管理对象的创建和依赖注入。
今天,我们来学习更简单的配置方式,即基于注解(Annotation)的配置,不需要XML文件,而配置后让 Spring 自动扫描Bean并组装它们。
2.2 声明Bean
(1)@Component
@Component 是 Spring 框架中用于表示一个类是 Bean 的注解之一。使用了@Component 注解就相当于定义了一个 Bean 。
具体来说,当 Spring IoC 容器启动时,它会自动扫描指定的包,找到带有
@Component注解的类,并将它们实例化为 Bean。

@Component 有一个 value 属性,该属性的作用实际上就跟之前一样,用来指定 bean 的 id的作用。

@Component注解就相当于定义了一个bean,该bean有一个可选的名称,默认是小写开头的类名。
(2)@Controller、@Service、@Repository
这三个注解 @Controller、@Service、@Repository 都是 Spring 框架中的特殊化注解,它们都派生自 @Component 注解,用于更明确地表示类的用途或角色。
简单来说,就是 @Component 注解的别名,实际上功能都一样,只不过用了该注解后程序可读性更强。
@Controller:用于标识控制器层(Controller)的类,通常处理用户请求和返回视图。

@Service:用于标识服务层(Service)的类,通常包含应用的业务逻辑。

@Repository:用于标识数据访问层(Repository)的类,通常用于数据库操作。

总的来说,这三个注解的作用与 @Component 一样,都是为了将类标识为 Spring 管理的bean组件,只不过可以在不同的层次上提供更加明确的语义,方便开发者更清晰地区分和理解应用程序中各个类的作用。
(3)举例
注意,在使用注解时,需要使用Spring 的 AOP 功能。因此我们需要在
pom.xml文件中添加spring-context依赖后,进而关联加入aop的依赖。
下面,我们来看简单的注解例子。
比如,现在我们写一个 UserDao 类,在类上使用@Component 注解,表示这里定义了一个Bean 。

接着,在XML配置文件中这样配置:

下面来看测试结果:

可以看到,我们成功获取了bean。
下面有两个注意点。
首先,在类上使用@Component 注解时,可以省略value属性,不用显示的标明。比如这样:

这也是可以的。
第二个注意点是,当我们不给@Component 注解的value属性进行赋值,那么Spring会给这个bean自动取名,默认为Bean类的首字母小写。如下所示:


(4)多包解决方案

如上所示,如果涉及到多个包下的bean扫描,有两种方法。
第一种方法,在 XML 配置中,<context:component-scan> 元素的 base-package 属性可以包含一个逗号分隔的字符串,其中包含了所有需要扫描的包名。

第二种方法,就是指定多个包的共同父包,就不用显式列出每个包。

(5)控制组件扫描
这部分作为扩展知识。
前面说过,使用@Controller、@Service、@Repository注解都派生自 @Component 注解,类似 @Component 注解的别名,功能一样,只不过可读性更高。
在一些特殊的业务需求下,我们可能会希望实例化某一个包下所有标注了 @Controller 注解的类,而其他类型注解(比如@Controller、@Service等)的Bean则不进行实例化。
那么在这种情况下,可以有两种方案。
方案一:禁用默认规则,仅实例化 @Controller 注解的类
比如:
xml
<context:component-scan base-package="com.example.dao" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在这里,我们通过设置 use-default-filters 为 false 禁用了默认规则,然后使用 <context:include-filter> 明确指定了只有标注了 @Controller 注解的类才会被实例化。其他类型的组件将被排除。
方案二:使用默认规则,排除不需要实例化的注解类型
比如:
xml
<context:component-scan base-package="com.example.dao">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在这里,我们使用默认规则(use-default-filters 默认为 true),但通过 <context:exclude-filter> 排除了标注了 @Repository、@Service、@Controller 注解的类,从而达到只实例化 @Controller 类的目的。
2.3 使用注解注入
(1)@value
使用@Value注解可以进行简单类型属性的注入,如下所示:

可以看到,我们使用@Value注解直接标注在字段上,这样,Spring容器在初始化 Bean的时候,会自动将值注入到相应的字段中。
另外,我们还可以将使用在 set 方法上,也可以使用在构造器的参数中。如下所示:


(2)@Autowired
使用@Autowired 注解就相当于把指定类型的Bean注入到指定的属性当中。
我们来看源码:

@Autowired注解表示自动装配(自动连线),可以用来注入非简单类型 ,单独使用@Autowired注解时,默认根据类型装配 (byType)。该注解可以标注在很多地方,包括构造方法上、方法上、形参上、属性上以及注解上。
如果将
@Autowired注解的required属性设置为false,表示注入的Bean如果存在,就注入;不存在的话,也不报错。
下面我们来看 @Autowired 的基本使用。
首先我们看下示例一:标注在属性(字段)上。

运行后也是没问题的:

当然,@Autowired 注解还可以标注在很多地方,如下所示:

另外,还可以标注在构造方法的形参上,比如:

需要注意的是,@Autowired注解默认根据类型注入的(byType),因此,如果UserDao接口还有另外一个实现类:

那就会有问题,如下所示:

这时,我们就不用默认的根据类型注入(byType),而是根据名称(byName)进行注入。
我们需要联合使用@Qualifier注解来根据名称进行自动装配,即在@Qualifier注解中指定Bean名称。

我们来总结一下,@Autowired注解可以标注在bean的属性上、setter方法上、构造方法上、构造方法的参数上,默认是根据类型注入(byType)。如果有多个bean冲突,无法自动装配,那么需要结合@Qualifier注解来根据名称注入(byName)。
2.4 使用全注解开发
(1)配置类
现在,我们可以不再编写XML配置文件,而是写一个配置类,如下所示:

这时,我们在编写程序时,需要 new一个 AnnotationConfigApplicationContext 对象,而不是 ClassPathXmlApplicationContext对象。

(2)@Configuration
使用@Configuration注解标识的类就是一个配置类,用于代替原先的XML配置文件。
我们来看一下源码:

配置类其实就是一个普通的 Java 类,在类上用@Configuration 注解来标注后,表明该类是一个配置类,通过配置类可以定义和组装 Spring Bean。
(3)@ComponentScan
@ComponentScan 注解用于 bean 扫描配置,替代原有XML配置文件中的<context:component-scan base-package=""/>,我们可以指定一个或多个包名,Spring会扫描指定包及其子包下使用注解的类。
如果不配置包名,那么扫描当前
@componentScan注解配置类所在包及其子包下的类。
源码如下:

三、总结
总的来说,基于注解的配置方式极大的简化了开发,使得配置更加简洁。每个Bean都通过@Component来声明,以及使用 @Autowired进行注入,同时,为了提高程序的可读性,我们可以使用@Controller、@Service、@Repository 来代替@Component,实质上是一样的。
下一节,我们来学习 @Resource 注解,分析该注解和@Component注解的区别。