IOC:Inversion of Control 即反转控制
获取资源的传统方式
自己做饭:买菜、洗菜、择菜、改刀、炒菜,全过程参与,费时费力,必须清楚了解资源创建整个过程中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制方式获取资源
点外卖:下单、等、吃,省时省力,不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向--------改由容器主动的将资源推送给需要的组件,开发人员不需要知道容路是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动开式。
DI
Dl: Dependency Injection,翻译过来是依赖注入。
DI是IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现
Spring的IOC容器就是IOC思想的一个落地的产品的实现。IOC容器中管理的组件也叫bean,在创建bean之前,首先需要创建IOC容器。Spring提供了IOC容器的两种实现方式
这是IOC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用
BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory

| 类型名 | 简介 |
|---|---|
| ClassPathXmlApplicationContext | 通过读取类路径下的XML格式的配置文件创建IOC容器对象 |
| FileSystemXmlApplicationContext | 通过文件系统路径读取XML格式的配置文件创建IOC容器对象 |
| ConfigurableApplicationContext | ApplicationContext的子接口,包含一些拓展方法refresh()和close(),让ApplicationContext具有启动、关闭和刷新上下文的能力 |
| WebApplicationContext | 专门为web应用准备,基于web环境创建IOC容器对象,并将对象引入存入ServletContext域中 |
org.springframework spring-context 5.3.1
junit junit 4.13.2 test
package com.bijing.spring.pojo;/*** @author 毕晶* @date 2022/11/11 14:50*/
public class HelloWorld {public void sayHello(){System.out.println("hello,spring");}
}
创建Spring的配置文件

