注解 简化bean配置

?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
              
     <context:annotation-config/>
    
</beans>

(隐式注册 post-processors 包括了 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,也包括了前面提到的 RequiredAnnotationBeanPostProcessor。)
@Autowired

@Autowired 注解可以用于“传统的”setter 方法,如下例:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
这个注解也可以用于以属性为参数/多个参数的方法

public class MovieRecommender {

    private MovieCatalog movieCatalog;
   
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
prepare这个方法添加了自动拼装,因此spring在初始化这个bean的时候会调用这个方法。

@Autowired注解甚至可以用于构造器与字段:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;
   
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
也可以一种提供来自ApplicationContext的特殊类型的所有 beans,注解字段或者方法,例如:

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}
这同样适用于集合类型:

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;
   
    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
甚至是 Maps 也可以这样注解,只要这个 Map 的 key 类型为 String。这个 Map 的 values 应该是已知的类型,并且 keys 应该包含符合 bean 的命名:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;
   
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
在缺省情况下,当出现0个候选的 beans时自动连接将失败;缺省行为把连接方法,构造器,字段假设为 required 的依赖。这样的行为如下所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Note
虽然当 一个类只有一个连接构造器时它将被标记为 required, 但是还是可以标记多个构造器的。在这种情况下,每一个构造器都有可能被认为是连接构造器, Spring 将会把依赖关系能够满足的构造器认为是greediest 的构造器。

@Autowired也能用于总所周知的“可解决的依赖”:BeanFactory接口,ApplicationContext接口,ResourceLoader接口,ApplicationEventPublisher接口,还有MessageSource接口。这些接口(还有它们的扩展,例如ConfigurableApplicationContext或者ResourcePatternResolver)将可以自动解决依赖,没有任何特殊必须的其它步骤需要。

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

另外自动注解
标注@Autowired注解的Bean并不会自动进行装配,它需要一个配套的处理器,既AutowiredAnnotationBeanPostProcessor,该Bean后置处理器会在Spring容器启动时自动为标住@Autowired注解的Bean实施自动装配.
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
或者使用
<context:annotation-config />代替。它将自动完成注解配置的依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--
<bean id="accountService"
class="com.liyixing.spring.service.imp.AccountService"> <lookup-method
name="look" bean="downloadService" /> </bean> <bean
id="downloadService"
class="com.liyixing.spring.service.imp.DownloadService"
scope="prototype"> </bean> <bean id="messageService"
class="com.liyixing.spring.service.imp.MessageService"> </bean>
<context:annotation-config />
--><context:annotation-config />
</beans>

Test.java
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "beans.xml" });
System.out.println(context.getBeanDefinitionCount());
String[] beanNames = context.getBeanDefinitionNames();

for(String beanName : beanNames) {
System.out.println(beanName);
}
可以看到结果是



自动定义了依赖的bean

基于注解的自动连接微调
因为通过类型的自动连接可能会有多个候选,因此经常需要在选择过程中加以控制。一种方法去完成这个控制就是使用@Qualifier注解。在最简单的情况下,您能够通过命名方式去实现这个自动连接:

public class MovieRecommender {

    @Autowired
    @Qualifier("mainCatalog")
    private MovieCatalog movieCatalog;

    // ...
}
@Qualifier注解也能够被指定为构造器的参数或者方法的参数:

public class MovieRecommender {

    private MovieCatalog movieCatalog;
   
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("mainCatalog") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
您也可以创建您自定义的限定器注解。您只要在定义一个注解时提供@Qualifier注解就可以了:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();

}
然后您就能够将这个自定义的限定器与参数用于自动连接的字段:

public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;
   
    private MovieCatalog comedyCatalog;
   
    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...
}
下一步就是提供信息给候选的 bean 的定义。您能够添加<qualifier/>标签作为<bean/>标签的子元素,然后指定'type'还有'value'以匹配您的自定义限定器注解。类型必须匹配注解的全名,或者是一个不危险的、方便一点的名字,您也可以使用“短” 类名。参看下例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
   
    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

在某些情况下,有足够充分的理由去使用不带值的注解。这使得注解可以提供更多解决不同类型依赖的能力。例如,在 Internet 连接不可用时,您可以提供一个离线的搜索目录。首先就要定义一个简单的注解:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}
然后添加这个注解给字段作为自动连接:

public class MovieRecommender {

    @Autowired
    @Offline
    private MovieCatalog offlineCatalog;

    // ...
}
现在,这个 bean 的定影只组要一个限定器了:

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/>
    <!-- inject any dependencies required by this bean -->
</bean>
另外,也可以定制自己的限定器注解去使用命名的属性或者简单的'value'属性。如果自动连接时多个属性值被指定给了一个字段或者参数,那么一个 bean 的定义必须全部匹配这些属性的值。例如,考虑如下的注解定义:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();
   
    Format format();

}
在这种情况下,Format是一个枚举:

public enum Format {
   
    VHS, DVD, BLURAY
}
这些字段将与自定义的限定器进行自动连接,包括了每个属性的值:'genre' 以及 'format'。

public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;
  
    // ...
}
最终,这个 bean 的定义应该与限定器匹配的值。这个列子将说明bean 的元属性可以用于替代<qualifier/>的子元素。那样的话,<qualifier/>以及它的属性将优先考虑,但是如果没有限定器的话(参看如下定义的后两个 bean ),自动连接机制将取消以<meta/>标签标记的值。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute name="format" value="VHS"/>
            <attribute name="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute name="format" value="VHS"/>
            <attribute name="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
   
    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

