基于注释的配置的引入引起了这种方法是否比XML“更好”的问题。简短的回答是“它取决于。”长期的答案是每种方法都有其优势和缺点,一般,由开发人员决定哪一种策略更适合他们。因为它们的定义方式,注释在其声明中提供了大量上下文,从而致使更短更简洁的配置。可是,XML擅长在不触及源代码或从新编译它们的状况下链接组件。一些开发人员更喜欢将布线靠近源,而另外一些开发人员则认为注释类再也不是POJO,并且配置变得分散且难以控制。html
不管选择如何,Spring均可以兼顾两种风格,甚至能够将它们混合在一块儿。值得指出的是,经过其JavaConfig选项,Spring容许以非侵入方式使用注释,而无需触及目标组件源代码,而且在工具方面,Spring Tool Suite支持全部配置样式 。java
基于注释的配置提供了XML设置的替代方案,该配置依赖于字节码元数据来链接组件而不是角括号声明。开发人员不是使用XML来描述bean链接,而是经过在相关的类,方法或字段声明上使用注释将配置移动到组件类自己。如示例中所述:RequiredAnnotationBeanPostProcessor使用BeanPostProcessor与注释结合使用是扩展Spring IoC容器的经常使用方法。例如,Spring 2.0引入了使用@Required注释强制执行所需属性的可能性。Spring 2.5使得有可能采用相同的通用方法来驱动Spring的依赖注入。基本上,@Autowired注释提供与自动装配协做者中描述的相同的功能,但具备更细粒度的控制和更普遍的适用性。Spring 2.5还增长了对JSR-250注释的支持,例如 @PostConstruct和@PreDestroy。Spring 3.0增长了对javax.inject包中包含的JSR-330(Java的依赖注入)注释的支持,例如@Inject 和@Named。有关这些注释的详细信息,请参阅 相关章节。web
注释注入在XML注入以前执行。所以,XML配置会覆盖经过这两种方法链接的属性的注释。算法
与往常同样,您能够将它们注册为单独的bean定义,但也能够经过在基于XML的Spring配置中包含如下标记来隐式注册它们(请注意包含context命名空间):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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
复制代码
在隐式注册后处理器包括 AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,和前面提到的 RequiredAnnotationBeanPostProcessor。)编程
context:annotation-config/仅查找在定义它的同一应用程序上下文中的bean上的注释。这意味着,若是你 context:annotation-config/输入一个WebApplicationContextfor DispatcherServlet,它只会检查@Autowired你的控制器中的bean,而不是你的服务。有关更多信息,请参阅 DispatcherServlet。api
该@Required注释适用于bean属性setter方法,以下面的例子:数组
public class SimpleMovieLister {缓存
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
复制代码
此批注指示必须在配置时经过bean定义中的显式属性值或经过自动装配填充受影响的bean属性。若是还没有填充受影响的bean属性,则容器将引起异常。这容许急切和明确的失败,之后避免NullPointerException 实例等。咱们仍然建议您将断言放入bean类自己(例如,转换为init方法)。即便您在容器外部使用类,这样作也会强制执行那些必需的引用和值。安全
从@RequiredSpring Framework 5.1开始,注释正式被弃用,支持使用构造函数注入所需的设置(或者InitializingBean.afterPropertiesSet()bean属性setter方法的自定义实现 )。
在本节包含的示例中,JSR 330的@inject注释能够代替spring的@autowired注释。有关详细信息,请参阅此处。
能够将@autowired注释应用于构造函数,以下例所示:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
复制代码
从Spring Framework 4.3开始,@Autowired若是目标bean只定义了一个开头的构造函数,则再也不须要对这样的构造函数进行注释。可是,若是有几个构造器可用,则必须注释至少一个构造器以教导容器使用哪个。
您还能够将@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;
}
// ...
}
复制代码
您还能够ApplicationContext 经过将注释添加到须要该类型数组的字段或方法来提供特定类型的全部bean ,如如下示例所示:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
复制代码
您的目标bean能够实现org.springframework.core.ordered接口,或者若是您但愿数组或列表中的项按特定顺序排序,可使用@order或standard@priority注释。不然,它们的顺序遵循容器中相应目标bean定义的注册顺序。 您能够@Order在目标类级别和@Bean方法上声明注释,多是经过单个bean定义(在多个定义使用相同bean类的状况下)。@Order值可能会影响注入点的优先级,但要注意它们不会影响单例启动顺序,这是由依赖关系和@DependsOn声明肯定的正交关注点。
请注意,标准javax.annotation.Priority注释在该@Bean级别不可用 ,由于它没法在方法上声明。它的语义能够经过@Order值与@Primary每种类型的单个bean 相结合来建模。
Map只要预期的密钥类型是,即便是类型化的实例也能够自动装配String。Map值包含全部指望类型的bean,而且键包含相应的bean名称,如如下示例所示:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
复制代码
默认状况下,当没有匹配的候选bean可用于给定的注入点时,自动布线将失败。对于声明的数组、集合或映射,至少须要一个匹配元素。
默认行为是将带注释的方法和字段视为指示所需的依赖项。您能够更改此行为,如如下示例所示,使框架可以经过将其标记为非必需来跳过不可知足的注入点:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
复制代码
若是不可用的依赖项(或多个参数状况下的依赖项之一),则根本不会调用非必需的方法。在这种状况下,根本不会填充非必填字段,保留其默认值。
注入的构造函数和工厂方法参数是一种特殊状况,由于@Autowired因为Spring的构造函数解析算法可能涉及多个构造函数,所以'required'标志的含义有些不一样。默认状况下有效地须要构造函数和工厂方法参数,但在单构造函数场景中有一些特殊规则,例如,若是没有匹配的bean可用,则解析为空实例的多元素注入点(数组,集合,映射)。这容许一个通用的实现模式,其中全部依赖项均可以在一个惟一的多参数构造函数中声明,例如声明为没有@Autowired注释的单个公共构造函数。
每一个类只能标记一个带注释的构造函数,但能够注释多个非必需的构造函数。在这种状况下,每一个都被认为是候选者之一,Spring使用最贪婪的构造函数,其依赖性能够获得知足 - 也就是说,具备最多参数的构造函数。构造函数解析算法与具备重载构造函数的非注释类相同,只是将候选者缩小到带注释的构造函数。
建议使用“必需”属性而@Autowired不是@Requiredsetter方法的注释。“required”属性表示该属性不是自动装配所必需的。若是没法自动装配,则会忽略该属性。@Required另外一方面,它更强大,由于它强制经过容器支持的任何方式设置属性。若是未定义任何值,则会引起相应的异常。
或者,您能够经过Java 8表达特定依赖关系的非必需特性java.util.Optional,如如下示例所示:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
复制代码
从Spring Framework 5.0开始,您还可使用@Nullable注释(任何包中的任何类型的注释 - 例如,javax.annotation.Nullable来自JSR-305):
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
复制代码
您还可使用@Autowired对于那些众所周知的解析依赖接口:BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher,和MessageSource。这些接口及其扩展接口(如ConfigurableApplicationContext或ResourcePatternResolver)会自动解析,无需特殊设置。如下示例自动装配一个ApplicationContext对象:
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
复制代码
在
@Autowired
,@Inject
,@Value
,和@Resource
注释由Spring处理BeanPostProcessor
实现。这意味着您没法在本身的类型BeanPostProcessor
或BeanFactoryPostProcessor
类型(若是有)中应用这些注释。必须使用XML或Spring@Bean
方法显式地“链接”这些类型。
@Primary
因为按类型自动装配可能会致使多个候选人,所以一般须要对选择过程进行更多控制。实现这一目标的一种方法是使用Spring的@Primary
注释。@Primary
表示当多个bean能够自动装配到单值依赖项时,应该优先选择特定的bean。若是候选者中只存在一个主bean,则它将成为自动装配的值。
请考虑如下定义firstMovieCatalog
为主要的配置MovieCatalog
:
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
复制代码
使用上述配置,如下MovieRecommender
内容自动装配 firstMovieCatalog
:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
复制代码
相应的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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
复制代码
@Primary
当能够肯定一个主要候选者时,是经过具备多个实例的类型使用自动装配的有效方式。当您须要更多控制选择过程时,可使用Spring的@Qualifier
注释。您能够将限定符值与特定参数相关联,缩小类型匹配集,以便为每一个参数选择特定的bean。在最简单的状况下,这能够是一个简单的描述性值,如如下示例所示:
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
复制代码
您还能够@Qualifier
在各个构造函数参数或方法参数上指定注释,如如下示例所示:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
复制代码
@Primary
当能够肯定一个主要候选者时,是经过具备多个实例的类型使用自动装配的有效方式。当您须要更多控制选择过程时,可使用Spring的@Qualifier
注释。您能够将限定符值与特定参数相关联,缩小类型匹配集,以便为每一个参数选择特定的bean。在最简单的状况下,这能够是一个简单的描述性值,如如下示例所示:
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
复制代码
您还能够@Qualifier
在各个构造函数参数或方法参数上指定注释,如如下示例所示:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
复制代码
如下示例显示了相应的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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
复制代码
具备main 限定符值的bean与使用相同值限定的构造函数参数链接。 |
|
---|---|
具备action 限定符值的bean与使用相同值限定的构造函数参数链接。 |
对于回退匹配,bean名称被视为默认限定符值。所以,能够用一个定义bean id
的main
代替嵌套限定符元素,致使相同的匹配结果。可是,尽管您可使用此约定来按名称引用特定bean,但@Autowired
基本上是关于具备可选语义限定符的类型驱动注入。这意味着即便使用bean名称回退,限定符值在类型匹配集中也老是具备缩小的语义。它们在语义上不表示对惟一bean的引用id
。良好限定的值是main
或EMEA
或persistent
,表达独立于从所述豆的特定部件的特性id
,在匿名bean定义的状况下能够自动生成,例如前面例子中的定义。
限定符也适用于类型集合,如前所述 - 例如,to Set<MovieCatalog>
。在这种状况下,根据声明的限定符,全部匹配的bean都做为集合注入。这意味着限定符没必要是惟一的。相反,它们构成了过滤标准。例如,您能够MovieCatalog
使用相同的限定符值“action” 定义多个bean,全部这些bean都注入带Set<MovieCatalog>
注释的注释中@Qualifier("action")
。
在类型匹配候选项中,根据目标bean名称选择限定符值,不须要
@Qualifier
注入点处的注释。若是没有其余解析指示符(例如限定符或主要标记),则对于非惟一依赖性状况,Spring会将注入点名称(即字段名称或参数名称)与目标bean名称进行匹配,而后选择同名的候选人,若是有的话。也就是说,若是您打算按名称表达注释驱动的注入,请不要主要使用
@Autowired
,即便它可以在类型匹配候选项中经过bean名称进行选择。相反,使用JSR-250@Resource
注释,该注释在语义上定义为经过其惟一名称标识特定目标组件,声明的类型与匹配过程无关。@Autowired
具备至关不一样的语义:在按类型选择候选bean以后,String
仅在那些类型选择的候选中考虑指定的限定符值(例如,将account
限定符与标记有相同限定符标签的bean 匹配)。对于自身定义为集合
Map
或数组类型的bean来讲,这@Resource
是一个很好的解决方案,它经过惟一名称引用特定的集合或数组bean。也就是说,从4.3开始,只要在返回类型签名或集合继承层次结构中保留元素类型信息,就能够Map
经过Spring的@Autowired
类型匹配算法匹配和数组类型@Bean
。在这种状况下,您可使用限定符值在相同类型的集合中进行选择,如上一段所述。从4.3开始,
@Autowired
还考虑了自引用注入(即,引用回到当前注入的bean)。请注意,自我注入是一种后备。对其余组件的常规依赖性始终具备优先权。从这个意义上说,自我引用并不参与常规的候选人选择,所以特别是否是主要的。相反,它们老是最低优先级。在实践中,您应该仅使用自引用做为最后的手段(例如,经过bean的事务代理调用同一实例上的其余方法)。考虑在这种状况下将受影响的方法分解为单独的委托bean。或者,您可使用@Resource
,它能够经过其惟一名称获取代理回到当前bean。
@Autowired
适用于字段,构造函数和多参数方法,容许在参数级别经过限定符注释缩小范围。相比之下,@Resource
仅支持具备单个参数的字段和bean属性setter方法。所以,若是注射目标是构造函数或多参数方法,则应该使用限定符。
您能够建立本身的自定义限定符注释。为此,请定义注释并@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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.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>
复制代码
在类路径扫描和托管组件中,您能够看到基于注释的替代方法,即在XML中提供限定符元数据。具体来讲,请参阅使用注释提供限定符元数据。
在某些状况下,使用没有值的注释可能就足够了。当注释用于更通用的目的而且能够应用于多种不一样类型的依赖项时,这可能颇有用。例如,您能够提供可在没有Internet链接时搜索的脱机目录。首先,定义简单注释,如如下示例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
复制代码
而后将注释添加到要自动装配的字段或属性中,如如下示例所示:
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
复制代码
此行添加@Offline 注释。 |
---|
如今bean定义只须要一个限定符type
,以下例所示:
<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/>
元素及其属性优先,但<meta/>
若是不存在此类限定符,则自动装配机制将回退到标记内提供的值 ,如如下示例中的最后两个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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="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>
复制代码
除了@Qualifier
注释以外,您还可使用Java泛型类型做为隐式的限定形式。例如,假设您具备如下配置:
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
复制代码
假设前面的bean实现了一个通用接口(即Store<String>
和, Store<Integer>
),您能够@Autowire
将Store
接口和泛型用做限定符,以下例所示:
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
复制代码
除了@Qualifier
注释以外,您还可使用Java泛型类型做为隐式的限定形式。例如,假设您具备如下配置:
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
复制代码
假设前面的bean实现了一个通用接口(即Store<String>
和, Store<Integer>
),您能够@Autowire
将Store
接口和泛型用做限定符,以下例所示:
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
复制代码
通用限定符也适用于自动装配列表,Map
实例和数组。如下示例自动装配通用List
:
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
复制代码
CustomAutowireConfigurer
CustomAutowireConfigurer
是一个BeanFactoryPostProcessor
容许您注册本身的自定义限定符注释类型的,即便它们没有使用Spring的@Qualifier
注释进行注释。如下示例显示如何使用CustomAutowireConfigurer
:
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
复制代码
经过如下方式AutowireCandidateResolver
肯定autowire候选人:
autowire-candidate
每一个bean定义的值default-autowire-candidates
上可用的任何模式<beans/>
@Qualifier
注释的存在以及注册的任何自定义注释CustomAutowireConfigurer
当多个bean有资格做为autowire候选者时,“primary”的肯定以下:若是候选者中只有一个bean定义具备primary
设置为的属性true
,则选择它。
@Resource
Spring还经过在字段或bean属性setter方法上使用JSR-250 @Resource
annotation(javax.annotation.Resource
)来支持注入。这是Java EE中的常见模式:例如,在JSF管理的bean和JAX-WS端点中。Spring也支持Spring管理对象的这种模式。
@Resource
采用名称属性。默认状况下,Spring将该值解释为要注入的bean名称。换句话说,它遵循按名称语义,如如下示例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
复制代码
这条线注入了一个@Resource
。
若是未明确指定名称,则默认名称是从字段名称或setter方法派生的。若是是字段,则采用字段名称。在setter方法的状况下,它采用bean属性名称。下面的例子将把bean movieFinder
注入其setter方法:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
复制代码
提供注解的名称解析由一个bean的名称
ApplicationContext
,其中的CommonAnnotationBeanPostProcessor
知道。若是您SimpleJndiBeanFactory
明确配置Spring,则能够经过JNDI解析名称 。可是,咱们建议您依赖于默认行为并使用Spring的JNDI查找功能来保留间接级别。
在专属状况下,@Resource
不指定明确的名称,以及相似的使用@Autowired
,@Resource
发现的主要类型的比赛,而不是一个具体的bean并解决众所周知的解析依存关系:BeanFactory
, ApplicationContext
,ResourceLoader
,ApplicationEventPublisher
,和MessageSource
接口。
所以,在如下示例中,customerPreferenceDao
字段首先查找名为“customerPreferenceDao”的bean,而后返回到该类型的主要类型匹配 CustomerPreferenceDao
:
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
复制代码
context
根据已知的可解析依赖类型注入该字段: ApplicationContext
。
@PostConstruct
和@PreDestroy
将CommonAnnotationBeanPostProcessor
不只认可了@Resource
注解也是JSR-250的生命周期注解:javax.annotation.PostConstruct
和 javax.annotation.PreDestroy
。在Spring 2.5中引入,对这些注释的支持提供了初始化回调和 销毁回调中描述的生命周期回调机制的替代 方法。若是 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...
}
}
复制代码
有关组合各类生命周期机制的效果的详细信息,请参阅 组合生命周期机制。
例如
@Resource
,@PostConstruct
和@PreDestroy
注释类型是JDK 6到8的标准Java库的一部分。可是,整个javax.annotation
包与JDK 9中的核心Java模块分离,最终在JDK 11中删除。若是须要,javax.annotation-api
工件须要是如今经过Maven Central得到,只需像任何其余库同样添加到应用程序的类路径中。
1.10 1.11再补
本节介绍如何在Java代码中使用注释来配置Spring容器。它包括如下主题:
@Bean
和@Configuration
AnnotationConfigApplicationContext
@Bean
注释@Configuration
注释PropertySource
抽象化@PropertySource
@Bean
和@Configuration
Spring的新Java配置支持中的中心工件是 @Configuration
注释类和@Bean
注释方法。
该@Bean
注释被用于指示一个方法实例,配置和初始化为经过Spring IoC容器进行管理的新对象。对于那些熟悉Spring的<beans/>
XML配置的人来讲,@Bean
注释与<bean/>
元素扮演的角色相同。你能够@Bean
在任何Spring中使用-annotated方法 @Component
。可是,它们最经常使用于@Configuration
bean类。
对类进行注释@Configuration
代表其主要目的是做为bean定义的来源。此外,@Configuration
类容许经过调用@Bean
同一类中的其余方法来定义bean间依赖关系。最简单的@Configuration
类以下:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
复制代码
上面的AppConfig
类等效于如下Spring <beans/>
XML:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
复制代码
当@Bean
在未注释的类中声明方法时 @Configuration
,它们被称为以“精简”模式处理。在一个@Component
或甚至在一个普通的旧类中声明的Bean方法被认为是“精简”,包含类的主要目的不一样,而且@Bean
方法在那里是一种奖励。例如,服务组件能够经过@Bean
每一个适用组件类的附加方法将管理视图公开给容器。在这种状况下,@Bean
方法是通用的工厂方法机制。
与full不一样@Configuration
,lite @Bean
方法不能声明bean间依赖关系。相反,它们对其包含组件的内部状态进行操做,而且可选地,对它们能够声明的参数进行操做。@Bean
所以,这种方法不该该引用其余 @Bean
方法。每一个这样的方法实际上只是特定bean引用的工厂方法,没有任何特殊的运行时语义。这里的积极反作用是没必要在运行时应用CGLIB子类,所以在类设计方面没有限制(也就是说,包含类多是final
等等)。
在常见的场景中,@Bean
方法将在@Configuration
类中声明,确保始终使用“完整”模式,并所以将交叉方法引用重定向到容器的生命周期管理。这能够防止@Bean
经过常规Java调用意外地调用相同的 方法,这有助于减小在“精简”模式下操做时难以跟踪的细微错误。
的@Bean
和@Configuration
注解的深度在如下章节中讨论。首先,咱们将介绍使用基于Java的配置建立弹簧容器的各类方法。
@Bean
和@Configuration
注解的深度在如下章节中讨论。首先,咱们将介绍使用基于Java的配置建立弹簧容器的各类方法。
AnnotationConfigApplicationContext
如下部分AnnotationConfigApplicationContext
介绍了在Spring 3.0中引入的Spring。这种通用ApplicationContext
实现不只可以接受@Configuration
类做为输入,还能接受 @Component
使用JSR-330元数据注释的普通类和类。
当@Configuration
提供类做为输入时,@Configuration
类自己被注册为bean定义,而且@Bean
类中的全部声明的方法也被注册为bean定义。
当@Component
提供JSR-330类时,它们被注册为bean定义,而且假定DI元数据例如@Autowired
或@Inject
在必要时在这些类中使用。
简单的施工
与实例化a时Spring XML文件用做输入的方式大体相同 ClassPathXmlApplicationContext
,能够@Configuration
在实例化时使用类做为输入AnnotationConfigApplicationContext
。这容许彻底无XML使用Spring容器,如如下示例所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
复制代码
如前所述,AnnotationConfigApplicationContext
并不只限于使用@Configuration
类。@Component
能够将任何或JSR-330带注释的类做为输入提供给构造函数,如如下示例所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
复制代码
前面的例子中假定MyServiceImpl
,Dependency1
以及Dependency2
使用Spring依赖注入注解,例如@Autowired
。
register(Class<?>…)
您能够AnnotationConfigApplicationContext
使用no-arg构造函数实例化一个,而后使用该register()
方法对其进行配置。这种方法在以编程方式构建时特别有用AnnotationConfigApplicationContext
。如下示例显示了如何执行此操做:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
复制代码
scan(String…)
要启用组件扫描,您能够@Configuration
按以下方式注释您的类:
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
复制代码
此注释可启用组件扫描。
有经验的Spring用户可能熟悉与Spring context:
命名空间等效的XML声明,以下例所示:
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
复制代码
在前面的示例中,com.acme
扫描包以查找任何已 @Component
注释的类,并将这些类注册为容器中的Spring bean定义。AnnotationConfigApplicationContext
公开 scan(String…)
方法以容许相同的组件扫描功能,如如下示例所示:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
复制代码
请记住,
@Configuration
类是元注释 的@Component
,所以它们是组件扫描的候选者。在前面的示例中,假设AppConfig
在com.acme
包(或下面的任何包)中声明了它,它在调用期间被拾取scan()
。以后refresh()
,它的全部@Bean
方法都被处理并在容器中注册为bean定义。
AnnotationConfigWebApplicationContext
可用的WebApplicationContext
变体。在配置Spring servlet侦听器,Spring MVC等时 ,可使用此实现。如下代码段配置典型的Spring MVC Web应用程序(请注意context-param和init-param的使用):AnnotationConfigApplicationContext``AnnotationConfigWebApplicationContext``ContextLoaderListener``DispatcherServlet``web.xml``contextClass
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes. Fully-qualified packages may also be
specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
复制代码
@Bean
注释@Bean
是方法级注释和XML <bean/>
元素的直接模拟。注释支持一些提供的属性<bean/>
,例如:* init-method * destroy-method * autowiring * name
。
您能够在带@Bean
注释的类@Configuration
或带 注释的类中使用注释@Component
。
要声明bean,可使用注释注释方法@Bean
。您可使用此方法在ApplicationContext
指定为方法的返回值的类型中注册bean定义。默认状况下,bean名称与方法名称相同。如下示例显示了@Bean
方法声明:
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
复制代码
上述配置与如下Spring XML彻底等效:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
复制代码
这两个声明都将一个名为transferService
available 的bean命名为ApplicationContext
绑定到类型的对象实例,TransferServiceImpl
以下图所示:
transferService - > com.acme.TransferServiceImpl
您还能够@Bean
使用接口(或基类)返回类型声明您的方法,如如下示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
复制代码
可是,这会将高级类型预测的可见性限制为指定的接口类型(TransferService
)。而后,TransferServiceImpl
只有容器已知的完整类型()一次,已经实例化了受影响的单例bean。非延迟单例bean根据其声明顺序进行实例化,所以您可能会看到不一样的类型匹配结果,具体取决于另外一个组件什么时候尝试经过非声明类型进行匹配(例如@Autowired TransferServiceImpl
,只有transferService
在实例化bean以后才会解析)。
若是您始终经过声明的服务接口引用您的类型,则您的
@Bean
返回类型能够安全地加入该设计决策。可是,对于实现多个接口的组件或可能由其实现类型引用的组件,更可能声明可能的最具体的返回类型(至少与引用您的bean的注入点所需的具体相同)。
带@Bean
注释的方法能够有任意数量的参数来描述构建该bean所需的依赖关系。例如,若是咱们TransferService
须要a AccountRepository
,咱们可使用方法参数来实现该依赖关系,以下例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
复制代码
解析机制与基于构造函数的依赖注入很是类似。有关详细信息,请参阅相关部分。
使用@Bean
注释定义的任何类都支持常规生命周期回调,而且可使用JSR-250中的注释@PostConstruct
和@PreDestroy
注释。有关更多详细信息,请参阅 JSR-250注释。
彻底支持常规的Spring 生命周期回调。若是bean实现InitializingBean
,DisposableBean
或者Lifecycle
它们各自的方法由容器调用。
还彻底支持标准*Aware
接口集(例如BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等)。
该@Bean
注释支持指定任意初始化和销毁回调方法,就像春天XML的init-method
,并destroy-method
在属性上的bean
元素,以下例所示:
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
复制代码
默认状况下,使用Java配置定义的具备public
close
或shutdown
method的bean 会自动使用销毁回调登记。若是您有公共close
或shutdown
方法,而且您不但愿在容器关闭时调用它,则能够添加@Bean(destroyMethod="")
到bean定义以禁用默认(inferred)
模式。对于使用JNDI获取的资源,您可能但愿默认执行此操做,由于其生命周期在应用程序以外进行管理。特别是,确保始终为a执行此操做
DataSource
,由于已知它在Java EE应用程序服务器上存在问题。如下示例显示如何防止自动销毁回调
DataSource
:@Bean(destroyMethod="") public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS"); } 复制代码
此外,使用
@Bean
方法,您一般使用编程JNDI查找,经过使用SpringJndiTemplate
或JndiLocatorDelegate
帮助程序或直接JNDIInitialContext
用法但不使用JndiObjectFactoryBean
变量(这将强制您将返回类型声明为FactoryBean
类型而不是实际目标类型,使得更难以用于其余@Bean
打算在此处引用所提供资源的方法中的交叉引用调用。
在BeanOne
前面注释中的示例的状况下,init()
在构造期间直接调用该方法一样有效,如如下示例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
复制代码
当您直接使用Java工做时,您可使用对象执行任何您喜欢的操做,而且没必要老是依赖于容器生命周期。
Spring包含@Scope
注释,以便您能够指定bean的范围。
@Scope
注释您能够指定使用@Bean
注释定义的bean 应具备特定范围。您可使用Bean Scopes部分中指定的任何标准做用域 。
默认范围是singleton
,但您可使用@Scope
注释覆盖它,如如下示例所示:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
复制代码
@Scope
和 scoped-proxy
Spring提供了一种经过做用域代理处理做用域依赖项的便捷方法 。使用XML配置时建立此类代理的最简单方法是<aop:scoped-proxy/>
元素。使用@Scope
注释在Java中配置bean 提供了对该proxyMode
属性的等效支持。默认值为no proxy(ScopedProxyMode.NO
),但您能够指定ScopedProxyMode.TARGET_CLASS
或ScopedProxyMode.INTERFACES
。
若是将scoped代理示例从XML参考文档(请参阅范围代理)移植 到@Bean
使用Java,它相似于如下内容:
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences());
return service;
}
复制代码
默认状况下,配置类使用@Bean
方法的名称做为结果bean的名称。可是,可使用name
属性覆盖此功能,如如下示例所示:
@Configuration
public class AppConfig {
@Bean(name = "myThing")
public Thing thing() {
return new Thing();
}
}
复制代码
正如Naming Beans中所讨论的,有时须要为单个bean提供多个名称,也称为bean别名。 为此目的name
,@Bean
注释的属性接受String数组。如下示例显示如何为bean设置多个别名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
复制代码
有时,提供更详细的bean文本描述会颇有帮助。当bean(可能经过JMX)进行监视时,这可能特别有用。
要向a添加描述@Bean
,可使用 @Description
注释,如如下示例所示:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
复制代码
@Configuration
注释@Configuration
是一个类级别的注释,指示对象是bean定义的来源。@Configuration
classes经过公共@Bean
注释方法声明bean 。@Bean
对@Configuration
类上的方法的调用也可用于定义bean间依赖项。请参阅基本概念:@Bean
和@Configuration
通常性介绍。
当bean彼此依赖时,表达该依赖关系就像让一个bean方法调用另外一个bean同样简单,以下例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
复制代码
在前面的示例中,beanOne
接收对beanTwo
构造函数注入的引用。
这种声明bean间依赖关系的@Bean 方法只有在@Configuration 类中声明方法时才有效。您不能使用普通@Component 类声明bean间依赖项。 |
|
---|---|
这种声明bean间依赖关系的
@Bean
方法只有在@Configuration
类中声明方法时才有效。您不能使用普通@Component
类声明bean间依赖项。
如前所述,查找方法注入是一项不多使用的高级功能。在单例范围的bean依赖于原型范围的bean的状况下,它颇有用。将Java用于此类配置提供了实现此模式的天然方法。如下示例显示如何使用查找方法注入:
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
复制代码
经过使用Java配置,您能够建立一个子类,CommandManager
其中抽象createCommand()
方法被覆盖,以便查找新的(原型)命令对象。如下示例显示了如何执行此操做:
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// inject dependencies here as required
return command;
}
@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
复制代码
请考虑如下示例,该示例显示了@Bean
两次调用的带注释的方法:
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
复制代码
clientDao()
被称为一次进入clientService1()
和进入一次clientService2()
。因为此方法建立了一个新实例ClientDaoImpl
并将其返回,所以一般须要两个实例(每一个服务一个)。这确定会有问题:在Spring中,实例化的bean singleton
默认具备范围。这就是魔术的用武之地:全部@Configuration
类都在启动时被子类化CGLIB
。在子类中,子方法在调用父方法并建立新实例以前,首先检查容器是否有任何缓存(做用域)bean。
根据bean的范围,行为可能会有所不一样。咱们在这里谈论单身人士。 |
---|
从Spring 3.2开始,再也不须要将CGLIB添加到类路径中,由于CGLIB类已经从新打包
org.springframework.cglib
并直接包含在spring-core JAR中。
因为CGLIB在启动时动态添加功能,所以存在一些限制。特别是,配置类不能是最终的。可是,从4.3开始,配置类容许使用任何构造函数,包括使用
@Autowired
默认注入的单个非默认构造函数声明。
若是您但愿避免任何CGLIB强加的限制,请考虑
@Bean
在非@Configuration
类上声明您的方法(例如,在普通@Component
类上)。@Bean
而后拦截方法之间的跨方法调用,所以您必须彻底依赖于构造函数或方法级别的依赖注入。
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
复制代码
如今,不须要同时指定ConfigA.class
和ConfigB.class
实例化上下文,只ConfigB
须要显式提供,以下例所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
复制代码
这种方法简化了容器实例化,由于只须要处理一个类,而不是要求您@Configuration
在构造期间记住可能大量的 类。
从Spring Framework 4.2开始,
@Import
还支持引用常规组件类,相似于AnnotationConfigApplicationContext.register
方法。若是要避免组件扫描,这一点特别有用,可使用一些配置类做为明肯定义全部组件的入口点。
@Bean
定义的依赖性前面的例子有效,但很简单。在大多数实际状况中,bean跨配置类彼此依赖。使用XML时,这不是问题,由于不涉及编译器,而且您能够声明 ref="someBean"
并信任Spring在容器初始化期间解决它。使用@Configuration
类时,Java编译器会对配置模型施加约束,由于对其余bean的引用必须是有效的Java语法。
幸运的是,解决这个问题很简单。正如咱们已经讨论过的,一个@Bean
方法能够有任意数量的参数来描述bean的依赖关系。考虑如下更多真实场景,其中包含几个@Configuration
类,每一个类都依赖于其余类中声明的bean:
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
复制代码
还有另外一种方法能够达到相同的效果。请记住,
@Configuration
类最终只是容器中的另外一个bean:这意味着它们能够利用@Autowired
和@Value
注入以及与任何其余bean相同的其余功能。
确保以这种方式注入的依赖项只是最简单的类型。
@Configuration
在上下文初始化期间很早就处理了类,而且强制以这种方式注入依赖项可能会致使意外的早期初始化。尽量采用基于参数的注入,如前面的示例所示。
另外,要特别注意
BeanPostProcessor
和BeanFactoryPostProcessor
定义@Bean
。这些一般应该声明为static @Bean
方法,而不是触发其包含配置类的实例化。不然,@Autowired
而@Value
不要在配置类自己的工做,由于它是被做为一个bean实例建立为时尚
如下示例显示了如何将一个bean自动链接到另外一个bean:
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
@Autowired
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
复制代码
@Configuration
仅在Spring Framework 4.3中支持类中的 构造函数注入。另请注意,无需指定@Autowired
目标bean是否仅定义了一个构造函数。在前面的示例中,构造函数@Autowired
上没有必要RepositoryConfig
。
彻底符合条件的进口豆类,便于导航
在前面的场景中,使用@Autowired
效果很好并提供了所需的模块性,但肯定声明自动装配的bean定义的确切位置仍然有些模棱两可。例如,做为开发人员ServiceConfig
,您如何确切地知道@Autowired AccountRepository
bean的声明位置?它在代码中并不明确,这可能就行了。请记住, Spring Tool Suite提供的工具能够呈现图形,显示全部内容的连线方式,这可能就是您所须要的。此外,您的Java IDE能够轻松找到该AccountRepository
类型的全部声明和用法,并快速显示@Bean
返回该类型的方法的位置。
若是这种歧义是不可接受的,而且您但愿从IDE中直接从一个@Configuration
类导航到另外一个类,请考虑自行装配配置类自己。如下示例显示了如何执行此操做:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
复制代码
在前面的状况中,AccountRepository
定义的位置是彻底明确的。可是,ServiceConfig
如今牢牢联系在一块儿RepositoryConfig
。这是权衡。经过使用基于接口的或基于@Configuration
类的抽象类,能够在某种程度上减轻这种紧密耦合。请考虑如下示例:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
public interface RepositoryConfig {
@Bean
AccountRepository accountRepository();
}
@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(...);
}
}
@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
复制代码
如今ServiceConfig
与具体的松散耦合 DefaultRepositoryConfig
,内置的IDE工具仍然有用:您能够轻松得到实现的类型层次结构RepositoryConfig
。经过这种方式,导航@Configuration
类及其依赖关系与导航基于接口的代码的常规过程没有什么不一样。
若是要影响某些bean的启动建立顺序,能够考虑将它们中的一些声明为
@Lazy
(用于在第一次访问时建立而不是在启动时)或@DependsOn
某些其余bean(确保在当前bean以前建立特定的其余bean,超出后者的直接依赖意味着什么)。
@Configuration
类或@Bean
方法基于某些任意系统状态,有条件地启用或禁用完整@Configuration
类或甚至单个@Bean
方法一般颇有用。一个常见的例子是@Profile
只有在Spring中启用了特定的配置文件时才使用注释激活bean Environment
( 有关详细信息,请参阅Bean定义配置文件)。
该@Profile
注释是经过使用一种称为更灵活的注释实际执行@Conditional
。该@Conditional
注释指示特定org.springframework.context.annotation.Condition
前应谘询的实施@Bean
是注册。
Condition
接口的实现提供了一个matches(…)
返回true
或的方法false
。例如,如下清单显示了Condition
用于的实际 实现@Profile
:
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
// Read the @Profile annotation attributes
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
复制代码
有关@Conditional
更多详细信息,请参阅javadoc。
Spring的@Configuration
类支持并不是旨在成为Spring XML的100%彻底替代品。某些工具(如Spring XML命名空间)仍然是配置容器的理想方法。在XML方便或必要的状况下,您能够选择:例如,经过使用“以XML为中心”的方式实例化容器ClassPathXmlApplicationContext
,或者经过使用AnnotationConfigApplicationContext
和@ImportResource
注释以“以Java为中心”的方式实例化它。 根据须要导入XML。
@Configuration
类的使用最好从XML引导Spring容器并@Configuration
以ad-hoc方式包含 类。例如,在使用Spring XML的大型现有代码库中,能够@Configuration
根据须要更轻松地建立类,并将其包含在现有XML文件中。在本节的后面部分,咱们将介绍@Configuration
在这种“以XML为中心”的状况下使用类的选项。
将@Configuration
类声明为普通的Spring <bean/>
元素
请记住,@Configuration
类最终是容器中的bean定义。在本系列示例中,咱们建立了一个@Configuration
名为的类,AppConfig
并将其system-test-config.xml
做为<bean/>
定义包含在其中。由于 <context:annotation-config/>
已打开,容器会识别@Configuration
注释并 正确处理@Bean
声明的方法AppConfig
。
如下示例显示了Java中的普通配置类:
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
复制代码
如下示例显示了示例system-test-config.xml
文件的一部分:
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
复制代码
如下示例显示了一个可能的jdbc.properties
文件:
jdbc.url = JDBC:HSQLDB:HSQL://本地主机/ XDB jdbc.username = SA jdbc.password =
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
复制代码
在system-test-config.xml 文件中,AppConfig <bean/> 不声明id 元素。虽然这样作是能够接受的,可是没有必要,由于没有其余bean引用它,而且不太可能经过名称从容器中明确地获取它。相似地,DataSource bean只是按类型自动装配,所以id 不严格要求显式bean 。 |
---|
使用<context:component-scan />来获取@Configuration
类
由于@Configuration
带有元注释@Component
,注释@Configuration
类自动成为组件扫描的候选者。使用与前一个示例中描述的相同的方案,咱们能够从新定义system-test-config.xml
以利用组件扫描。请注意,在这种状况下,咱们不须要显式声明<context:annotation-config/>
,由于<context:component-scan/>
启用相同的功能。
如下示例显示了已修改的system-test-config.xml
文件:
<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
复制代码
@Configuration
以类为中心的XML使用 @ImportResource
在@Configuration
类是配置容器的主要机制的应用程序中,仍然可能须要使用至少一些XML。在这些场景中,您能够@ImportResource
根据须要使用和定义尽量多的XML。这样作能够实现“以Java为中心”的方法来配置容器并将XML保持在最低限度。如下示例(包括配置类,定义bean的XML文件,属性文件和main
类)显示了如何使用@ImportResource
注释来实现根据须要使用XML的“以Java为中心”的配置:
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
复制代码
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
复制代码
jdbc.properties jdbc.url = JDBC:HSQLDB:HSQL://本地主机/ XDB jdbc.username = SA jdbc.password =
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
复制代码