在Spring的配置文件中配置bean
application.xml
package com.bijing.spring.test;import com.bijing.spring.pojo.HelloWorld;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 毕晶* @date 2022/11/11 14:56*/
public class HelloWorldTest {@Testpublic void testHelloWorld(){//获取IOC容器ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");//获取IOC容器中的beanHelloWorld helloworld = (HelloWorld) ioc.getBean("helloworld");helloworld.sayHello();}
}
如果组件类实现了接口,根据接口类型可以获取bean吗
可以,前提是bean唯一
//此时Student实现了Person接口,组件类是Student,所以编译类型是Person,运行类型是StudentPerson person = ioc.getBean(Person.class);System.out.println(person.getClass());//class com.bijing.spring.pojo.Student
如果一个接口有多个实现类,这些实现类都配置了bean,根据接口类型可以获取bean吗
不行,因为bean不唯一
此时,因为Person的实现类有多个,不知道用哪个实现类的bean
7. 结论
根据类型来获取bean时,在满足bean唯一的前提下,其实只是看:对象instanceof指定的类型 的返回结果,只要是true就可以认为和类型匹配,就能够获取到。即通过bean的类型,bean所继承的类的类型,bean所实现的接口类型都可以获取bean
@Testpublic void testDI() {//获取IOC容器ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");//获取beanStudent studentTwo = ioc.getBean("studentTwo", Student.class);System.out.println(studentTwo);}
Student studentThree = ioc.getBean("studentThree", Student.class);System.out.println(studentThree);
什么是字面量?
int a=10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际拿到的值是10
如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母,这就是字面量。
]]>
package com.bijing.spring.pojo;/*** @author 毕晶* @date 2022/11/11 19:27*/
public class Clazz {private Integer cid;private String cname;public Clazz(Integer cid, String cname) {this.cid = cid;this.cname = cname;}public Clazz() {}public Integer getCid() {return cid;}public void setCid(Integer cid) {this.cid = cid;}public String getCname() {return cname;}public void setCname(String cname) {this.cname = cname;}@Overridepublic String toString() {return "Clazz{" +"cid=" + cid +", cname='" + cname + '\'' +'}';}
}
package com.bijing.spring.pojo;/*** @author 毕晶* @date 2022/11/11 15:09*/
public class Student implements Person {private Integer sid;private String sname;private Integer age;private String gender;private Clazz clazz;public Student() {}public Student(Integer sid, String sname, Integer age, String gender) {this.sid = sid;this.sname = sname;this.age = age;this.gender = gender;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public Clazz getClazz() {return clazz;}public void setClazz(Clazz clazz) {this.clazz = clazz;}@Overridepublic String toString() {return "Student{" +"sid=" + sid +", sname='" + sname + '\'' +", age=" + age +", gender='" + gender + '\'' +", clazz=" + clazz +'}';}
}
package com.bijing.spring.pojo;import java.util.Arrays;/*** @author 毕晶* @date 2022/11/11 15:09*/
public class Student implements Person {private Integer sid;private String sname;private Integer age;private String gender;private String[] hobby;private Clazz clazz;public Student() {}public Student(Integer sid, String sname, Integer age, String gender) {this.sid = sid;this.sname = sname;this.age = age;this.gender = gender;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public Clazz getClazz() {return clazz;}public void setClazz(Clazz clazz) {this.clazz = clazz;}public String[] getHobby() {return hobby;}public void setHobby(String[] hobby) {this.hobby = hobby;}@Overridepublic String toString() {return "Student{" +"sid=" + sid +", sname='" + sname + '\'' +", age=" + age +", gender='" + gender + '\'' +", hobby=" + Arrays.toString(hobby) +", clazz=" + clazz +'}';}
}
抽烟 喝酒 烫头 蹦迪
通过util:list
需要导入util约束
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
//Student增加下面属性private Map teacherMap;
抽烟 喝酒 烫头 蹦迪
引入约束p,应该是指property
xmlns:p="http://www.springframework.org/schema/p"
jdbc为例
mysql mysql-connector-java 8.0.30 com.alibaba druid 1.0.31
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimeZone=UTC"
jdbc.username=root
jdbc.password=root1234
xmlns:context="http://www.springframework.org/schema/context"
public class DataSourceTest {@Testpublic void testDataSource() throws SQLException {ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");DruidDataSource datasource = ioc.getBean("datasource", DruidDataSource.class);DruidPooledConnection connection = datasource.getConnection();System.out.println(connection);}
}
| 取值 | 含义 | 创建对象的时机 |
|---|---|---|
| singleton(默认) | 在IOC容器中,这个bean对象始终为单例 | IOC容器初始化 |
| prototype | 这个bean在IOC容器中有多个实例 | 获取bean时 |
如果在WebApplicationContext环境下还有另外两个作用域(但不常用)
| 取值 | 含义 |
|---|---|
| request | 在一个请求范围内有效 |
| session | 在一次会话范围内有效 |
@Testpublic void testScope() {ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");Student student1 = ioc.getBean("student", Student.class);Student student2 = ioc.getBean("student", Student.class);System.out.println(student1 == student2);//true说明是单例模式创建的student,是同一个对象}
package com.bijing.spring.pojo;/*** @author 毕晶* @date 2022/11/11 22:31*/
public class User {private Integer id;private String username;private String password;private Integer age;public User() {System.out.println("生命周期1:实例化");}public User(Integer id, String username, String password, Integer age) {this.id = id;this.username = username;this.password = password;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {System.out.println("生命周期2:依赖注入");this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", age=" + age +'}';}public void initMethod() {System.out.println("生命周期3:初始化");}public void destroyMethod() {System.out.println("生命周期4:销毁");}
}
package com.bijing.spring.test;import com.bijing.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 毕晶* @date 2022/11/11 22:43*/
public class LifeCycleTest {/*** 1.实例化* 2.依赖注入* 3.初始化 要通过bean标签的init-method属性指定方法* 4.IOC容器关闭时销毁 要通过bean标签的destroy-method属性指定方法* * 注意:* 若bean的作用域是单例时,生命周期的前三个步骤会在获取IOC容器时执行* 若bean的作用域是多例时,生命周期的前三个步骤会在获取bean时执行,销毁步骤不由ioc控制了*/@Testpublic void testLifeCycle() {//ConfigurableApplicationContext是ApplicationContext的子接口,其中拓展了刷新和关闭容器的方法//注意:1.如果是bean对象是单例模式获取的,则生命周期前三个步骤已经完成,即ioc容器创建的时候bean对象就已经创建并完成了初始化// 2.如果是多例,destroy步骤不由ioc来控制了,即ioc.close()时销毁方法不执行ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");User user = ioc.getBean(User.class);System.out.println(user);//ApplicationContext没有close方法ioc.close();}
}
创建bean的后置处理器
package com.bijing.spring.pojo.com.bijing.spring.process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;/*** @author 毕晶* @date 2022/11/12 10:17*/
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {//此方法在bean的生命周期初始化之前执行System.out.println("此方法在bean的生命周期初始化之前执行");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {//此方法在bean的生命周期初始化之后执行System.out.println("此方法在bean的生命周期初始化之后执行");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}
配置bean的后置处理器
简介
Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,但工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
创建类UserFactoryBean
package com.bijing.spring.factory;import com.bijing.spring.pojo.User;
import org.springframework.beans.factory.FactoryBean;/*** @author 毕晶* @date 2022/11/12 10:35*/
public class UserFactoryBean implements FactoryBean {@Overridepublic User getObject() throws Exception {return new User();}@Overridepublic Class> getObjectType() {return User.class;}
}
public class FactoryBeanTest {@Testpublic void testFactoryBean() {ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");User user = ioc.getBean(User.class);System.out.println(user);}
}
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或者接口类型属性赋值
public class UserController {private UserService userService;//UserService会变动,不要直接写死,使用set和get方法有利于改动public UserService getUserService() {return userService;}public void setUserService(UserService userService) {this.userService = userService;}public void saveUser() {userService.saveUser();}
}
package com.bijing.spring.test;import com.bijing.spring.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 毕晶* @date 2022/11/12 17:13*/
public class AutowireByXmlTest {/*** 自动装配:* 根据指定的策略,在IOC容器中匹配某个bean,自动为bean中的类类型的属性或者接口类型属性赋值* 可以通过bean标签中autowire属性设置自动装配的策略* 自动装配策略:* #no,default表示不装配,即bean 中的属性不会自动匹配某个bean为属性赋值,此时属性使用默认值* #byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值* 注意:1.若通过类型没有找到任何一个类型匹配的bean,此时不匹配,属性使用默认值* 2.若通过类型找到了多个类型匹配的bean,此时会抛出异常:* 总结:当使用byType实现自动装配时,IOC容器中有且只有一个类型匹配的bean能够为属性赋值* #byName:将要赋值的属性的属性名作为bean的id在IOC容器中匹配某个bean为属性赋值* 当类型匹配的bean有多个时,此时可以使用byName实现自动装配*/@Testpublic void testAutowireByXml() {ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire-xml.xml");UserController userController = ioc.getBean(UserController.class);userController.saveUser();}
}