一、前言
今天,我们来学习基于注解的配置方法,代替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
注解的区别。