名师讲坛:Spring实战开发(Redis+SpringDataJPA+SpringMVC+SpringSecurity)
上QQ阅读APP看书,第一时间看更新

3.8 基于Annotation配置管理

Spring中,所有的Bean都必须通过配置文件进行管理。这样处理的优势在于可以利用配置文件实现程序控制,劣势在于大型项目中可能出现配置文件过多的情况。为了弥补这一设计缺陷,Spring提供了Annotation配置支持。

提示:关于配置文件与Annotation。

Spring早期版本比较强调配置文件与程序相分离的设计原则。随着Spring项目的不断增多,开发人员面临着大量配置文件的维护工作,所以后期的Spring版本提供了Annotation注解配置。利用这些注解配置和一些规则,可以避免配置文件过多,但配置文件并不会彻底消失。为了平衡配置文件的数量,Spring还提供了配置Bean类,即可通过Java类实现配置。这一点在本章后面的部分可以看见。

为了方便读者理解Spring注解配置与实际开发的关联关系,下面将通过一个简单的业务层与数据层(不使用数据库)调用模拟形式,来进行讲解。调用关系如图3-3所示。

图3-3 实际调用还原

3.8.1 context扫描配置

要想使用Spring中的注解配置,需要先配置注解类的扫描包。也就是说,配置包下的所有注解都会自动生效,扫描包的配置则需要在项目中引入context命名空间,如图3-4所示。

图3-4 配置中引入context注解

引入context配置之后,还需要使用<context:component-scan>元素配置程序的扫描包,这样配置包以及其子包下的所有程序类就都可以实现注解配置了。

范例:【mldnspring-base项目】修改spring-base.xml配置文件。

早期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接口。

2.【mldnspring-base项目】定义IDeptDAO接口实现子类,该类将通过注解配置。

由于数据层需要进行持久化处理,所以本程序使用了@Repository注解进行Bean的定义。

提示:关于Bean的名称。

上述程序虽然使用了@Repository注解,但却等价于如下Spring配置项:

即注解配置时默认的Bean名称为类名称(首字母小写)。

3.【mldnspring-base项目】定义IDeptService业务接口。

4.【mldnspring-base项目】定义IDeptService接口实现子类。

由于DeptDAOImpl子类上使用了@Repository注解,所以该类对象将由Spring自动管理。在业务层中使用@Resource注解实现了IDeptDAO子类对象的注入处理。DeptServiceImpl类上也提供有@Service注解,所以该类对象也将被Spring自动管理。

5.【mldnspring-base项目】编写测试类,注入IDeptService接口实例。

测试程序会自动启动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注解。

此时的程序依然可以正常执行,即可以根据类型自动实现Bean对象的注入管理。但当Bean有两个不同的实例化对象时,将无法准确地进行注入处理。

范例:【mldnspring-base项目】修改spring-base.xml配置文件,采用手动方式增加一个DeptServiceImpl子类配置。

      <bean id="deptServiceNew" class="cn.mldn.mldnspring.service.impl.DeptServiceImpl"/> 

这里,程序采用两种配置方式,定义了两个DeptServiceImpl子类对象。这种情况下,不管使用的是@Autowired还是@Resource类型注入,执行时都会出现如下错误提示信息:

该错误信息明确表示存在两个同样类型的IDeptService对象,Spring无法区分要注入的是哪个对象。对于该问题,有以下3种解决方案。

解决方案1:使用@Autowired注解,并采用优先选择配置。

前面在讲Bean配置的时候,曾经讲过自动匹配处理,可以在配置文件中使用primary="true"进行优先选择配置,此配置可以直接在类中使用@Primary注解完成。

此时将优先选择注解配置的Bean类,这样就不会出现Bean冲突问题。

解决方案2:使用@Resource注解,定义引入Bean名称。

解决方案3:联合使用@Autowired注解和@Qualifier注解。

@Resource虽然可以解决重名Bean的问题,但由于部分开发者认为其并不是Spring提供的注解,所以更愿意使用@Autowired。为了解决这个问题,在Spring开发框架中还可以使用@Qualifier注解来标注待注入的对象名称。

如果程序直接使用@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。

本程序将配置类直接保存到了context扫描子包中,由于使用@Configuration注解,所以会自动将该类中有@Bean注解的配置项交由Spring容器管理。其中,@Bean(name="deptDAONew")注解的作用等同于在配置文件中编写了如下配置项:

     <bean id="deptDAONew" class="cn.mldn.mldnspring.dao.impl.DeptDAOImpl" /> 

利用Bean实现的配置相对简单,在Spring微架构开发中有着广泛应用。