如〈使用 Spring DI〉中看過的,@Autowired
可以標註在建構式參數上,在〈設定 Profile〉中看過標註在值域上的情況,實際上,它也可以標註在方法的參數上。
預設情況下,被 @Autowired
標註的參數或值域,一定要在 Spring 管理的 Bean 中能夠找到,若希望允許找不到時設定為 null
,可以指定 @Autowired(required=false)
。
@Autowired
預設採用型態來找到對應的 Bean,然而有時,同一型態可以有多個實現,這時就會試著使用參數或值域名稱,與 Bean 的名稱進行比對,因此,對於底下的 AppConfig
:
package cc.openhome;
import javax.sql.DataSource;
import org.h2.jdbcx.JdbcDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
@ComponentScan
@PropertySource("classpath:jdbc.properties")
public class AppConfig {
@Value("${cc.openhome.jdbcUrl}")
private String jdbcUrl;
@Value("${cc.openhome.user}")
private String user;
@Value("${cc.openhome.password}")
private String password;
@Bean
public DataSource getDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
@Bean(destroyMethod="shutdown")
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:testData.sql")
.build();
}
@Bean
public static PropertySourcesPlaceholderConfigurer
propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
搭配〈使用 Spring DI〉中的 AccountDAOJdbcImpl
或 MessageDAOJdbcImpl
時,由於 DataSource
具有兩個實現,由於 AccountDAOJdbcImpl
與 MessageDAOJdbcImpl
的建構式參數上,都是 @Autowired DataSource dataSource
,參數名稱為 dataSource
,因此會使用 EmbeddedDatabaseBuilder
建構出來的 DataSource
實現,這是因為 @Bean
都沒有指定名稱,因而會使用方法名稱作為預設的 Bean 名稱。
你可以使用 @Primary
搭配 @Bean
或者是 @Component
,當 Spring 發現有兩個以上相同類型的 Bean 時,會選擇被標註了 @Primary
的 Bean。例如,上例中若使用:
...略
@Primary
@Bean
public DataSource getDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
...略
那麼 AccountDAOJdbcImpl
與 MessageDAOJdbcImpl
被注入的,就會是 JdbcDataSource
的版本。
你也可以使用 @Qualifier
搭配 @Autowired
,這可以指定要注入的 Bean 之名稱。例如:
...略
@Component
public class AccountDAOJdbcImpl implements AccountDAO {
private DataSource dataSource;
public AccountDAOJdbcImpl(@Autowired @Qualifier("getDataSource") DataSource dataSource) {
System.out.println(dataSource);
this.dataSource = dataSource;
}
...略
這麼一來,AccountDAOJdbcImpl
被注入的會是 JdbcDataSource
的版本,當然,getDataSource
這名稱取得不好,在使用標註進行 JavaConfig 組態的情況下,Bean 名稱實際上是不用遵守 Getter 命名慣例的,可以依實際的需求來為產生 Bean 的方法命名。
@Autowired
不一定要標註在參數或值域上,例如,當方法或建構式上有多個參數都需要自動注入時,像是〈Spring DI〉中的 UserService
:
...略
@Component
public class UserService {
private final AccountDAO acctDAO;
private final MessageDAO messageDAO;
public UserService(@Autowired AccountDAO acctDAO, @Autowired MessageDAO messageDAO) {
this.acctDAO = acctDAO;
this.messageDAO = messageDAO;
}
...略
如果沒有個別設定上,例如 required=true
或者是 @Qualifier
等的需求,實際上只要在建構式(或方法上)標註一個就可以了:
...略
@Component
public class UserService {
private final AccountDAO acctDAO;
private final MessageDAO messageDAO;
@Autowired
public UserService(AccountDAO acctDAO, MessageDAO messageDAO) {
this.acctDAO = acctDAO;
this.messageDAO = messageDAO;
}
...略