1.IOC容器创建对象的原理
我们知道Spring创建对象是在xml文件中使用bean标签创建的,当我们获取上下文对象时就会被创建(下面会讲到),那么为什么Spring使用bean标签就能创建一个对象呢?
通常我们在new一个对象时会调用这个类的构造器,即使我们在编写一个类时不写构造方法,系统也会生成一个默认的无参构造方法,这个无参构造方法实际上继承的是万类之父Object的无参构造方法,所以即使我们不写构造函数也能直接new创建。那么当我们写了自己的无参构造方法后,new一个对象就会调用我们自己写的无参构造方法,这就是重写构造函数。
既然new是调用的构造器来创建,那么bean标签是不是也是通过调用的构造器来创建对象呢?我们来做一个实验,使用bean标签创建一个User对象:
先定义一个User类:
public class User {
private String name;
private int age;
//重写无参构造,如果调用了无参构造就会输出“调用了构造函数”
public User() {
System.out.println("调用了构造函数");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
然后通过bean标签创建一个user对象:
<!--创建一个User类对象,并给name和age属性赋值-->
<bean id="user" class="com.anzekun.pojo.User">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
最后通过使用getBean方法让Spring开始创建来测试是否调用了重写的构造方法:
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user= (User) context.getBean("user");
}
运行结果:
由此可见,Spring容器使用bean标签来创建对象,实际上是调用了构造器创建的,这就是IOC容器创建对象的原理。
2.使用bean标签实现有参构造
上面我们使用的是无参构造方法,并利用set方法给user的属性赋值(property标签给属性赋值就是调用的set方法,即使用set方法进行依赖注入),那么怎样进行有参构造呢?Spring也为我们提供了有参构造的bean标签创建对象的方法,并且有三种:
先写一个有参构造器:
//有参构造
public User(String name,int age){
this.name=name;
this.age=age;
}
测试:
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user= (User) context.getBean("user");
System.out.println("name:"+user.getName());
System.out.println("age:"+user.getAge());
}
- 第一种,使用下标赋值
<!--利用下标赋值-->
<bean id="user" class="com.anzekun.pojo.User">
<!--0代表第一个参数,1代表第二个参数-->
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="15"/>
</bean>
运行结果:
- 第二种,使用参数类型赋值
<!--使用参数类型赋值-->
<bean id="user" class="com.anzekun.pojo.User">
<constructor-arg type="java.lang.String" value="lisi"/>
<constructor-arg type="int" value="20"/>
</bean>
运行结果:
不过要注意的是,由于这种方法是利用参数类型区分参数的,所以如果有多个相同类型的参数不建议使用这种方法
- 第三种,使用参数名赋值
<!--使用参数名赋值-->
<bean id="user" class="com.anzekun.pojo.User">
<constructor-arg name="name" value="wangwu"/>
<constructor-arg name="age" value="19"/>
</bean>
运行结果:
3.IOC容器创建对象是一次创建所有对象还是用到哪个才创建哪个?
我们先获取两个对象,观察是否调用了构造函数:
<!--创建两个对象-->
<bean id="user" class="com.anzekun.pojo.User">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
<bean id="user1" class="com.anzekun.pojo.User">
<property name="name" value="lisi"/>
<property name="age" value="15"/>
</bean>
使用之前的无参构造函数进行测试,如果调用了构造函数就输出“调用了构造函数”:
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
System.out.println("获取第一个对象");
User user= (User) context.getBean("user");
System.out.println("获取第二个对象");
User user1=(User) context.getBean("user1");
}
运行结果:
从运行结果可以看出,我们在获取对象之前就调用了两次构造函数,这就说明当我们在获得Spring的上下文对象时Spring就已经创建好了所有的对象,后面我们获取的时候直接拿就行了,这也就是为什么叫做容器的原因了
4.如果两次获取同一个id的对象,这两个对象是否是同一个对象?
上面我们进行的测试结果是先创建所有的对象,后面获取时不会再创建,对于这个问题我们大致也能猜到这两个是同一个对象了,来看测试:
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
//两次获取同一个id对象
User user= (User) context.getBean("user");
User user1= (User) context.getBean("user");
//输出比较结果
System.out.println(user1==user);
运行结果:
所以同一个id的对象Spring只会创建一个,每次获取的都是同一个对象