0.引言
大家可能不知道什么是IoC,但只要你接触过Java,多多少少都会接触到Spring框架,比如用于web开发的Spring MVC、持久层开发的Spring Data、微服务开发Spring Cloud,以及经典的“轮子的轮子,框架的框架”——Spring Boot。我们把这一大坨东西就叫做Spring全家桶。
今天给大家介绍的,就是spring全家桶中相当基础的概念:IoC
本文我们简单的介绍下概念以及实现方法,后面有机会来分享高阶应用以及源码分析
1 什么是IoC?为什么要用IoC?
IoC,即控制反转——是Spring全家桶各个功能模块的基础
每个功能模块都依赖于对象而发挥作用,IoC的本质就是帮助创建对象。
↑↑↑ 这句话听起来真的迷惑,创建对象还要帮助?那我不是要多少就new多少?
能有这个疑惑的读者,可能跟我一样年轻,没咋改过那种耦合度很高的代码。尤其是现在利用spring开发的绝大部分大型工程,耦合度都很高。
举个简单的例子:在业务层,我定义了一个类,比如说是用来计算某个几何图形的属性,里面可能有计算面积啊、计算周长啊这些方法。同时肯定会有一个实例化的图形类对象,我们就叫他diagram。那你写出来可能就是下面这样:
private Diagram diagram = new Diagram( );
这个业务层的类比如定义为DiagramService0,那定义Diagram这个类的数据层就被DiagramService0调用了。一些大型的工程里,我们极有可能写了Diagram这个类,运用到很多地方,可能不仅仅是DiagramService0。
也许换个使用场景,比如我又需要算图形有几个内角,但是这个需求又只在某个场景用得上。那我是不是没必要专门把这个功能集成到DiagramService0,更好的办法显然是写一个继承类,比如说是DiagramService1。
如果有一天,Diagram这个类要升级要迭代(就比如最简单的改名,改成DiagramNew),我们就需要去维护DiagramService0、DiagramService1。这只是一个简单的例子,事实上你需要维护的东西在工程的方方面面,非常的不方便。
那怎么解决呢?
——是不是我们可以考虑,不由自己主动去new对象,交给外部来创建对象。不管我的类怎么迭代,它只要按照我写的这个类去对应的地方创建对象就可以了
这就是控制反转的核心,我们把对象创建的权利,从我们的程序转移到外部,从而解决我们在开发中修改一处代码,往往要连带着修改很多关联代码的问题。这个外部,就是我们的IoC容器(对,IoC的本质就是一个容器)
学术一点的说:IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean
2 如何实现IoC
其实上实现IoC的办法并不少,比较常用的就有:1.基于注解 2.基于xml文件 3.扫包+注解
package wy.springboot01.domain;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
/**
* springboot项目启动的时候,自动将application.yml内容加载到实体对象中
*/
@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
private Integer uid;
private String uname;
private String password;
private ArrayList<String> addrs;
}
package wy.springboot01.IoC;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import wy.springboot01.domain.User;
public class IoCTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("wy.springboot01.domain");
//context.getBean(User.class)利用IoC生成对象
System.out.println(context.getBean(User.class));
}
}
3 对IoC创建对象的参数进行赋值
当然,如果你将这个IoC创建的User对象保存下来,就可以用set对这些参数进行赋值,但是我们往往喜欢给他们来上一个默认值,怎么做呢?
方法一:在参数上加@Value注解
这种方法非常简单,如下:
@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
@Value("132456")
private Integer uid;
@Value("wy")
private String uname;
@Value("asd")
private String password;
@Value("Beijing, Sichuan, Nanchang")
private List<String> addrs;
public User() {
}
public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
this.uid = uid;
this.uname = uname;
this.password = password;
this.addrs = addrs;
}
}
方法二:导入一个配置类
通常我们还是推荐这个方法,如下更改代码:
package wy.springboot01.domain;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
/**
* springboot项目启动的时候,自动将application.yml内容加载到实体对象中
*/
@Data
//将实体类交给spring管理,自动扫描
@Component
//加载配置内容,设定配置前缀,注意:prefix参数不支持小驼峰原则,必须全部小写
@ConfigurationProperties(prefix = "user")
public class User {
private Integer uid;
private String uname;
private String password;
private ArrayList<String> addrs;
public User() {
}
public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
this.uid = uid;
this.uname = uname;
this.password = password;
this.addrs = addrs;
}
}
显然我们通过@ConfigurationProperties这个注解去加载了配置类,我们只需要在与java这个路径平行的resources路径下新增一个yml文件当做配置即可,比如我们就叫做application.yml(通常这个路径会有一个默认的application.properties,但我习惯了用yml做配置这里就不写application.properties做配置的方法了,其实也很简单感兴趣的可以自行去搜索一下),我们这么来配置它(一定要注意冒号后面的空格):
#配置属性并加载到User实体中
user:
uid: 1546879
uname: wuyu
password: wuyu1999
addrs:
- Beijing
- Sichuan
- Nanchang
可以看到我们的前缀叫做user,所以我们在User中使用@ConfigurationProperties注解时,参数要填 prefix="user"。这么一来,这些默认值就会在运行的时候被加载进去,此时如果你再次运行IoCTest,就会输出有上述值的对象(需要注意的是,很多时候IoC创建对象这种方式有可能不生效,还是建议使用@Value)
还有一种特殊情况:如果一个类的参数包含了另一个类的对象,这个时候我们就要用到依赖注入@Autowired:
@Data
//将实体类交给spring管理,自动扫描
@Component
public class SuperUser {
@Value(“777”)
private String info1;
@Value(“666”)
private String info2;
@Autowired
private User user;
}
当时我试到这一步的时候产生了一个疑问,有的时候我们为了好区分或者说为了好给读代码的人信息,可能我这个User类在注入Bean的时候(即@Component这一步)我会加一些参数,打个比方我会写成@Component("ctyunUser"),那这个User类注入到bean里面的名称就叫ctyunUser。
这个时候我的SuperUser的user参数还找不找得到User这个类呢?大家可以自己去试一下,实际上是找的到的,因为@Autowired是默认使用参数的类型去找,在这个例子里他是默认去找Bean中有没有User这个类,实际上是有的只不过它的别名叫做ctyunUser。
如果我们要用名称去找,就需要在@Autowired下面再加一个@Qualifier(“ctyunUser”),这样他就会根据名称去找,如果这个时候User的@Component的参数没有写或写的不是ctyunUser,创建对象就会报错。
这一块大家可以自己去试一试,还是挺有意思的
以上就是IoC非常初级的一个用法,下一篇我们会介绍一些进阶的用法和更多有趣的案例