- 名师讲坛:Spring实战开发(Redis+SpringDataJPA+SpringMVC+SpringSecurity)
- 李兴华
- 2108字
- 2021-03-30 21:09:16
3.8 基于Annotation配置管理
Spring中,所有的Bean都必须通过配置文件进行管理。这样处理的优势在于可以利用配置文件实现程序控制,劣势在于大型项目中可能出现配置文件过多的情况。为了弥补这一设计缺陷,Spring提供了Annotation配置支持。
提示:关于配置文件与Annotation。
Spring早期版本比较强调配置文件与程序相分离的设计原则。随着Spring项目的不断增多,开发人员面临着大量配置文件的维护工作,所以后期的Spring版本提供了Annotation注解配置。利用这些注解配置和一些规则,可以避免配置文件过多,但配置文件并不会彻底消失。为了平衡配置文件的数量,Spring还提供了配置Bean类,即可通过Java类实现配置。这一点在本章后面的部分可以看见。
为了方便读者理解Spring注解配置与实际开发的关联关系,下面将通过一个简单的业务层与数据层(不使用数据库)调用模拟形式,来进行讲解。调用关系如图3-3所示。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer667.jpg?sign=1739129265-5zU92VrzsZGAGFtBZitj7lbqjxVIZrVD-0-e8f363f656670d97d77a6455070b3404)
图3-3 实际调用还原
3.8.1 context扫描配置
要想使用Spring中的注解配置,需要先配置注解类的扫描包。也就是说,配置包下的所有注解都会自动生效,扫描包的配置则需要在项目中引入context命名空间,如图3-4所示。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer668.jpg?sign=1739129265-AUHEoWwCPaZGo3jNrYEck5KY5Z0SSKFo-0-468d28a405634c32b489e7db1aae57d4)
图3-4 配置中引入context注解
引入context配置之后,还需要使用<context:component-scan>元素配置程序的扫描包,这样配置包以及其子包下的所有程序类就都可以实现注解配置了。
范例:【mldnspring-base项目】修改spring-base.xml配置文件。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer669.jpg?sign=1739129265-rhE5QRkJZT3IY22mrkTNuz3hpNL87DpR-0-187552d04fd4b036ec63756799857a8e)
早期Spring版本中,要使用注解配置,需要先定义<context:annotation-config />元素,表示启用注解。随着版本提升,现在已经不需要进行配置了。在定义<context:component-scan>元素的扫描包时可以同时定义多个扫描包,使用“,”进行分隔。
提示:程序开发包。
本程序定义的扫描基础包为cn.mldn.mldnspring,所以后续的DAO实现类必须定义在cn.mldn.mldnspring.dao.impl子包中,业务层实现类定义在cn.mldn.mldnspring.service.impl子包中,这样就可以自动扫描这些类上的注解,从而实现自动配置。
3.8.2 资源扫描与注入
注解配置环境搭建完成后,如果想采用注解形式实现Bean配置,还需要在配置类上使用如下的4个注解(全部等价于<bean>功能)。
定义组件:org.springframework.stereotype.Component。
数据层注解:org.springframework.stereotype.Repository。
业务层注解:org.springframework.stereotype.Service。
控制层注解:org.springframework.stereotype.Controller。
提示:4个注解功能相同。
@Repository、@Service、@Controller 3个注解,如果开发者打开源代码,会发现其中都包含了@Component注解(等价于配置文件中的<bean>元素定义,自动交由Spring管理)。实际上这4个注解的功能是完全相同的,Spring中为了描述的准确性,才设计了不同的名称。
1.【mldnspring-base项目】定义IDeptDAO接口。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer670.jpg?sign=1739129265-Nzzesse8IjO3nqw2RAH83tU4cUvoMbGx-0-9774b8ae448fd14f65931c31a643929a)
2.【mldnspring-base项目】定义IDeptDAO接口实现子类,该类将通过注解配置。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer671.jpg?sign=1739129265-jem2kwCuDIIHZJ7n1oSK6u7cx41LiLCl-0-6c0a3239fde27bceec83c46a14ca9565)
由于数据层需要进行持久化处理,所以本程序使用了@Repository注解进行Bean的定义。
提示:关于Bean的名称。
上述程序虽然使用了@Repository注解,但却等价于如下Spring配置项:
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer672.jpg?sign=1739129265-4pLKPMpR8gMbAqeIqM4msGJjJvLlew5M-0-89dbb464da95f7825e62a41a047207fa)
即注解配置时默认的Bean名称为类名称(首字母小写)。
3.【mldnspring-base项目】定义IDeptService业务接口。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer673.jpg?sign=1739129265-5wNoo3ELHQklXsUVvhqGr5m6bUAywjrL-0-76b74bc9923c06acb23f3355c790e07e)
4.【mldnspring-base项目】定义IDeptService接口实现子类。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer674.jpg?sign=1739129265-N3ZYyG0mrGm7wodw6SombLn5DVNtPB3H-0-da05a9d028191d16172a47ae27badc0c)
由于DeptDAOImpl子类上使用了@Repository注解,所以该类对象将由Spring自动管理。在业务层中使用@Resource注解实现了IDeptDAO子类对象的注入处理。DeptServiceImpl类上也提供有@Service注解,所以该类对象也将被Spring自动管理。
5.【mldnspring-base项目】编写测试类,注入IDeptService接口实例。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer675.jpg?sign=1739129265-jYWTHkaL4MzAT7nWhd5kU7J5hd3aIUH8-0-99bab4e3a56144a1097646d5d97f7502)
测试程序会自动启动Spring容器,加载spring-base.xml配置文件,并根据context扫描包配置自动获取IDeptService接口实例,这样测试类中就可以直接调用业务方法了。这种做法与Spring在实际开发之中的处理形式非常类似。
3.8.3 @Autowired注解
进行资源注入的时候,我们使用的是javax.annotation.Resource注解,但此注解并不是Spring的官方注解。Spring开发框架中还有一个org.springframework.beans.factory.annotation.Autowired注解,在不出现重名Bean的情况下,两者的效果是完全一样的。
范例:【mldnspring-base项目】修改之前的程序类,使用@Autowired注解替代@Resource注解。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer676.jpg?sign=1739129265-egrMH3G8XA2RnZoUOLbZnFSUlbbavcY1-0-d9ae45e4f385d4d084f5db2fea95ef73)
此时的程序依然可以正常执行,即可以根据类型自动实现Bean对象的注入管理。但当Bean有两个不同的实例化对象时,将无法准确地进行注入处理。
范例:【mldnspring-base项目】修改spring-base.xml配置文件,采用手动方式增加一个DeptServiceImpl子类配置。
<bean id="deptServiceNew" class="cn.mldn.mldnspring.service.impl.DeptServiceImpl"/>
这里,程序采用两种配置方式,定义了两个DeptServiceImpl子类对象。这种情况下,不管使用的是@Autowired还是@Resource类型注入,执行时都会出现如下错误提示信息:
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer677.jpg?sign=1739129265-Tdw2FdOUYeU93W0oEHPWuMKig09ebicl-0-77db0eedd81708958a05d21e928f17f6)
该错误信息明确表示存在两个同样类型的IDeptService对象,Spring无法区分要注入的是哪个对象。对于该问题,有以下3种解决方案。
解决方案1:使用@Autowired注解,并采用优先选择配置。
前面在讲Bean配置的时候,曾经讲过自动匹配处理,可以在配置文件中使用primary="true"进行优先选择配置,此配置可以直接在类中使用@Primary注解完成。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer678.jpg?sign=1739129265-wNSoJIsf8Depdj3HZuKLuWNXbLf1bvPH-0-e08136175b11e470772a557a6c8afaee)
此时将优先选择注解配置的Bean类,这样就不会出现Bean冲突问题。
解决方案2:使用@Resource注解,定义引入Bean名称。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer679.jpg?sign=1739129265-dbRYfPOwqNQqtQPNPwV488m0EVF1JolA-0-eb6726f9c12680fce26272f7f8dd5f1e)
解决方案3:联合使用@Autowired注解和@Qualifier注解。
@Resource虽然可以解决重名Bean的问题,但由于部分开发者认为其并不是Spring提供的注解,所以更愿意使用@Autowired。为了解决这个问题,在Spring开发框架中还可以使用@Qualifier注解来标注待注入的对象名称。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer680.jpg?sign=1739129265-ex2BBdojl8Ak0RAC58KWXFaXmsPPp3Mh-0-cbe7e6922b31d402670a1943134f7642)
如果程序直接使用@Autowired,将无法确定要导入的是哪个Bean对象。使用@Qualifier可以指明要导入的Bean名称,从而避免混淆。
提示:@Resource注解与@Autowired注解的区别。
通过分析可以发现,实际上,@Resource=@Autowired+@Qualifier。默认情况下,@Resource与@Autowired会根据类型(byType)自动匹配注入对象。类型相同时,可通过名称(byName)进行匹配,@Resource可直接通过name属性设置Bean名称,@Autowired必须结合@Qualifier注解来设置名称。
3.8.4 使用Java类进行配置
项目中,如果觉得配置文件过多易导致配置混乱,可以使用Java程序类来实现Bean的配置处理。此模式基于context扫描包进行配置,同时需要用到@Configuration注解。
范例:【mldnspring-base项目】在扫描包中定义配置Bean。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer681.jpg?sign=1739129265-zA0MxHw2FetPk9woa0YX19bcdK09Dcpx-0-2f3abcf83869c79c5fb0f80162f07b1d)
本程序将配置类直接保存到了context扫描子包中,由于使用@Configuration注解,所以会自动将该类中有@Bean注解的配置项交由Spring容器管理。其中,@Bean(name="deptDAONew")注解的作用等同于在配置文件中编写了如下配置项:
<bean id="deptDAONew" class="cn.mldn.mldnspring.dao.impl.DeptDAOImpl" />
利用Bean实现的配置相对简单,在Spring微架构开发中有着广泛应用。