在配置Spring时注解是否比XML更好?
基于注解配置的引入引出了一个问题——这种方式是否比基于XML的配置更好。简短的回答是视情况而定。长一点的回答是每种方法都有它的优点和缺点,通常是由开发者决定哪一种策略更适合他们。由于注解的定义方式,注解在它们的声明中提供了许多上下文,导致配置更简短更简洁。然而,XML擅长连接组件而不必接触源代码或重新编译它们。一些开发者更喜欢接近源代码,而另一些人则认为基于注解的类不再是POJOs,此外,配置变的去中心化,而且更难控制。
无论选择是什么,Spring都能容纳这两种风格,甚至可以将它们混合在一起。值得指出的是,通过它的Java配置选项,Spring允许注解以一种非入侵的方式使用,不触碰目标组件源码和那些工具,所有的配置风格由Spring工具套件支持。
基于注解的配置提供了一种XML设置的可替代方式,它依赖于字节码元数据来连接组件,而不是用尖括号声明的方式。代替使用XML来描述bean连接,开发者通过将注解使用在相关的类,方法或字段声明中,将配置移动到了组件类本身的内部。正如在“Example: The RequiredAnnotationBeanPostProcessor”那节提到的那样,使用BeanPostProcessor与注解结合是扩展Spring IoC容器的的常见方法。例如,Spring 2.0引入了@Required注解来执行需要的属性的可能性。Spring 2.5使以同样地通用方法来驱动Spring的依赖注入变为可能。本质上来说,@Autowired提供了如3.4.5小节描述的同样的能力。“Autowiring collaborators”但更细粒度的控制和更广的应用性。Spring 2.5也添加对JSR-250注解的支持,例如,@PostConstruct和@PreDestroy
。Spring 3.0添加了对JSR-330,包含在javax.inject包内的注解(Java的依赖注入)的支持,例如@Inject和@Named。关于这些注解的细节可以在相关的小节找到。
注解注入在XML注入之前进行,因此对于通过两种方法进行组装的属性后者的配置会覆盖前者。
跟以前一样,你可以作为单独的bean定义来注册它们,但也可以通过在一个基于XML的Spring配置(注入包含上下文命名空间)中包含下面的标签来隐式的注册它们:
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
(隐式注册的后处理器包括 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor。)
仅在定义它的同样的应用上下文中寻找注解的beans。这意味着,如果你在一个为DispatcherServlet服务的WebApplicationContext中放置了,它只能在你的控制器中寻找@Autowired注解的beans,而不是在你的服务层中。更多信息请看18.2小节,“The DispatcherServlet”。
3.9.1 @Required
@Required注解应用到bean属性的setter方法上,例子如下:
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
这个注解仅仅是表明受影响的bean属性必须在配置时通过显式的bean定义或自动组装填充。如果受影响的bean属性没有填充,容器会抛出一个异常,这允许及早明确的失败,避免NullPointerExceptions或后面出现类似的情况。仍然建议你在bean类本身加入断言,例如,加入到初始化方法中。这样做可以强制这些需要的引用和值,甚至是你在容器外部使用这个类的时候。
2 @Autowired
在下面的例子中JSR 330的@Inject注解可以用来代替Spring的@Autowired注解。
你可以将@Autowired注解应用到构造函数上。
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
从Spring框架4.3起,如果目标bena仅定义了一个构造函数,那么@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; } // ... }
你也可以应用@Autowired到字段上,甚至可以与构造函数混合用:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
通过给带有数组的字段或方法添加@Autowired注解,也可以从ApplicationContext中提供一组特定类型的bean:
public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ... }
同样也可以应用到具有同一类型的集合上:
public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
如果你希望数组或列表中的项按指定顺序排序,你的bean可以实现org.springframework.core.Ordered接口,或使用@Order或标准@Priority注解。
只要期望的key是String,那么类型化的Maps就可以自动组装。Map的值将包含所有期望类型的beans,key将包含对应的bean名字:
public class MovieRecommender { private Map<String, MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
默认情况下,当没有候选beans可获得时,自动组装会失败;默认的行为是将注解的方法,构造函数和字段看作指明了需要的依赖。这个行为也可以通过下面的方式去改变。
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
每个类只有一个构造函数可以标记为必需的,但可以注解多个非必需的构造函数。在这种情况下,会考虑这些候选者中的每一个,Spring使用最贪婪的构造函数,即依赖最满足的构造函数,具有最大数目的参数。
建议在@Required注解之上使用@Autowired的required特性。required特性表明这个属性自动装配是不需要的,如果这个属性不能被自动装配,它会被忽略。另一方面@Required是更强大的,在它强制这个属性被任何容器支持的bean设置。如果没有值注入,会抛出对应的异常。
你也可以对那些已知的具有可解析依赖的接口使用@Autowired:BeanFactory,ApplicationContext,Environment, ResourceLoader,ApplicationEventPublisher和MessageSource。这些接口和它们的扩展接口,例如ConfigurableApplicationContext或ResourcePatternResolver,可以自动解析,不需要特别的设置。
public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... }
@Autowired、@Inject、@Resource和@Value都是通过Spring的BeanPostProcessor实现处理的,这反过来意味着,你不能在自己的BeanPostProcessor或BeanFactoryPostProcessor中使用这些注解。
这些类型必须显式通过XML或使用Spring的@Bean方法来装配。