一、IOC思想
https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html
IOC理论的提出
在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson 1996年提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中。
IOC理论的含义
IOC提出的观点:借助于“第三方”实现具有依赖关系的对象之间的解耦
IOC的解释
在引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
软件系统在引入IOC容器之后,这种情形就完全改变了。由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
依赖注入
2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
IOC优点
松耦合、灵活性、可维护
IOC缺点
生成对象的步骤变得复杂,增加学习成本;
运行效率有一定损耗;
大量配置工作;
IOC框架本身的成熟度会影响项目整体的进程;
二、xml方式配置bean
todo 补代码
https://www.cnblogs.com/ysmdbk/p/11398482.html
1.无参构造
构建一个学生类,添加属性及get,set方法,添加tostring方法
package com.gf.springboot.ioc.xml;
import java.util.List;
public class Student {
private String name;
private Integer age;
private List<String> classList;
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 List<String> getClassList() {
return classList;
}
public void setClassList(List<String> classList) {
this.classList = classList;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
", classList=" + String.join(",",classList) +
'}';
}
}
定义一个xml文件,定义学生类的bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<property name="name" value="zhangsan"/>
<property name="age" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
</beans>
构建一个类调用学生类
package com.gf.springboot.ioc.xml;
public class HelloService {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
return student.toString();
}
}
在xml文件中,定义调用类对学生类的引用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<property name="name" value="zhangsan"/>
<property name="age" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">
<property name="student" ref="student"/>
</bean>
</beans>
测试类测试
package com.gf.springboot;
import com.gf.springboot.ioc.xml.HelloService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
@SpringBootTest
@ContextConfiguration(locations = "classpath:ioc/demo.xml")
class SpringbootApplicationTests {
@Autowired
private HelloService helloService;
@Test
public void testHello(){
System.out.println(helloService.Hello());
}
}
2.有参构造
修改学生类,通过构造器注入
package com.gf.springboot.ioc.xml;
import java.util.List;
public class Student {
private String name;
private Integer age;
private List<String> classList;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
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 List<String> getClassList() {
return classList;
}
public void setClassList(List<String> classList) {
this.classList = classList;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
", classList=" + String.join(",",classList) +
'}';
}
}
修改xml文件,学生类bean的定义,添加constructor-arg标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">
<property name="student" ref="student"/>
</bean>
</beans>
测试
3.静态工厂方法
https://www.jianshu.com/p/ceb5ec8f1174
定义父类(animal类),子类(dog类,cat类),工厂类(animalfactory类)
package com.gf.springboot.ioc.xml;
public abstract class Animal {
abstract String getName();
}
package com.gf.springboot.ioc.xml;
public class Dog extends Animal {
@Override
String getName() {
return "dog";
}
}
package com.gf.springboot.ioc.xml;
public class Cat extends Animal{
@Override
String getName() {
return "cat";
}
}
package com.gf.springboot.ioc.xml;
public class AnimalFactory {
public static Animal getAnimal(String type) {
if ("dog".equals(type)) {
return new Dog();
} else {
return new Cat();
}
}
}
xml文件中定义两个子类的bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">
<property name="student" ref="student"/>
</bean>
<bean id="dog" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-method="getAnimal">
<constructor-arg value="dog"/>
</bean>
<bean id="cat" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-method="getAnimal">
<constructor-arg value="cat"/>
</bean>
</beans>
构建一个类调用动物类
package com.gf.springboot.ioc.xml;
public class HelloService {
private Student student;
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
return animal.getName();
}
}
xml文件中配置引用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">
<property name="student" ref="student"/>
<property name="animal" ref="dog"/>
</bean>
<bean id="dog" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-method="getAnimal">
<constructor-arg value="dog"/>
</bean>
<bean id="cat" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-method="getAnimal">
<constructor-arg value="cat"/>
</bean>
</beans>
测试类测试
4.实例工厂方法
修改animalfactory类,去掉static
package com.gf.springboot.ioc.xml;
public class AnimalFactory {
public Animal getAnimal(String type) {
if ("dog".equals(type)) {
return new Dog();
} else {
return new Cat();
}
}
}
修改xml文件,添加工厂bean,并修改dog,cat的bean,添加属性factory-bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">
<property name="student" ref="student"/>
<property name="animal" ref="cat"/>
</bean>
<bean id="animalFactory" class="com.gf.springboot.ioc.xml.AnimalFactory"/>
<bean id="dog" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-bean="animalFactory" factory-method="getAnimal">
<constructor-arg value="dog"/>
</bean>
<bean id="cat" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-bean="animalFactory" factory-method="getAnimal">
<constructor-arg value="cat"/>
</bean>
</beans>
测试类测试
优点
低耦合、对象关系清晰、集中管理
缺点
配置繁琐、开发效率稍低、文件解析耗时
三、注解方式配置bean
1.@Component声明
将xml中的bean定义注释掉,并在相应的类上添加@Component注解,在测试类中引用就可以发现并且不报错
package com.gf.springboot.ioc.xml;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
private Student student;
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
return "hello";
// return animal.getName();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.gf.springboot.ioc.xml.Student">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="13"/>
<property name="classList" >
<list>
<value>math</value>
<value>english</value>
</list>
</property>
</bean>
<!-- <bean id="helloService" class="com.gf.springboot.ioc.xml.HelloService">-->
<!-- <property name="student" ref="student"/>-->
<!-- <property name="animal" ref="cat"/>-->
<!-- </bean>-->
<bean id="animalFactory" class="com.gf.springboot.ioc.xml.AnimalFactory"/>
<bean id="dog" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-bean="animalFactory" factory-method="getAnimal">
<constructor-arg value="dog"/>
</bean>
<bean id="cat" class="com.gf.springboot.ioc.xml.AnimalFactory" factory-bean="animalFactory" factory-method="getAnimal">
<constructor-arg value="cat"/>
</bean>
</beans>
package com.gf.springboot;
import com.gf.springboot.ioc.xml.HelloService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
@SpringBootTest
//@ContextConfiguration(locations = "classpath:ioc/demo.xml")
class SpringbootApplicationTests {
@Autowired
private HelloService helloService;
@Test
public void testHello(){
System.out.println(helloService.Hello());
}
}
2.配置类中使用@Bean
使用注解来实现子类bean定义,需要新建一个BeanConfiguration类添加@Configuration注解,定义子类并添加@Bean注解
package com.gf.springboot.ioc.ann;
import com.gf.springboot.ioc.xml.Animal;
import com.gf.springboot.ioc.xml.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean("dog")
Animal getDog(){
return new Dog();
}
}
在测试类中引用并测试
package com.gf.springboot.ioc.xml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
private Student student;
@Autowired
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
// return "hello";
return animal.getName();
}
}
3.继承FactoryBean
新建一个类实现类型为animal的FactoryBean,添加@Component注解,实现它的方法
package com.gf.springboot.ioc.ann;
import com.gf.springboot.ioc.xml.Animal;
import com.gf.springboot.ioc.xml.Cat;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class MyCat implements FactoryBean<Animal> {
@Override
public Animal getObject() throws Exception {
return new Cat();
}
@Override
public Class<?> getObjectType() {
return Animal.class;
}
}
在测试类中需要用@Qualifier注解指定哪一个实现类,测试
package com.gf.springboot.ioc.xml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
private Student student;
@Autowired
@Qualifier("myCat")
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
// return "hello";
return animal.getName();
}
}
4.继承BeanDefinitionRegistryPostProcessor
新建一个类实现BeanDefinitionRegistryPostProcessor接口,添加注解,实现需要重写的方法
重写postProcessBeanDefinitionRegistry方法
package com.gf.springboot.ioc.ann;
import com.gf.springboot.ioc.xml.Monkey;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class MyBeanRegister implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(Monkey.class);
registry.registerBeanDefinition("monkey",rootBeanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
package com.gf.springboot.ioc.xml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
private Student student;
@Autowired
@Qualifier("monkey")
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
// return "hello";
return animal.getName();
}
}
5.继承ImportBeanDefinitionRegistrar
新建一个类实现ImportBeanDefinitionRegistrar接口,重写对应方法,方式和方法4一致
package com.gf.springboot.ioc.ann;
import com.gf.springboot.ioc.xml.Bird;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
@Component
public class MyBeanImport implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(Bird.class);
registry.registerBeanDefinition("bird",rootBeanDefinition);
}
}
package com.gf.springboot.ioc.xml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
private Student student;
@Autowired
@Qualifier("bird")
private Animal animal;
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String Hello() {
// return "hello";
return animal.getName();
}
}
在测试类指定实现类名称
package com.gf.springboot;
import com.gf.springboot.ioc.ann.MyBeanImport;
import com.gf.springboot.ioc.xml.HelloService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
@SpringBootTest
//@ContextConfiguration(locations = "classpath:ioc/demo.xml")
@Import(MyBeanImport.class)
class SpringbootApplicationTests {
@Autowired
private HelloService helloService;
@Test
public void testHello(){
System.out.println(helloService.Hello());
}
}
优点
使用简单、开发效率高、高内聚
缺点
配置分散、对象关系不清晰、配置修改需要重新编译工程
四、refresh方法解析
bean配置读取加载入口,spring框架启动流程,面试重点
子方法调用
prepareRefresh方法:刷新上下文之前做一个准备
容器状态设置
初始化属性设置
检查必备属性是否存在
obtainFreshBeanFactory方法:获取bean工厂
设置beanFactory序列化id
获取beanFactory
prepareBeanFactory方法:对beanFactory进行配置
设置beanFactory一些属性
添加后置处理器
设置忽略的自动装配接口
注册一些组件
postProcessBeanFactory方法:注册web请求,相关的处理器和bean以及配置
子类重写以在BeanFactory完成创建后做进一步设置
invokeBeanFactoryPostProcesses方法
调用BeanDefinitionRegistryPostProcessor实现向容器内添加bean的定义
调用BeanFactoryPostProcessor实现向容器内bean的定义的添加属性
registerBeanFactoryPostProcessors方法
找到BeanPostProcessor的实现
排序后注册进容器内
initMessageSource方法
初始化国际化相关属性
initApplicationEventMulticaster方法
初始化事件广播器
onRefresh方法
创建web容器
registerListeners方法
添加容器内事件监听器至事件广播器中
派发早期事件
finishBeanFactoryInitialization方法
初始化所有剩下的单实例bean
finishRefresh方法
初始化生命周期处理器
调用生命周期处理器onRefresh方法
发布ContextRefreshedEvent事件
JMX相关处理
resetCommonCaches方法
事后清理缓存
五、refresh方法中bean实例化解析
spring容器是如何初始化单例bean
还有再多看几遍视频加深理解~~~
太难了~~
BeanDefinition介绍
一个对象在Spring中描述,RootBeanDefinition是其常见实现
通过操作BeanDefinition来完成bean实例化和属性注入
BeanDefinition类图
自定义创建bean
六、总结
介绍一下IOC思想?
springboot中bean有哪几种配置方式?分别介绍一下。
bean的配置你喜欢哪种方式,为什么?
介绍下refresh方法流程
请介绍一个refresh中你比较熟悉的方法说出其作用?
介绍下bean实例化的流程?
说几个bean实例化的扩展点及其作用?
补充学习:静态工厂模式,单例模式,反射机制