Spring的属性填充,到底是根据哪个构造方法填充的呢?
可以参考这个图。
(普通maven工程引入spring-webmvc依赖)比如有两个类:
@Component
public class OrderService {
}
------------------------------------------------------------------------------------------------
@Component
public class UserService {
//属性
private OrderService orderService;
public UserService(){
System.out.println(1);
}
public UserService(OrderService orderService){
System.out.println(2);
this.orderService = orderService;
}
}
使用注解开发:
@ComponentScan(value = "com.xuangong.pojo")
public class PojoConfig {
}
测试:
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(PojoConfig.class);
UserService userService = ac.getBean(UserService.class);
}
}
当什么都不写的时候,默认用的是无参构造方法。
而当我们去掉无参构造,再加一个有参构造的时候,也就是有两个有参构造。那么会报错。
@Component
public class UserService {
//属性
public OrderService orderService;
public UserService(OrderService orderService){
System.out.println(2);
this.orderService = orderService;
}
public UserService(OrderService orderService,OrderService orderService1){
System.out.println(2);
this.orderService = orderService;
}
@Override
public String toString() {
return "UserService{" +
"orderService=" + orderService +
'}';
}
}
1、@Autowired
因为系统不知道这两个方法到底应该用哪个,它们并没有优先级。
这个时候会看哪个加了@Autowired,如果没有会先根据byType然后根据byName查找。
什么叫先byType后byName吗,不是单例的吗?
单例不代表这个类只能有一个。而是这个对象名字只能有一个对应的对象。
比如:
@ComponentScan(value = "com.xuangong.pojo")
public class PojoConfig {
@Bean
public OrderService orderService1(){
System.out.println("s1");
return new OrderService();
}
@Bean
public OrderService orderService2(){
System.out.println("s2");
return new OrderService();
}
}
根据这个类我们发现,我们一共在容器当中放了三个OrderService对象,一个是PojoConfig中生成的两个,另外一个就是OrderService自己作为一个组件。
那么我们尝试拿到这几个对象看看是不是一个:
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(PojoConfig.class);
OrderService orderService1 = (OrderService) ac.getBean("orderService");
OrderService orderService2 = (OrderService) ac.getBean("orderService1");
OrderService orderService3 = (OrderService) ac.getBean("orderService2");
System.out.println(orderService1);
System.out.println(orderService2);
System.out.println(orderService3);
}
}
测试发现,我们拿到的三个对象并不是一个。所以单例模式并不是单例bean,我们把对象放入一个map集合,这个对象的beanName作为key,这个对象本身作为value,所以beanName只能对应一个对象。
那么现在如果我的UserSerivce中的这个方法:
@Autowired
public UserService(OrderService orderService1){
System.out.println(2);
this.orderService = orderService;
}
这个对象加上了Autowired注解,其中参数也确定了是OrderService,这是先byType确定的,但是我们发现,我们找到了三个,接下来就要根据byName了,找到名为orderService1对象。
如果我们把上面这个方法的orderService1改成orderService4就会报错,因为单例池中没有这个名字的对象。
2、byType
上面先根据byType来检查,其实byType也是有很多步骤的。
@AutowiredCandidate
还以上面为例,根据type找到了多个,接下来根据名字查询,查询的是orderService1,如果在orderService1上加上代码。
@Bean(autowireCandidate = false)
public OrderService orderService1(){
System.out.println("s1");
return new OrderService();
}
这样就会报错,因为orderService1不作为自动注入的候选对象,那么就没有这个名字了。
也可以在某个属性上加上@Qualifier,这样原本名字不符合的这回就可以匹配上了。
上面有可能从容器中找到多个。
下面这两个是设置优先级,@Primary是先设置主bean,@Priority是设置优先级,这样就只拿到一个了。
上面都没有才会根据名字找出适合的。
3、@Resource
@Resource是先byName再byType。
@Resource(name = "XXX")那么就直接根据设置的名字找,如果没有就根据属性名找,才会根据类型查找,再找不到或者找到多个才会报错。