目录
Q1:对象的创建是在初始化容器的时候还是在getBean方法的时候?
Q2:如果通过相同的key调用两次getBean方法,那么拿到的对象是同一个吗?
Spring注入的方式有两种,一种是通过xml文件来实现,另一种则是通过注解的方式来实现。注解方式注入比xml文件方式注入更为简单。
如上图所示,无论是哪种注入方式,都是先将类的配置信息存入map中,然后再将实例化的对象存入BeanPool中。
1.注解注入示例
实现创建一个类:SpringConfig
package org.example;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("org.beans")
public class SpringConfig {
}
其中SpringConfig类就相当于application-context.xml文件,@ComponentScan注解指定了要扫描的包路径,即需要把哪个包下面的类(具有@ComponentScan注解的类)添加到BeanFactory中。
接下来在org.beans包下添加一个类:DefauleCache
package org.beans;
import org.springframework.stereotype.Component;
@Component
public class DefaultCache {
}
@Component注解表示此类需要加入到BeanFactory中。同时Component可以指定key,如@Component("cache"),如果不指定,key默认为类名的首字母小写所得到的字符串。
编写测试类如下:
package org.example;
import org.beans.DefaultCache;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBase {
protected AnnotationConfigApplicationContext ctx;
@Before
public void init(){
//xml形式的配置
/* ctx = new ClassPathXmlApplicationContext("spring-context.xml");*/
//注解形式的配置
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
}
@Test
public void testCtx(){
System.out.println(ctx);
DefaultCache cache = ctx.getBean("defaultCache", DefaultCache.class);
System.out.println(cache);
}
}
可以看到ctx和bean都已经拿到了。
这样我们就省掉了编写xml文件的方法,直接用注解的方式就可以直接实现注入。
Q1:对象的创建是在初始化容器的时候还是在getBean方法的时候?
答:默认情况下,对象的创建是在容器初始化的时候,但是如果有业务需要,让类在需要使用时再进行创建,可以使用@Lazy延迟加载注解,如:
package org.beans;
import org.springframework.stereotype.Component;
@Lazy
@Component
public class DefaultCache {
}
Q2:如果通过相同的key调用两次getBean方法,那么拿到的对象是同一个吗?
默认情况下,容器中对象的作用域是"singleton",即单例,那么两次获取同一个对象得到的实例时相同的。
如下测试:
首先我们再DefaultCache类中添加构造方法去打印一些信息,如下:
package org.beans;
import org.springframework.stereotype.Component;
@Component
public class DefaultCache {
public DefaultCache(){
System.out.println("DefaultCache");
}
}
测试文件如下:
package org.example;
import org.beans.DefaultCache;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBase {
protected AnnotationConfigApplicationContext ctx;
@Before
public void init(){
/* ctx = new ClassPathXmlApplicationContext("spring-context.xml");*/
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
}
@Test
public void testCtx(){
/*System.out.println(ctx);*/
DefaultCache cache1 = ctx.getBean("defaultCache", DefaultCache.class);
DefaultCache cache2 = ctx.getBean("defaultCache", DefaultCache.class);
System.out.println(cache1==cache2);
}
}
可以看到,只调用了一次构造方法,且得到的两个实例是相等的。
如果我们需要每次拿到不同的对象,可以使用@Scope注解去改变对象的作用域。
如修改DefaultCache类:
package org.beans;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//表示getBean时都创建出一个新的实例
@Scope("prototype")
@Component
public class DefaultCache {
public DefaultCache(){
System.out.println("DefaultCache");
}
}
继续执行测试方法:
可以发现DefaultCache的构造方法被执行了两次,并且两个实例是不相等的。
另外需要注意Scope为prototype的对象是不会放入Bean容器中的,只有Scope为singletor的对象才会放入池中。
Q3:容器中对象的生命周期
Scope为singletor的对象的生命周期为容器初始化到容器关闭,而Scope为prototype的对象Spring容器只负责创建它,但是其生命周期并不由Spring进行管理。