组件化的注解@Component
spring2.0开始支持@Repository注解,标记用来充当库存。包括了@Repository,@Service和@Controller。而这三个注解则是@Component的细化。分别对应了持久层(@Repository),服务层(@Service),控制层或叫表现层(@Controller)。选择使用细化的注解而不是使用@Component则更好。
如:
@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

检测这些类并注册相应的bean,需要在XML中包含以下元素,其中'basePackage'是两个类的公共父包 (或者可以用逗号分隔的列表来分别指定包含各个类的包)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
              
     <context:component-scan base-package="org.example"/>
    
</beans>

此外,在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,连个组件都会被自动检测并织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。
Note
通过加入值为'false'的annotation-config属性可以禁止注册这些后置处理器。(<context:annotation-config>false</context:annotation-config>)

使用过滤器自定义扫描
默认情况下,用@Component、 @Repository、@Service或 @Controller (或本身使用了@Component注解的自定义注解) 注解的类是唯一会被检测到的候选组件。但是可以很方便地通过自定义过滤器来改变并扩展这一行为。 可以用'component-scan'的include-filter或 exclude-filter子元素来进行添加。 每个过滤器元素都要求有'type'和'expression'属性。 下面给出了四个已有的可选过滤器。

annotation
org.example.SomeAnnotation 具有指定的注解的
assignable
org.example.SomeClass 指定的类
regex
org\.example\.Default.* 正则匹配
aspectj
org.example..*Service+ aspectj方式

<beans ...>

     <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
     </context:component-scan>

</beans>
会忽略@Repository注解,会扫描stub的下面的库

也可以用<component-scan/>元素的use-default-filters="false" 属性来禁用默认的过滤器。这会关闭对使用了@Component、 @Repository、@Service或 @Controller的类的自动检测。

自动检测组件的命名
当一个组件在某个扫描过程中被自动检测到时,会根据那个扫描器的BeanNameGenerator 策略生成它的bean名称。默认情况下,任何包含name值的Spring“典型”注解 (@Component、@Repository、 @Service和@Controller) 会把那个名字提供给相关的bean定义。如果这个注解不包含name值或是其他检测到的组件 (比如被自定义过滤器发现的),默认bean名称生成器会返回小写开头的非限定(non-qualified)类名。 例如,如果发现了下面这两个组件,它们的名字会是'myMovieLister'和'movieFinderImpl':
@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}

@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

如果你不想使用默认bean命名策略,可以提供一个自定义的命名策略。首先实现 BeanNameGenerator 接口,确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定(fully-qualified)类名:
<beans ...>
              
     <context:component-scan base-package="org.example"
                             name-generator="org.example.MyNameGenerator" />

</beans>

为自动检测的组件提供一个作用域
通常受Spring管理的组件,默认或者最常用的作用域是“singleton”。然而,有时也会需要其他的作用域。 因此Spring 2.5还引入了一个新的@Scope注解。只要在注解中提供作用域的名称就行了, 比方说:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}


@Resource

Spring 也提供了使用 JSR-250 bean 属性支持的注射方式。这是一种在 Java EE 5 与 Java 6 中普遍使用的方式(例如,在 JSF 1.2 中映射 beans 或者 JAX-WS 2.0 端点),对于Spring 托管的对象 Spring 可以以这种方式支持映射。

@Resource有一个‘name’属性,缺省时,Spring 将这个值解释为要注射的 bean 的名字。换句话说,如果遵循by-name的语法,如下例:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
如果没有显式地给出名字,缺省的名字将继承于字段名或者 setter 方法名:如果是字段名,它将简化或者等价于字段名;如果是 setter 方法名,它将等价于 bean 属性名。下面这个例子使用名字 "movieFinder" 注射到它的 setter 方法:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
Note
注解提供的名字将被BeanFactory解析为 bean 名。请注意,这些名字也可能通过 JNDI 被解析(需要配置 Spring 的SimpleJndiBeanFactory)。不过,建议您依靠缺省行为与 Spring 的 JNDI 查找功能。

与@Autowired类似,@Resource可以回退为与标准 bean 类型匹配(例如,使用原始类型匹配取代特殊命名 bean)来解决著名的"resolvable dependencies":BeanFactory 接口,ApplicationContext 接口,ResourceLoader 接口,ApplicationEventPublisher 接口以及 MessageSource 接口。请注意:这只有适用于未指定命名的@Resource!

下面的例子有一个customerPreferenceDao字段,首先要查找一个名叫 “customerPreferenceDao” 的 bean,然后回退为一个原始类型以匹配类型CustomerPreferenceDao。"context" 字段将基于已知解决的依赖类型ApplicationContext而被注入。

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

@PostConstruct 与 @PreDestroy
CommonAnnotationBeanPostProcessor 不只是能识别@Resource注解,而且也能识别 JSR-250 lifecycle注解。

CommonAnnotationBeanPostProcessor已经在 Spring 的ApplicationContext中注册,当一个方法带有这些注解之一时,将被在其生命周期与 Spring 生命周期接口的方法或者显式声明回调方法同一刻上调用。下面的例子里,缓存将预置于初始化与销毁阶段。

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }
   
    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

猜你喜欢

转载自liyixing1.iteye.com/blog/1037999