这篇文章我们将主要来讨论一下spring的IOC
目录
1.IOC概述
什么是IOC?
答:IOC — Inverse of Control,控制权反转,指将对象的创建权力反转给Spring框架!
我们可以结合下面的场景来思考一下:
在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况, 比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类出了问题,那么可能会导致这个框架出现问题。 Spring 将创建对象的权利给了IOC,在IOC当中创建了ABC三个对象吗,那么我们我们其他的类只需要调用集合, 大大的解决了程序耦合性的问题。
控制权反转(Inversion of Control,缩写为IOC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
解决问题:使用IOC可以解决程序耦合性高的问题。
2. IOC容器的底层原理
2.1 概述
IOC的实现,依赖于以下三门技术:
- dog4j 解析xml文档
- 工厂模式
- 采用反射设计模式创建对象
说明:
1.dog4j 解析xml文档:暂时了解即可,没必要知道太多
2.工厂模式:这里简单讲解一下,后面会有专门的工厂模式讲解
工厂模式,指的是:不同的类在相互调用时,不直接调用对方,而是我们创建一个工厂,在工厂中创建这些类,然后调用时是从工厂中调用创建好的,就如同下面这张图片所示:
如果还不是太懂,下面我们来看一个具体的例子:
然后,我们再来看一下用工厂模式怎么写上面的这里例子:
如上图所示,A调用创建B的工厂BFactory,然后BFactory创建B,这样就不用在A中直接创建B了。这就是一种简单的工厂模式。
第一种方法,A类调用B类就必须自己在自己的内部新建B类的对象,这样耦合度很高,而第二种方法,采用工厂模式就能很有效的降低耦合度。
工厂模式,说白了,就是一种降低耦合度的方法。
3.采用反射设计模式创建对象:反射在java基础中讲过,这里不多说。
2.2 上面的三种技术如何实现IOC?
第一步:配置xml文件,配置创建对象
就如同下图:
第二步:创建工厂类(代码如下):
public class DemoFactory {
//利用dom4j得到name所对应的value值
public static Demo getDemo() throws Exception {
//利用dom4j得到name所对应的value值
String value="class路径";
//通过反射创建对象
Class clazz = Class.forName(value);
//返回并创建demo对象
return (Demo) clazz.newInstance();
}
}
通过以上两步,我们基本上就可以得到我们所创建的对象。
(上面这些东西的目的,作用,及如何写我也不是太懂)
2.3 IOC(接口)
1. IOC思想是基于IOC容器完成的,IOC的底层就是对象工厂
2. Spring里边提供了IOC容器的实现的两种方式
(1) BeanFactroy:IOC容器是Spring内部的使用接口,不提供给开发人员使用
注意:BeanFactroy在加载配置文件的时候不会去创建对象,在使用对象的时候才会去创建对象;也就是说,当你运行时,不会创建对象,只有当你调用了它或使用它时,才会创建对象(这是牺牲时间换取空间的做法)
(2)ApplicationContext: BeanFactory接口的子接口,提供了更多更强大的功能,一般由开发人员进行使用
注意:ApplicationContext在加载配置文件的时候就会把对象创建出来。(这是牺牲空间换取时间的做法,我=我们通常选用这个)
3.Spring框架的Bean管理
3.1 什么是bean管理
什么是bean管理? bean管理,说白了,就是对象的管理;spring对bean的管理就是spring对java对象的管理。
管理什么?spring的bean管理包括以下两个方面:
- 创建对象
- 注入属性
(1)创建对象,我们前面说了许多,IOC就是干这个的
(2)注入属性,重点说下这个。属性,即类中的全局变量,注入属性,即spring对对象中的变量赋值
3.2 bean管理操作的两种方式
bean管理主要有以下两种方式,即无论是创建对象还是注入属性均有下面的两种方式:
- 基于xml配置文件的方式实现
- 基于注解的方式实现
3.3 基于xml配置文件的方式实现bean管理
下面,我们将来学习一下基于xml配置文件的方式实现创建对象和属性注入两种操作
3.3.1 基于xml方式创建对象
这个前一篇文章讲过,就是IOC,控制权反转,下面给几张图,大家快速想一下过程就行
第一步:创建对象:
第二步:在xml文件中进行配置扫描:
第三步:写测试方法:
第四步:测试运行:
注意:工厂在创建对象的时候,默认是执行无参构造方法完成对象
3.3.2 基于xml方式注入属性
- 依赖注入的概述:
IOC和DI的概念
- IOC:Inverse of Control,控制反转,将对象的创建权反转给Spring!!
-
Dependency Injection,依赖注入,就是注入属性
-
属性的set方法注入值:
这种方式叫set注入,因为它在属性注入时调用了类中的set方法。下面,我们来看一下具体的书写:
第一步:写类(注意:必须要给每个变量写set方法,代码如下:)
public class User {
private int age;
private String name;
private Demo01 demo01;
//必须要写每个变量的set方法
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Demo01 getDemo01() { return demo01; }
public void setDemo01(Demo01 demo01) { this.demo01 = demo01; }
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
", demo01=" + demo01 +
'}';
}
public void hello(){
System.out.println("Hello,User!");
}
}
第二步:进行xml文件中书写属性注入(代码如下):
<!--配置扫描-->
<!--set注入-->
<!--使用 property完成属性注入
name:类里面属性名称
value:向属性注入值
ref:对象映射-->
<bean id="user" class="com.qcby.service.User">
<property name="age" value="20"/>
<property name="name" value="张三"/>
<property name="demo01" ref="demox"/>
</bean>
截图如下:
说明:
第17行:property:表示是属性注入,必须放在bean标签内部;name:后面写类中的属性名称;value:写该属性的值;ref:对象映射,也就是特殊字段特殊处理,这里要注意:ref后面接的一定是存在的对象,也就是在这个xml文件中已经扫描配置的对象,不能是没有扫描配置的,这一点特别要注意!
第三步:运行测试(截图如下):
第17行挺有意思的,大家可以试一下。
- 属性构造方法的方式注入值
这种方式叫构造方法注入,因为它在属性注入时调用了类中的构造方法。下面,我们来看一下具体的书写:
第一步:在类中写构造方法(代码如下):
public class User {
private int age;
private String name;
private Demo01 demo01;
//使用构造方法注入要写构造方法!!!
public User(int age,String name,Demo01 demo011){
this.age=age;
this.name=name;
this.demo01=demo011;
}
//必须要写每个对象的set方法
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Demo01 getDemo01() { return demo01; }
public void setDemo01(Demo01 demo01) { this.demo01 = demo01; }
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
", demo01=" + demo01 +
'}';
}
public void hello(){
System.out.println("Hello,User!");
}
}
第二步:在xml文件中书写属性注入(代码如下):
<bean id="user" class="com.qcby.service.User">
<constructor-arg name="age" value="18"/>
<constructor-arg name="name" value="张三"/>
<constructor-arg name="demo011" ref="demox"/>
</bean>
截图如下:
说明:
第23行:当我们使用set注入时,使用的是property标签;当我们使用构造器注入时,要使用constructor-arg标签;然后的name,value,ref和前面的一样;注意:name后面接的是你构造器中定义的参数名称,不是你全局变量的名称。
第三步:运行测试(截图如下):
上面写的是使用xml进行属性注入的两种方式,即set方法和构造器的方法;但是,当我们的的全局变量发生了变化,那我们又该怎么对它们进行属性注入呢?下面,我们一起来看看
- 数组,集合(List,Set,Map)等属性值的注入
这里主要讲的是当你的全局变量中有数组,集合时,我们对其进行属性注入的方式,这里用的是xml注入的set方式,依然是三步走战略
第一步:写类(注意写set方法):
public class CollectionBean {
private String [] strs;
private List<String> list;
private Map<String,String> map;
public String[] getStrs() { return strs; }
public void setStrs(String[] strs) { this.strs = strs; }
public List<String> getList() { return list; }
public void setList(List<String> list) { this.list = list; }
public Map<String, String> getMap() { return map; }
public void setMap(Map<String, String> map) { this.map = map; }
@Override
public String toString() {
return "CollectionBean{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", map=" + map +
'}';
}
}
截图如下:
第二步:在xml文件中写属性注入(代码如下):
<bean id="collectionBeans" class="com.qcby.service.CollectionBean">
<!--给数组赋值-->
<property name="strs">
<array>
<value>A</value>
<value>B</value>
<value>C</value>
</array>
</property>
<!--给list赋值-->
<property name="list">
<list>
<value>熊大</value>
<value>熊二</value>
<value>熊三</value>
</list>
</property>
<!--给map赋值-->
<property name="map">
<map>
<entry key="aa" value="张三"></entry>
<entry key="bb" value="李四"></entry>
</map>
</property>
</bean>
截图如下:
说明:
第30行:set方式注入,用property,没啥问题
第31行:因为是给数组注入值的,所以用array标签,并且数组有多个值,所以下面的value可以多写
第39行:因为是给list类型的变量注入值,所以用list标签,可以注入多个值,所以value可以多写
第47行:因为是给map类型的变量注入值,所以用map标签
第48行:map的形式是key-value形,所以注入值的形式也是这种形式,entry标签记住就行,我也不知道是干啥的
第三步:写测试方法,运行测试(截图如下):
小结:上面就是用xml的方式实现了对bean的管理,即实现创建对象和属性注入两方面的内容。实现创建对象就是在xml文件中进行配置扫描,属性注入就是在xml文件中使用property或constructor-arg标签然后写值,其中属性注入分为两种方法即set方法和构造器方法,并且我们特地的说明了一些特殊的变量像集合、类变量的属性注入的方法。
3.4 基于注解的方式实现bean管理
上面我们讲了基于xml配置文件的方式来实现对bean的管理,现在,我们来说一下基于注解的方式来实现对bean的管理。
3.4.1 注解概述
什么是注解?
①:注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)
②:使用注解,注解作用在类上面,方法上面,属性上边
③:使用注解的目的:简化XML配置
3.4.2 用注解的方式来创建对象
- Spring针对Bean管理中创建对象提供的注解
@Component 普通的类(用于普通的类的,没啥实际意义)
@Controller 表现层(针对表现层的)
@Service 业务层(针对业务层的)
@Repository 持久层(针对持久层的,就是Mybatis那一层)
上边四个功能一样,都可以用来创建bean实例,其中@Controller和@Service是比较重要的。
- 实际书写一下,用注解的方式来创建对象
下面就让我们来实际写一下,如何用注解的方式来创建对象。
第一步:给类加注解(如下图所示):
第二步:进行注解扫描的配置
说明:
第11行:这里依然是在xml文件中配置的,但是与之前的配置扫描不同,之前扫描的是类,这里扫描的是注解,要注意一下。
第三步:运行测试:
注意!!!!:
第23行:getBean传入的参数是啥?这里分两种情况。第一种,在注解中没有赋值,那么此时传入的参数就是你类的名称(首字母小写,如果类的首字母已经小写了,那就写类名,为什么?因为用idea创建对象时,敲个空格后面出现的就是你首字母小写的类名)。第二种,在注解中赋值了,那就直接写注解中的值,从此,注解中的名字将代替你类的名字。我们可以看下下面的这种情况。
其余的类写起来也是大同小异,多练练就会了。
3.4.2 用注解的方式来注入属性
下面我们来看看如何用注解的方式来实现属性注入
- Spring针对Bean管理中属性注入提供的注解
@Value 用于注入普通类型(String,int,double,Integer等类型)
@Autowired 默认按类型进行自动装配(用于引用类型)
@Qualifier 不能单独使用必须和@Autowired一起使用,强制使用名称注入
@Resource Java提供的注解,也被支持。使用name属性,按名称注入
- 实际来写一下,用注解的方式来给属性注入值
下面就让我们来实际写一下,如何用注解的方式来给属性注入值。依然是“三步走”战略
第一步:写类(给类上注解,给属性上注解)代码如下:
@Component(value = "user011")
public class User {
private int age;
private String name;
@Autowired
private Demo01 demo01;
//使用构造方法注入要写构造方法!!!
// public User(int age,String name,Demo01 demo011){
// this.age=age;
// this.name=name;
// this.demo01=demo011;
// }
//必须要写每个对象的set方法
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Demo01 getDemo01() { return demo01; }
public void setDemo01(Demo01 demo01) { this.demo01 = demo01; }
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
", demo01=" + demo01 +
'}';
}
public void hello(){
System.out.println("Hello,User!");
}
}
截图如下:
第二步:在xml中进行注解扫描的配置(截图如下):
第三步:运行测试(截图如下):
对整个流程进行做一下说明:
(1)首先明确我们在干什么,我们在用注解的方式来实现Bean管理中的属性注入,所以原本写的xml的方式就要注释掉,避免出现错误
(2)使用注解,所以我们要进行注解扫描
(3)注释掉了xml的方式的语句,而又要使用注解的方式,首先你要生成对象,所以要用到生成类对象的注解
(4)关于属性注入的注解,@Value不多说,@Autowired用与引用类型的属性上,不需要给什么值,它会自动进行匹配,就像我代码写的,那里有个Demo01的类,并且在这个类上,我加了@Component注解,那么也就是说当我运行UserTest时,Demo01这个类也会被创建出来存储起来,用的时候直接被调用(spring干的),然后我在User类里定义了Demo01类型的变量,我用@Autowired给它赋值,那么这个注解就会进行自动匹配,从我的内存中有没有事先创好的类,有那就匹配上,没有就报错
不信,我们可以试一下:
然后,我们解掉一个注解,再运行一下:
很有意识的,可以自己试一下。
4. IOC纯注解开发方式
4.1 概述
什么是纯注解开发?
纯注解开发就是不用xml文件,只用注解的方式来写。这也是我们之后写spring的主要方式。
纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目的是替换掉所有的配置文件。但是需要编写配置类。
常用的注解总结:
@Configuration 声明是配置类
@ComponentScan 扫描具体包结构的
4.2 具体书写
第一步:编写实体类(类的创建和参数注入都要用注解的方式):
代码如下(User类):
@Component(value = "user011")
public class User {
@Value("20")
private int age;
@Value("张三")
private String name;
@Autowired
private Demo01 demo01;
//使用构造方法注入要写构造方法!!!
// public User(int age,String name,Demo01 demo011){
// this.age=age;
// this.name=name;
// this.demo01=demo011;
// }
//必须要写每个对象的set方法
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Demo01 getDemo01() { return demo01; }
public void setDemo01(Demo01 demo01) { this.demo01 = demo01; }
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
", demo01=" + demo01 +
'}';
}
public void hello(){
System.out.println("Hello,User!");
}
}
第二步:编写配置类
创建包,命名为config,在这个包里创建类,命名为SpringConfig,然后给类加注解(代码如下):
@Configuration//作用:声明这是一个配置类
@ComponentScan(value = "com.qcby.service") //作用:扫描bean类所在的包
public class SpringConfig {
}
截图如下:
第三步:写测试方法(代码如下):
@Test
public void runByZhuJie(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = ac.getBean("user011",User.class);
user.hello();
System.out.println(user.toString());
}
截图如下:
第四步:运行测试(截图如下):
没啥难的。
5. 小结
这篇文章我们主要讲了spring的IOC和其对Bean的管理两方面内容。IOC,说白了是一种技术,是一种思想,是spring的一个核心特点,它体现在对bean的管理里面。在对bean的管理里面,我们按内容分,讲了创建对象和属性注入两部分内容,按操作方式分,讲了xml配置的方式和注解的方式两部分。现在一起回想一下,创建对象用xml怎么写,用注解怎么写。属性注入用xml怎么写,用属性注入怎么写。之后,我们讲了IOC的纯注解开发,这也是我们以后写spring的主要方式。