前言
IOC 是Spring的两大核心概念之一,它是一种思想,需要极其熟练的掌握。
今日摘录:
低能无聊的人太多。说他们勤勉,不过是因困为不会合理分配时间;说他们积极,不过是逃避其他困难工作而已。即便说工作只是生存手段,也没见他们有什么拿得出手的爱好或特长。我真是每天都在失望。低能无聊的人要是边不满边骂着自己的低能无聊却不愿做出任何改变,那就真是自寻烦恼自掘坟墓了。
——东野圭吾《变身》
1、IOC
IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
1.1、控制反转
- 控制反转不是技术,而是一种思想。
- 控制反转是为了降低程序耦合度,提高程序扩展力。
- 控制反转,反转的是什么?
- 将对象的创建权利交出去,交给第三方容器(IOC 容器)负责。
- 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
- 控制反转这种思想如何实现呢?
- DI(Dependency Injection):依赖注入
依赖注入(DI):
- 指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造器注入
所以,IOC 就是一种控制反转的思想, 而 DI 是对 IOC的一种具体实现。
1.2、获取 Bean 的 3 种方式
其实也就是 getBean() 的三种传参方式,下面对我们上一节的 User 类进行测试。
<bean id="user" class="com.lyh.study.User"/>
1.2.1、getBean(String id)
因为返回的是一个 Object 类型所以需要强转一下。
@Test public void testHelloWorld1(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); User user = (User)ac.getBean("user"); user.add(); }
1.2.2、 getBean(Class requiredType)
因为传入的就是一个类,所以可以自动推断出返回的类型。
注意:这种方式的配置文件中属于该类(com.lyh.study.User)的 bean 只能有一个,这个很好理解,如果有多个,它怎么知道你到底要取哪一个 bean。
@Test public void testHelloWorld1(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); User user = ac.getBean(User.class); user.add(); }
1.2.3、getBean(String id,Class requiredType)
这种方式同样指定了 bean 的类型,所以返回的直接就是该类型的 bean 对象。
@Test public void testHelloWorld1(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); User user = ac.getBean("user",User.class); user.add(); }
个人建议使用这种方式,不用咱们自己强转,代码看起来也比较直观。
1.3、依赖注入的 2 种方式
1.3.1、setter 注入
(1)创建 bean 目录,编写 Student 类:
package com.lyh.study.bean; public class Student { private Integer id; private String name; private Integer age; private String sex; public Student() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; } }
注意:这里的 Bean 必须有 setter 方法,否则属性无法注入(idea 直接就会在配置文件中爆红);如果不设置 getter 方法,我们的私有属性无法获取(被public 修饰的属性可以通过 对象.属性(比如 student1.id) 直接获取,但是被 private 修饰的属性必须通过显示的方法(比如 student1.getId())来获取)。
(2)配置 bean 时给属性赋值:
<bean id="student1" class="com.lyh.study.bean.Student"> <property name="id" value="1001"/> <property name="name" value="lyh"/> <property name="age" value="20"/> <property name="sex" value="男"/> </bean>
(3)测试:
@Test public void testStudent(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); Student student1 = ac.getBean("student1", Student.class); System.out.println(student1); }
(4)运行结果:
Student{id=1001, name='lyh', age=20, sex='男'}
1.3.2、构造器注入
(1)在我们的 JavaBean 中添加有参构造器:
public Student(Integer id, String name, Integer age, String sex) { this.id = id; this.name = name; this.age = age; this.sex = sex; }
(2)配置 bean
默认是按照我们构造器的顺序直接赋值:
<bean id="student2" class="com.lyh.study.bean.Student"> <constructor-arg value="1002"/> <constructor-arg value="燕双鹰"/> <constructor-arg value="28"/> <constructor-arg value="男"/> </bean>
也可以通过属性来指定属性的顺序:
- index 属性:从 0 开始(分别对应 id、name、age、sex)
- name 属性:直接指定参数名,然后通过 value 赋值
(3)测试
@Test public void testStudent(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); Student student2 = ac.getBean("student2", Student.class); System.out.println(student2); }
(4)运行结果:
Student{id=1002, name='燕双鹰', age=28, sex='男'}
1.4、给对象类型的属性注入的 3 种方式
给 Student 类添加一个 Address 类型的属性:
(1)创建 Address 类
package com.lyh.study.bean; public class Address { private String province; private String county; private String village; public Address(){ } public Address(String province, String county, String village) { this.province = province; this.county = county; this.village = village; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCounty() { return county; } public void setCounty(String county) { this.county = county; } public String getVillage() { return village; } public void setVillage(String village) { this.village = village; } @Override public String toString() { return "Address{" + "province='" + province + '\'' + ", county='" + county + '\'' + ", village='" + village + '\'' + '}'; } }
完了给我们的 Student 类添加一个 Adress 类型的属性,并设置 getter 和 setter 方法。
1.4.1、外部引用 Bean
第一种方式:我们通过外部 Bean 引用来给我们的属性赋值:
(1)配置文件添加(使用 ref 属性来引用其它 bean):
<bean id="address1" class="com.lyh.study.bean.Address"> <property name="province" value="山西省"/> <property name="county" value="天水县"/> <property name="village" value="英雄村"/> </bean> <bean id="student3" class="com.lyh.study.bean.Student"> <property name="id" value="1003"/> <property name="name" value="姜伯约"/> <property name="age" value="26"/> <property name="sex" value="男"/> <property name="address" ref="address1"/> </bean>
(2)测试:
@Test public void testStudent(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); Student student3 = ac.getBean("student3", Student.class); System.out.println(student3); System.out.println(student3.getAddress()); }
(3)运行结果:
Student{id=1003, name='姜伯约', age=26, sex='男'} Address{province='山西省', county='天水县', village='英雄村'}
1.4.2、内部 Bean
(1)配置文件:
<bean id="student4" class="com.lyh.study.bean.Student"> <property name="id" value="1004"/> <property name="name" value="李大喜"/> <property name="age" value="20"/> <property name="sex" value="男"/> <property name="address"> <!-- 内部bean不能给外部使用,所以可以省略 id 属性 --> <bean class="com.lyh.study.bean.Address"> <property name="province" value="山西省"/> <property name="county" value="英雄县"/> <property name="village" value="农民村"/> </bean> </property> </bean>
这里省略测试,和上面的结果是一样的。
1.4.3、级联属性赋值
使用外部 bean 重新给属性赋值:
<bean id="address1" class="com.lyh.study.bean.Address"> <property name="province" value="山西省"/> <property name="county" value="天水县"/> <property name="village" value="英雄村"/> </bean> <bean id="student5" class="com.lyh.study.bean.Student"> <property name="id" value="1005"/> <property name="name" value="狄仁杰"/> <property name="age" value="28"/> <property name="sex" value="男"/> <property name="address" ref="address1"/> <property name="address.province" value="山西省"/> <property name="address.county" value="杨柳县"/> <property name="address.village" value="大柳树村"/> </bean>
Spring 6(二)【IOC(2)https://developer.aliyun.com/article/1532217