上文咱们介绍了IOC和DI,IOC是一种设计模式,DI是它的具体实现,有不少的框架都有这样的实现,本文主要以spring框架的实现,来看具体的注入实现逻辑。java
spring将对象加入容器的方式有不少种,最主要的是xml和注解的形式,而当下注解的形式应用更加的普遍,因此这里咱们也主要介绍注解注入模式下的相关知识点。spring
spring下的注解也是有不少种的,其中应用最为普遍的就是模式注解。设计模式
咋一看,这是啥,说实话我也不太清楚,官方说的就叫这哈,其实他说的就是平时最经常使用的那些 @Component @Service @Controller @Repository @Configuration
注解。浏览器
若是一个类被打上 @Component
注解,那他就会被容器扫描到并加入到容器里去。那这里咱们用spring的方式再来实现一下上篇文章的获取英雄的需求。app
先给 Diana
这个英雄打上一个 @Component
注解:框架
@Component
public class Diana{
private String skillName = "Diana R";
public Diana() {
}
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println(this.skillName);
}
}复制代码
假设如今有一个Controller须要用到 Diana
网站
@RestController
public class BannerController {
@Autowired
private Diana diana;
@RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
public String test() {
diana.r();
return "Hello SpringBoot";
}
}复制代码
@Autowired
的意思就是说在这里注入一个对象,而后咱们启动应用,ui
经过浏览器访问 http://localhost:8081/v2/banner
,会发如今控制台会输出 Diana
的r方法的打印内容,说明这里确实拿到了 Diana
这个对象。this
若是咱们把 Diana
的 @Component
注解去掉,其余地方不变,再运行程序会发生什么,试试看。spa
从新启动的时候会提示咱们下面的错误:
看错误提示,说程序没有找到对应的bean,就是说我这里要注入bean了,可是在容器里没有找到。
这里就会得出一个结论:容器在初始化后就会给相应的代码片断进行对象的注入。
从提示信息中能够看到Autowired(required=true),这里设置的默认是true,表示注入的时候,该对象必须存在,不然就会注入失败。固然这里可设置不报错,Autowired(required=false),这个时候再启动就不会报错了,可是在访问的时候会报空指针异常。
那这里spring的容器到底是在何时实例化的对象呢,是在访问controller的时候呢,仍是在容器启动的时候呢?咱们能够经过下面的手段检测一下,在 Diana
的无参构造方法中放一个打印语句,而后再启动程序:
public Diana() {
System.out.println("I am Diana");
}复制代码
程序启动之后,会看到在控制台会显示出构造方法中打印的语句
这就说明在容器启动后把类加载到容器之后,就会实例化一个对象出来。这就又得出一个结论:容器初始化之后就会对bean进行实例化
上面提到容器在肯定后就会把对象(也就是bean)实例化,那有没有办法让他不立刻实例化?
spring提供了@Lazy这个注解,用以代表某个bean能够延迟加载,用以前的 Diana
延时一下,给他加上@Lazy这个注解:
@Component
@Lazy
public class Diana{
private String skillName = "Diana R";
public Diana() {
System.out.println("I am Diana");
}
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println(this.skillName);
}
}复制代码
再次启动程序,会发如今控制台仍是会执行构造方法里的打印语句,这是什么状况,不是明明已经加了延迟加载了么?
这里会设计到spring的一个机制,就是说若是某个bean没有设置延迟加载,这个bean会在容器启动就实例化,而且这个bean里面所依赖的其余bean都会进行过实例化,即便设置了懒加载。
咱们在BannerController里用到了 Diana
这个bean,并把它设置了延迟加载,可是并无把BannerController也设置成延迟加载,因此容器再实例化BannerController的时候一样会 Diana
这个bean进行实例化。若是要真正作到延迟加载,须要让BannerController也要延迟加载。给它加上@Lazy之后,再启动看一下:
这个时候控制就没有那条打印语句的输出内容了,此时咱们经过浏览器来访问BannerController的路由,此时就会在控制台输出构造方法里打印语句的输出内容了。
上文 Diana
的就是一个单独的类,没有实现任何接口,这种实现方式他是有问题的,很难进行扩展,这里不该该一类具体的实现类,而是要应该依赖抽象,这样才能知足开闭原则,让程序具备良好的扩展性。
这里让 Diana
实现了一 Skill
接口,
public interface Skill {
void q();
void w();
void e();
void r();
}复制代码
在BannerController注入这里也要改一下:
@Autowired
private Skill diana;复制代码
运行程序,发先可以运行成功,若是新添加一个英雄的实现类:
@Component
public class Irelia implements Skill {
public Irelia() {
System.out.println("Hello, Irelia");
}
public void q(){
System.out.println("Irelia Q");
}
public void w(){
System.out.println("Irelia W");
}
public void e(){
System.out.println("Irelia E");
}
public void r(){
System.out.println("Irelia R");
}
}复制代码
咱们再去运行程序,发现仍是能运行成功,而且发现注入的是 Diana
而不是 Irelia
,那这里咱们可能会问为何呀,前面好像也没指定注入哪一个实现类。
难道是由于 private Skill diana;
这里写了 diana
么?你还别说,还真是这个缘由,这里涉及到spring的注入机制了。
当spring的IOC容器注入bean的时候,若是发现有多个相同类型的bean时,就会去看它们的名称(name),若是名称符合要求,就会注入这个名称的bean。每一个被容器扫描到的bean都会有一个默认的name,就是它的类名,首字母会小写,好比咱们实例中的:BannerController的name就是bannerController,Irelia就是irelia。
能够试着把 private Skill diana
改为 private Skill skill
,再次运行,就会发生报错:
提示你找不到响应的bean。这里要明确一点哈,虽然报错了,可是这种写法是标准的,上面那个直接写实现类的名字不是标准的写法。
那怎么解决这个找不到的问题呢?
spring提供了一个@Qualifier的注解,给他传入要注入bean的名字,就能够指定注入的bean,@Qualifier("irelia")。
Autowired的注入方式有两种,一种是按照类型注入,一种是按照名称注入,默认是按照类型注入,若是只有一个实现类 Diana
,这里写成 private Skill skill
,运行程序,你会发现是可以注入成功的。由于Skill类型下只有一个实现类,天然就是注入它了。若是有多个实现类,那就会先按照这个类型查找,这个类型下全部到的实现类再按照名称查找,直至找到符合要求的,找不到就报错。
更多关于spring IOC内容,请查看(博客网站)[www.immortalp.com]