Spring属性填充

224 阅读3分钟

Spring的属性填充,到底是根据哪个构造方法填充的呢?

可以参考这个图。

image-20210607133245983

(普通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也是有很多步骤的。

image-20210607144636661

@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")那么就直接根据设置的名字找,如果没有就根据属性名找,才会根据类型查找,再找不到或者找到多个才会报错。