前面已经多次用到了注解,如自动装配、扫描,bean初始化及销毁回调等
现在汇总并回顾一下:

1、 @Autowired 注解

要让@Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。

<!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->
<bean class="org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor"/>

Spring 2.5 引入了 @Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作:

package twm.spring;
import org.springframework.beans.factory.annotation.Autowired;

public class Test {
//下面是演示,正常开发中切记不要将几种方式作用在同一属性上。 

//直接标注在属性上
    @Autowired
    private Sender sender;
private User user;

//标注在 Setter 方法上
    @Autowired
    public void setSender(Sender sender) {
        this.sender = sender;
    }
//@Autowired 注解标注在构造函数上
@Autowired
    public Boss(Sender sender ,User user){
        this.sender = sender;
        this.user = user ;
    }
    //......
}

2、 @Qualifier 注解

当容器中存在同样类型的多个bean时,可以使用 @Qualifier 注解指定注入 Bean 的名称 :

@Autowired
public void setSender(@Qualifier("cellphoneSender")Sender sender) {
    this.sender = sender;
}

@Autowired 和@Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。注意:@Autowired 标注的是成员变量、方法以及构造函数,而@Qualifier 标注的是成员变量、方法入参、构造函数入参。

3、 @PostConstruct 和 @PreDestroy注解

《Bean生命周期回调:初始化回调和销毁回调 》中已用到了这两个注解。
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注解类,分别是 @PostConstruct 和 @PreDestroy,这两个注解只能应用于方法上。标注了 @PostConstruct 注解的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。

package twm.spring;
import org.springframework.beans.factory.annotation.Autowired;

public class Test {
    //......
@PostConstruct
    public void myInit(){
        System.out.println("postConstruct");
    }

    @PreDestroy
    public void myDestory(){
        System.out.println("preDestroy"); 
    }
}

通过实现 InitializingBean/DisposableBean 接口,以及通过 <bean> 元素的init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化/销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注解却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct 或 @PreDestroy 注解的方法都会在初始化 / 销毁时被执行。

4、@Resource

@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面@Resource 默认按 byName 自动注入。@Resource 有两个重要属性分别是 name 和 type。如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。都不写就默认按 byName 自动注入,如果找不到对应name的bean,则再按byType查找。

Resource 注解类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中,因此在使用之前必须将其加入到项目的类库中。

public class Test {
//下面是演示,正常开发中切记不要将几种方式作用在同一属性上。 

    @Resource(type="Sender.Class")
    private Sender sender;

//标注在 Setter 方法上
    @Resource(name="cellphoneSender")
    public void setSender(Sender sender) {
        this.sender = sender;
    }
}

注:@Resource、@PostConstruct 以及
@PreDestroy。它们不属于spring框架,而是在java中定义的。只不过spring框架支持这种java注解。

5、@Component

《自动检测扫描Bean》已经提到了通过注解可以自动扫描bean,并注册到容器中去,这样不需在XML 文件中通过 进行定义,从而减小XML体积,便于维护。 Spring提供了这样的注解:
@Component 表示一个组件 (Bean) ,可以作用在任何层次。
@Service 通常作用在业务层,功能与 @Component 相同。
@Constroller 通常作用在控制层,功能与 @Component 相同。
@Repository 通常作用在数据持久层,功能与 @Component 相同。
通过在类上使用 @Repository、@Component、@Service 和 @Constroller 注解,Spring 会自动创建相应的 BeanDefinition 对象,并注册到 ApplicationContext 中。这些类就成了 Spring 受管组件。
例:

@Scope("prototype")
@Component(name="cellphoneSender")
public class Sender {
    //.....实现
}

使用自动检测后扫描到的类,容器会将类名且第一个字母小写来作为bean的id。@Component 也提供了一个可选的入参name,用于指定 Bean 的名称。不过一般可不必指定,因为大多时侯都使用byType方式注入的。
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过@Scope 注解来达到目标。

在使用 @Component 注解后,Spring 容器要启用类扫描机制以启用注解驱动 Bean 定义和注解驱动 Bean 自动注入的策略。Spring对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:c="http://www.springframework.org/schema/c" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:component-scan base-package="twm.spring.start" />
</beans> 

这里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了。<context:component-scan/> 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

6、@Required注解

@Required注解应用于bean属性的setter方法,它表明影响的bean属性在配置时必须放在XML配置文件中。

@Required
public void setSender(Sender sender) {
        this.sender = sender;
}

这样如果bean定义中没有给sender传值,就会报错。

<bean id="order" class="twm.spring.start.Order" autowire="byType">
    <!-- <property name="sender" ref="cellphonesender"></property> -->
</bean>

比如把<property name="sender" ref="cellphonesender"></property>这句注释掉就会报错。

7、配置

值得注意的是:
当我们需要使用@Autowired注解,必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor的Bean:

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>

使用 @Required注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean:

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

类似地,使用@Resource、@PostConstruct、@PreDestroy等注解就必须声明 CommonAnnotationBeanPostProcessor;使用@PersistenceContext注解,就必须声明 PersistenceAnnotationBeanPostProcessor的Bean。

这样的声明太不优雅,而Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式,即使用<context:annotation- config/>隐式地向 Spring容器注册AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor这4个BeanPostProcessor。如下:

<context:annotation-config/> 

在我们使用注解时一般都会配置扫描包路径选项:

<context:component-scan base-package="pack.pack"/>

该配置项其实也包含了自动注入上述processor的功能,因此当使用<context:component-scan/>后,即可将<context:annotation-config/>也省去。

总结:

注解配置相对于 XML 配置具有很多的优势:

1、它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。
2、 如使用 JPA 注解配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO属性名、类型都一致,甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
3、注解和 Java代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java代码放在一起,有助于增强程序的内聚性。如果采用独立的 XML配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。

注解配置和 XML 配置的适用场合

是否有了这些 IOC 注解,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:

1、注解配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注解配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注解配置,因为注解是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。

2、如果 Bean不是自己编写的类(如 JdbcTemplate、SessionFactoryBean 等),注解配置将无法实施,此时 XML配置是唯一可用的方式。

3、注解配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction事务注解,使用 aop/tx 命名空间的事务配置更加灵活和简单。

所以在实现应用中,我们往往需要同时使用注解配置和 XML配置,对于类级别且不会发生变动的配置可以优先考虑注解配置;而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