标记了@Autowired 注解的字段/方法,会由 Spring 容器自动的赋值一个实例化的对象。@Autowired 总是采用 byType 的方式实现自动装配,只要找到需要装配的类型的实例就行了。有时候 Spring 容器中,同一个类型的实例有多个,那么可能会出现异常,这个时候就需要精确的自动装配,需要用到@Qualifier 注解。
示例
有 2 个类,User 和 Company。
Company
package com.learn.entity;
import org.springframework.stereotype.Component;
@Component
public class Company {
public Company() {
System.out.println("init Company...");
}
private Integer companyCode = this.hashCode();
public Integer getCompanyCode() {
return companyCode;
}
public void setCompanyCode(Integer companyCode) {
this.companyCode = companyCode;
}
@Override
public String toString() {
return "CompanyCode:" + this.companyCode;
}
}
User
package com.learn.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class User {
public User()
{
System.out.println("init user...");
}
@Autowired
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
上面的代码中,User 类中有一个 company 字段,标注了 Autowired,这就说明 Spring 容器中的 User 对象,company 的自动的值会被自动赋值,不会是 null。
配置类,定义自动扫描的路径。
package com.learn;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.learn")
public class Config {
}
Main 入口。
Main类
package com.learn;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.learn.entity.User;
public class Main {
public static void main(String[] args) {
//使用Config.class这个配置类
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
//根据类型获取实例
User user= applicationContext.getBean(User.class);
System.out.println(user.getCompany());
applicationContext.close();
}
}
运行后输出: init Company… init user… CompanyCode:475779800 可以看到 User 类中的 company 字段并未主动初始化,但是却有值。
如果有 2 个 Bean 实例
上面的例子改一下
Config配置类
package com.learn;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.learn.entity.Company;
@Configuration
@ComponentScan("com.learn")
public class Config {
@Bean
public Company getCompany()
{
Company c=new Company();
c.setCompanyCode(99999);
return c;
}
}
这时候,Spring 容器中出现了 2 个 Company 的实例,一个是自动扫描得到的,另一个是配置类中配置的。此时,Autowired 会将属性的名称作为组件的 id 去容器中查找,即用 company 作为实例的 id 去匹配实例,那么就又会匹配到自动扫描后生成的那个实例,因为那个实例的名字就是首字母小写的类名 company。
如果必须要以配置类中的实例为优先实例,那么只要在该实例上标注上@Primary 注解,表示默认首先该实例。
@Primary注解
@Configuration
@ComponentScan("com.learn")
public class Config {
@Bean
@Primary
public Company getCompany()
{
Company c=new Company();
c.setCompanyCode(99999);
return c;
}
}
注意,采用@Primary 后,此实例会优先加载,Main 方法运行后,得到的 companyCode 是 99999: init Company… init user… init Company… CompanyCode:99999
当然,也可以更直接的,指定自动绑定的实例。上面的 User 类,修改如下代码:
@Qualifier
@Autowired
@Qualifier("getCompany")
private Company company;
那么 company 注入的时候,会去找 id 为 getCompany 的实例,也就是会找到配置类中配置的实例。
总结
@Autowired 根据类型自动注入对象的实例,如果同一个类型的实例有多个,则会根据实例的 id 名去匹配,但这种不是最好的方式,建议直接用@Qualifier 注解指定需要注入的实例,或者用@Primary 注解标识出首选 Bean,减少不必要的干扰。