1.11,使用JSR 330標準注解
從Spring 3.0開始,Spring提供了對JSR-330標準注解(依賴注入)的支持,
這些注解的掃描方式與Spring注解相同,
要使用它們,您需要在類路徑中包含相關的jar,
如果你使用Maven, javax,
注入工件在標準Maven存盤庫中可用
(https://repo1.maven.org/maven2/javax/inject/javax.inject/1/),
你可以添加以下依賴到你的檔案pom.xml:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
1.11.1,@Inject和@Named
除了@Autowired,您可以使用@javax.inject.Inject注入:
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
// ...
}
}
與@Autowired一樣,你可以在欄位級、方法級和構造引數級使用@Inject,
此外,您可以將注入點宣告為提供者,從而允許按需訪問作用域較短的bean,或者通過Provider.get()延遲呼叫訪問其他bean,
以下示例提供了先前示例的變體:
import javax.inject.Inject;
import javax.inject.Provider;
public class SimpleMovieLister {
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
// ...
}
}
如果要為應該注入的依賴項使用qualified名稱,則應使用@Named批注,如以下示例所示:
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
與一樣@Autowired,@Inject也可以與java.util.Optional或 一起使用@Nullable,
這在這里更為適用,因為@Inject它沒有required屬性,
以下示例展示了如何使用@Inject和 @Nullable:
public class SimpleMovieLister {
@Inject
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
// ...
}
}
public class SimpleMovieLister {
@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
// ...
}
}
1.11.2,@Named和@ManagedBean:
您可以使用@javax.inject.Named或@javax.annotation.ManagedBean代替@Component
如以下示例所示:
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
在不指定Component名稱的情況下使用@Component是很常見的,
@Named也可以以類似的方式使用,如下面的示例所示:
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
當使用@Named或時@ManagedBean,可以使用與使用Spring注解完全相同的方式來使用組件掃描,如以下示例所示:
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
與相比@Component,JSR-330的@Named和JSR-250的ManagedBean 注解是不可組合的,
1.11.3,JSR-330標準注解的局限性
當你使用標準注解時,你應該知道一些重要的特性是不可用的,如下表所示:
| Spring | javax.inject.* | javax.inject限制/解釋 |
|---|---|---|
| @Autowired | @Inject | @Inject沒有“required”屬性, 可以與Java 8一起使用Optional |
| @Component | @Named/@ManagedBean | JSR-330沒有提供可組合模型, 只是提供了一種識別已命名組件的方法, 就是說這倆注解僅僅是把類標識為bean |
| @Scope("singleton") | @Singleton | JSR-330的默認作用域類似于Spring的prototype, 但是,為了保持它與Spring的一般默認值一致, 在Spring容器中宣告的JSR-330 bean在默認情況下是單例的, 為了使用除singleton之外的作用域, 您應該使用Spring的@Scope注解, javax.inject包中還提供了一個@Scope注解, 不過,這個注解僅用于創建您自己的注解, 具體例子就看同層級包下的@Singleton注解就可以了 |
| @Qualifier | @Qualifier / @Named | javax.inject.Qualifier只是用于構建自定義限定符的元注解, 具體String qualifiers (例如Spring的帶有value的@Qualifier) 可以通過javax.inject.Named關聯, |
| @Value | -- | no equivalent |
| @Required | -- | no equivalent |
| @Lazy | -- | no equivalent |
| ObjectFactory | Provider | javax.inject.Provider是Spring的 直接替代方法ObjectFactory, 只是get()方法名稱較短, 它也可以與Spring@Autowired或 非注解建構式和setter方法結合使用, |
1.12,基于Java的容器配置
本節介紹如何在Java代碼中使用注解來配置Spring容器,它包括以下主題:
- 基本概念:@Bean和@Configuration
- 使用實體化Spring容器 AnnotationConfigApplicationContext
- 使用@Bean注解
- 使用@Configuration注解
- 組成基于Java的配置
- Bean定義組態檔
- PropertySource 抽象化
- 使用 @PropertySource
- 宣告中的占位符決議
1.12.1,基本概念:@Bean和@Configuration
Spring的新Java配置支持中的主要組件是-帶@Configuration注解的類和-帶@Bean注解的方法,
@Bean注解用于指示方法實體化、配置和初始化的新物件并將由Spring IoC容器管理,
對于那些熟悉Spring的
您可以對任何Spring @Component使用@bean注解的方法,
但是,它們最常與@Configuration bean一起使用,
用@Configuration注解類表明它的主要用途是作為bean定義的源,
此外,@Configuration類通過呼叫同一類中的其他@Bean方法來定義bean間的依賴關系,
最簡單的@Configuration類如下:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上一AppConfig類等效于以下Spring
<beans>
<bean id="myService" />
</beans>
Full @Configuration 與 “lite” @Bean?
當@Bean方法在沒有使用@Configuration注解的類中宣告時,它們被稱為在“lite”模式下處理,
與 full 的@Configuration不同,lite@Bean方法不能宣告bean之間的依賴關系,
Spring會正常實體化 lite @Bean,并執行依賴注入
但是單獨呼叫這個@Bean方法的話,就僅僅是執行new 操作沒有進行依賴注入
在常見的場景中,@Bean方法將在@Configuration類中宣告,以確保始終使用“full”模式,這樣可以使用方法之間互相呼叫,
并防止通過常規Java呼叫意外地呼叫相同的@Bean方法,這有助于減少在“lite”模式下操作時難以跟蹤的細微錯誤,
下面幾節將深入討論@Bean和@Configuration注解,
但是,首先,我們將介紹使用基于java的配置創建spring容器的各種方法,
1.12.2,使用AnnotationConfigApplicationContext實體化Spring容器
下面幾節介紹了Spring3.0中引入的AnnotationConfigApplicationContext,
這個多功能的ApplicationContext實作不僅可以接受@Configuration類作為輸入,還可以接受普通的@Component類和用JSR-330元資料注解的類,
當@Configuration類作為輸入提供時,@Configuration類本身被注冊為bean定義,類中所有宣告的@bean方法也被注冊為bean定義,
當@Component和JSR-330類被提供時,它們被注冊為bean定義,并且會被用在在必要的地方進行注入,比如@Autowired或@Inject,
Simple Construction
與使用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 {
...
}
ComponentScan注解啟用組件掃描,
有經驗的Spring用戶可能熟悉Springcontext:命名空間中的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定義,
支持Web應用程式 AnnotationConfigWebApplicationContext
一個基于AnnotationConfigApplicationContext的WebApplicationContext變種是AnnotationConfigWebApplicationContext,
可以使用此實作來配置Spring ContextLoaderListenerservlet監聽器,Spring MVC的 DispatcherServlet,
以下web.xml代碼片段配置了典型的Spring MVC Web應用程式(請注意使用contextClasscontext-param和init-param),這里的啟動流程前面的文章有講過的這里不再贅述:
<web-app>
<!-- 配置 ContextLoaderListener來使用AnnotationConfigWebApplicationContext
替換默認的 default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- 指定 配置類-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- 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>
<!-- 配置 ContextLoaderListener來使用AnnotationConfigWebApplicationContext替換默認的 default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- 指定mvc config-->
<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>
1.12.3,使用@Bean注解
@Bean是方法級別的注解,是XML
注解提供的某些屬性,例如:* init-method * destroy-method * autowiring * name *,
你可以一個注解@Configuration-annotated或在 @Component-annotated類使用@Bean,
宣告一個bean
要宣告一個bean,可以用注解對方法進行@Bean注解,
方法回傳值的型別就是在ApplicationContext中注冊的bean定義的型別,
默認情況下,Bean名稱與方法名稱相同,
以下示例顯示了@Bean方法宣告:
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
前面的配置與下面的Spring XML完全等效:
<beans>
<bean id="transferService" />
</beans>
這兩個宣告都使名為transferService的bean在ApplicationContext中可用,該bean系結到型別為TransferServiceImpl的物件實體,
如下圖所示:
transferService -> com.acme.TransferServiceImpl
您還可以使用@Bean將介面(或基類)作為回傳型別宣告您的方法,
如以下示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
如果您一直通過宣告的服務介面參考您的型別,
@Bean回傳型別可以安全地加入到設計決策中,
但是,對于實作多個介面的組件或其實作型別可能參考的組件,
宣告最具體的回傳型別更安全(至少與參考bean的注入點所需的回傳型別相同),
Bean依賴
帶@Bean注解的方法可以具有任意數量的引數,這些引數描述了構建該bean所需的依賴關系,
例如,如果我們TransferService需要AccountRepository,我們可以使用方法引數來實作該依賴關系,如以下示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
決議機制與基于建構式的依賴注入幾乎相同,
接收生命周期回呼
使用@Bean注解定義的任何類都支持常規的生命周期回呼,并且可以使用JSR-250中的@PostConstruct和@PreDestroy注解,
還完全支持常規的Spring生命周期回呼,
如果bean實作InitializingBean,DisposableBean或Lifecycle,則容器將呼叫它們各自的方法,
還完全支持標準*Aware介面集(例如BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等),
該@Bean注解支持指定任意初始化和銷毀回呼方法,就像SpringXML中的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配置定義的具有公共close或shutdown方法的bean將自動與銷毀回呼一起呼叫,
比如這樣:在容器銷毀的時候就會被呼叫,不需要繼承任何介面
@Bean
public class ServiceFour {
public void close(){
System.out.println("20201030-close");
}
}
如果您有一個公共的close或shutdown方法,并且不希望在容器關閉時呼叫它,
那么可以將@Bean(destroyMethod=“”)添加到Bean定義中,以禁用默認(推斷)模式,
@Bean(destroyMethod="")
public class ServiceFour {
public void close(){
System.out.println("20201030-close");
}
}
默認情況下,您可能希望對使用JNDI獲取的資源執行此操作,因為它的生命周期是在應用程式外部管理的,
特別是,確保總是對資料源執行此操作,
以下示例顯示如何阻止資料源的自動銷毀回呼:
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
此外,對于@Bean方法,通常使用程式化JNDI查找,
方法是使用SpringJndiTemplate或JndiLocatorDelegate輔助方法,或者直接InitialContext使用JNDI,
但不使用JndiObjectFactoryBean變體
(這將迫使您將回傳型別宣告為FactoryBean型別,而不是實際的目標型別,不是目標型別的話就很難使其他的@Bean方法來交叉呼叫此方法),
對于上述示例中的BeanOne,在構造程序中直接呼叫init()方法同樣有效,如下例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
當您直接使用Java作業時,您可以對物件執行任何操作,而不必總是依賴于容器生命周期,
指定Bean范圍
Spring包含@Scope注解,以便您可以指定bean的范圍,
使用@Scope注解
@Bean默認作用域是singleton,但是您可以使用@Scope注解覆寫它,如以下示例所示:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
@Scope 和 scoped-proxy
Spring提供了一種通過作用域代理處理作用域依賴關系的方便方法,
在使用XML配置時創建這樣一個代理的最簡單方法是aop:scoped-proxy/元素,
<bean id="serviceOne" >
<aop:scoped-proxy />
</bean>
用@Scope注解在Java中配置bean提供了與proxyMode屬性相同的支持,
默認設定為無代理(ScopedProxyMode.NO),但您可以指定ScopedProxyMode.TARGET_CLASS or ScopedProxyMode.INTERFACES,
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- http session 的作用域 將作為代理出現 -->
<bean id="userPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<!-- 單例注入 的是一個代理不是 一個實體 避免只參考http session的一個實體 -->
<bean id="userService" >
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
如果您使用Java Config將上述XML參考檔案中的作用域代理示例移植到我們的@Bean,它類似于以下內容:
// 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方法的名稱作為結果bean的名稱,
但是,可以使用name屬性覆寫此功能,如以下示例所示:
@Configuration
public class AppConfig {
@Bean(name = "myThing")
public Thing thing() {
return new Thing();
}
}
Bean別名
正如在命名bean中所討論的,有時需要為單個bean指定多個名稱,或者稱為bean別名,
為此,@Bean注解的name屬性接受一個字串陣列,
下面的示例演示如何為bean設定多個別名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
bean描述
有時,提供bean的更詳細的文本描述是很有幫助的,當bean(可能通過JMX)公開以進行監視時,這一點特別有用,
要向@Bean添加說明,可以使用@description注解,如下示例所示:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
1.12.4,使用@Configuration注解
@Configuration是一個類級別的注解,來指定當前類是一些bean definition的聚合,
@Configuration 類通過public@Bean注解方法宣告Bean,
對@Configuration類上的@Bean方法的呼叫也可以用來定義Bean間的依賴關系,
注入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在@Configuration類中宣告該方法時,此宣告bean間依賴關系的方法才有效,
您不能使用普通@Component類宣告bean間的依賴關系,
Lookup Method注入
如前所述,Lookup Method注入是一種高級特性,應該很少使用,
在單例范圍bean依賴于原型范圍bean的情況下,它非常有用,
將Java用于此類配置提供了實作此模式的自然方法,
以下示例演示如何使用查找方法注入:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
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();
return command;
}
@Bean
public CommandManager commandManager() {
// return CommandManager 的匿名實作類 并且實作了 createCommand()方法
// return a new 原型類的 Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
有關基于Java的配置如何在內部作業的更多資訊
考慮以下示例,該示例顯示了一個帶@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在啟動時動態添加特性,因此有一些限制,尤其是,配置類不能是final,
但是,從4.3開始,配置類上允許使用任何建構式,包括使用@Autowired或一個用于默認注入的非默認建構式宣告,
如果您希望避免任何CGLIB強加的限制,請考慮在非@configuration 類上宣告@Bean方法(例如,在普通@Component類上),
然后@Bean方法之間的跨方法呼叫不會被攔截,但是因此您必須在建構式或方法級別專門進行依賴的注入,直接呼叫非@configuration的@Bean方法Spring將不再負責依賴注入,
1.12.5,組成基于Java的配置
Spring基于Java的配置特性允許您撰寫注解,這可以降低配置的復雜性,
使用@Import注解
正如Spring XML檔案中使用
@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還支持對常規component 類的參考,類似于該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 類在背景關系初始化程序中很早就被處理,強制以這種方式注入依賴項可能會導致意外的早期初始化,
只要有可能,就盡可能使用基于引數的注入,如前面的示例所示,
另外,通過@Bean使用BeanPostProcessor和BeanFactoryPostProcessor定義要特別小心,這些方法通常應該宣告為static@Bean方法,而不是觸發其包含的配置類的實體化,
如果自定義的BeanPostProcessor創建為早于AutowiredAnnotationBeanPostProcessor的bean實體,那么,@Autowired和@Value不適用于配置類本身,
以下示例說明如何將一個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;
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");
}
從springframework4.3開始,@Configuration類中的建構式注入才受支持,
還要注意,如果目標bean只定義了一個建構式,則不需要指定@Autowired,
Fully-qualifying imported beans for ease of navigation
在前面的場景中,使用@Autowired可以很好地作業并提供所需的模塊性,但是準確地確定注入的bean定義的宣告位置仍然有些模糊,
例如,作為一個正在查看ServiceConfig的開發人員,您如何確切地知道@autowiredaccountrepository bean是在哪里宣告的?
如果這種模糊性是不可接受的,并且您希望在IDE中從一個@Configuration類直接導航到另一個@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的啟動創建順序,可以考慮將其中一些bean宣告為@Lazy(用于在首次訪問時而不是在啟動時創建)或@DependsOn某些其他bean(確保在當前bean之前創建特定的其他bean,而不是后者的直接依賴關系),
結合Java和XML配置
Spring的@Configuration類支持并不是要完全替代Spring XML,
有些工具(如Spring XML名稱空間)仍然是配置容器的理想方式,
在XML方便或必要的情況下,你有一個選擇:要么通過使用ClassPathXmlApplicationContext在容器實體化在一個“以XML為中心”的方式使用,或通過使用AnnotationConfigApplicationContext搭配@ImportResource注解匯入XML,
以XML為中心的@Configuration類使用
最好是從XML引導Spring容器,并以特別的方式包含@Configuration類,
例如,在使用Spring XML的大型現有代碼庫中,更容易根據需要創建@Configuration類并從現有XML檔案中包含它們,
在本節的后面,我們將介紹在這種“以xml為中心”的情況下使用@Configuration類的選項,
請記住,@Configuration類最終是容器中的bean定義,
在本系列示例中,我們創建一個@Configuration名為的類,AppConfig并將其包含在其中system-test-config.xml作為
因為 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>
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean />
<bean >
<property name="url" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.url}"/>
<property name="username" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.username}"/>
<property name="password" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.password}"/>
</bean>
</beans>
以下示例顯示了一個可能的jdbc.properties檔案:
jdbc.url = jdbc:hsqldb:hsql:// localhost / 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);
// ...
}
因為@Configuration元注解是@Component,@Configuration注解的類會自動成為組件掃描的候選類,
使用與前一個例子中描述的相同場景,我們可以重新定義system-test-config.xml利用組件掃描,
注意,在這種情況下,我們不需要顯式宣告context:annotation-config/,因為context:component-scan/啟用相同的功能,
以下示例顯示了修改后的system-test-config.xml檔案:
<beans>
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean >
<property name="url" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.url}"/>
<property name="username" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.username}"/>
<property name="password" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.password}"/>
</bean>
</beans>
以@Configuration 類為中心搭配 @ImportResource 的使用XML
在@Configuration類是配置容器的主要機制的應用程式中,仍然可能需要至少使用一些XML,
在這些場景中,您可以使用@ImportResource只定義所需的XML,
這樣做可以實作“以Java為中心”的方法來配置容器,并將XML保持在最低限度,
下面的示例(包括一個配置類、一個定義bean的XML檔案、一個屬性檔案和一個主類)演示了如何使用@ImportResource注解來實作“以Java為中心”的配置,并根據需要使用XML:
@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:// localhost / xdb
jdbc.username = sa
jdbc.password = adsdfas
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/195920.html
標籤:Java
上一篇:mybatis 框架學習筆記
