![名师讲坛:Spring实战开发(Redis+SpringDataJPA+SpringMVC+SpringSecurity)](https://wfqqreader-1252317822.image.myqcloud.com/cover/156/29977156/b_29977156.jpg)
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=1739068537-jr9078hx6qKc2wyFHsmRZnnBTfbm6CxH-0-9ccdb11e02d2002e815a33ab4c2ee448)
图3-3 实际调用还原
3.8.1 context扫描配置
要想使用Spring中的注解配置,需要先配置注解类的扫描包。也就是说,配置包下的所有注解都会自动生效,扫描包的配置则需要在项目中引入context命名空间,如图3-4所示。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer668.jpg?sign=1739068537-UUiC7oUsFug7D2NGt3ep2E5kGDh3pZf6-0-52b4f78981c72f7a950ae347f19ee6f4)
图3-4 配置中引入context注解
引入context配置之后,还需要使用<context:component-scan>元素配置程序的扫描包,这样配置包以及其子包下的所有程序类就都可以实现注解配置了。
范例:【mldnspring-base项目】修改spring-base.xml配置文件。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer669.jpg?sign=1739068537-njaRzBogVhXhE0JaGOTrsVtcLrI1vTkr-0-df3e0745a2334881bca0d0e5b2eec570)
早期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=1739068537-tonFEfjuPMagU5kPQoK1voM0gQhz2k4t-0-408162bd7aa32b3802f5460d4cdc064e)
2.【mldnspring-base项目】定义IDeptDAO接口实现子类,该类将通过注解配置。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer671.jpg?sign=1739068537-G7K3twwhU934zPuOkTUzRegR04asOK1E-0-540753a48a5090193710d97b0b084469)
由于数据层需要进行持久化处理,所以本程序使用了@Repository注解进行Bean的定义。
提示:关于Bean的名称。
上述程序虽然使用了@Repository注解,但却等价于如下Spring配置项:
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer672.jpg?sign=1739068537-MPqXEUzUBMy4DCZPy6WJi3bBz4LrYmTJ-0-f108229b6923a142b5c45a98fbc58d0f)
即注解配置时默认的Bean名称为类名称(首字母小写)。
3.【mldnspring-base项目】定义IDeptService业务接口。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer673.jpg?sign=1739068537-mjsCCC05XNfVUeKvRqerXjy7xU7lkMmr-0-9497b675798936f86ba4820c5d9903ab)
4.【mldnspring-base项目】定义IDeptService接口实现子类。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer674.jpg?sign=1739068537-Y1ytGJZd312GB0nDFPxJbTBAXp7S9GEn-0-3a26c44dc4ed7d2c8deef8458584bcd7)
由于DeptDAOImpl子类上使用了@Repository注解,所以该类对象将由Spring自动管理。在业务层中使用@Resource注解实现了IDeptDAO子类对象的注入处理。DeptServiceImpl类上也提供有@Service注解,所以该类对象也将被Spring自动管理。
5.【mldnspring-base项目】编写测试类,注入IDeptService接口实例。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer675.jpg?sign=1739068537-cT2QTSk2opTWny5G6ro1pSN8wd7MTY24-0-33a0979f8a94d5dd1d7a650038057dc5)
测试程序会自动启动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=1739068537-c6sqtQF70ZjTnT5OlCwBmdwW4X7xEIEl-0-f665cf740c01f89552029ac54eb84e2a)
此时的程序依然可以正常执行,即可以根据类型自动实现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=1739068537-LYbvFXFT9PyNLaMqxJJgvf2rmXRzkbrQ-0-f10c5617388116f052de68d3af8bffba)
该错误信息明确表示存在两个同样类型的IDeptService对象,Spring无法区分要注入的是哪个对象。对于该问题,有以下3种解决方案。
解决方案1:使用@Autowired注解,并采用优先选择配置。
前面在讲Bean配置的时候,曾经讲过自动匹配处理,可以在配置文件中使用primary="true"进行优先选择配置,此配置可以直接在类中使用@Primary注解完成。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer678.jpg?sign=1739068537-Ta2Xvc7PZDYjjvw7MBlZDPA4JBxibC6h-0-4d0e05fbb81ac756bae886129331307e)
此时将优先选择注解配置的Bean类,这样就不会出现Bean冲突问题。
解决方案2:使用@Resource注解,定义引入Bean名称。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer679.jpg?sign=1739068537-UMOaUf8WeKbiKNhadLtdiybDaEhUofxb-0-30fdaf91d9e774145c4d8e569e54b839)
解决方案3:联合使用@Autowired注解和@Qualifier注解。
@Resource虽然可以解决重名Bean的问题,但由于部分开发者认为其并不是Spring提供的注解,所以更愿意使用@Autowired。为了解决这个问题,在Spring开发框架中还可以使用@Qualifier注解来标注待注入的对象名称。
![](https://epubservercos.yuewen.com/5C1AE0/16499866905000506/epubprivate/OEBPS/Images/figer680.jpg?sign=1739068537-L6lrxdIKkil2YH37JNwMqNHSmlQRWFYb-0-c7e74ad3efe7aa2d85d2a4d8e0a8a8ca)
如果程序直接使用@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=1739068537-lna3dhOuEDlNyBSoRvpTAj8bRKih8AkG-0-d4fb73181bb404bbee0490dcd4b6323e)
本程序将配置类直接保存到了context扫描子包中,由于使用@Configuration注解,所以会自动将该类中有@Bean注解的配置项交由Spring容器管理。其中,@Bean(name="deptDAONew")注解的作用等同于在配置文件中编写了如下配置项:
<bean id="deptDAONew" class="cn.mldn.mldnspring.dao.impl.DeptDAOImpl" />
利用Bean实现的配置相对简单,在Spring微架构开发中有着广泛应用。