Spring技术原理之Spring IOC

简介: Spring技术原理之Spring IOC

依赖倒置原则: 依赖倒置的核心是面向接口编程



1.定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。


2.问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。


3.解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。


控制反转(Inversion of Control):



是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。


image.png


IoC Contain:



  1. 避免写大量的new来创建对象,将对象间的依赖关系,配置化。


  1. 创建实例的时候不需要了解具体的细节。


Spring Bean生产总体流程:



image.png


Spring 是一个大的 Bean 工厂,创建Bean时要考虑如下问题:



1.Bean定义。根据配置文件或者注解加载Bean定义到BeanDefinitionMap


2.作用域。单例作用域或者原型作用域,单例的话需要全局实例化一次,原型每次创建都需要重新实例化。


3.依赖关系。一个 Bean 如果有依赖,我们需要初始化依赖,然后进行关联。如果多个 Bean 之间存在着循环依赖,A 依赖 B,B 依赖 C,C 又依赖 A,需要解这种循环依赖问题。


Bean 定义:



  1. 基于XML


<bean id="userService" class="com.javhl.***.UserService" init-method="init" destory-method="destory"/>


2.基于注解


@Component:当对组件的层次难以定位的时候使用这个注解
@Controller:表示控制层的组件
@Service:表示业务逻辑层的组件
@Repository:表示数据访问层的组件


3.基于JavaConfig


@Configuration
public class JavaConfigBeanTest {
    @Bean
    public BeanTest beanTest(){
        BeanTest beanTest = new BeanTest();
        beanTest.setTestField("I am a beanTest");
        return beanTest;
    }
}


作用域:



image.png


循环依赖问题:




image.png


1.循环依赖根据注入方式分成两种类型:


     1.构造器循环依赖。依赖的对象是通过构造器传入的,发生在实例化 Bean 的时候。


     2.设值循环依赖。依赖的对象是通过 setter 方法传入的,对象已经实例化,发生属性填充和依赖注入的时候。


    3. 如果是构造器循环依赖,本质上是无法解决的。比如我们调用 A 的构造器,发现依赖 B,于是去调用 B 的构造器进行实例化,发现又依赖 C,于是调用 C 的构造器去初始化,结果依赖 A,整个形成一个死结, 导致 A 无法创建。如:A { public A(B b){}};B{public B(A a){}},A,B实例化时形成构造器循环依赖


     4.如果是设值循环依赖,Spring框架只支持单例下的设值循环依赖。Spring 通过对还在创建过程中的单例,缓存并提前暴露该单例,使得其他实例可以引用该依赖。


2.原型模式和单例构造器循环依赖问题:


image.png


     1.加载 A,记录 singletonsCurrentlyInCreation = [a],构造依赖 B,开始加载 B。


     2.加载 B,记录 singletonsCurrentlyInCreation = [a, b],构造依赖 C,开始加载 C。


     3.加载 C,记录 singletonsCurrentlyInCreation = [a, b, c],构造依赖 A,又开始加载 A


     4.加载 A,检测到循环依赖,直接抛出异常结束操作


3.单例setter循环依赖解决方法:


image.png


protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 查询缓存中是否有创建好的单例
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果缓存不存在,判断是否正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加锁防止并发
        synchronized (this.singletonObjects) {
            // 从earlySingletonObjects中查询是否有early缓存
            singletonObject = this.earlySingletonObjects.get(beanName);
            // early缓存也不存在,且允许early引用
            if (singletonObject == null && allowEarlyReference) {
                // 从单例工厂Map里查询beanName
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // singletonFactory存在,则调用getObject方法拿到单例对象
                    singletonObject = singletonFactory.getObject();
                    // 将单例对象添加到early缓存中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 移除单例工厂中对应的singletonFactory
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}


     1.采用三级缓存


     2.singletonObjects(一级缓存),单例缓存,存储已经实例化完成的单例,成品的Bean。


     3.earlySingletonObjects(二级缓存),提前暴露的单例缓存,这时候的单例刚刚创建完,后期还会注入依赖,存放半成品的Bean。


     4.singletonFactories(三级缓存),生产单例的工厂的缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。如果Bean存在AOP的话,返回的是AOP的代理对象。为了解决二级缓存中 AOP 生成新对象的问题,Spring 中的解决方案就是提前 AOP。


     5.从流程图上看,实际上注入 C的 A 实例,还在填充属性阶段,并没有完全地初始化。等递归回溯回去,A 顺利拿到依赖 B,才会真实地完成 A 的加载。


BeanFactory & FactoryBean:



1.BeanFactory: 以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。


2.FactoryBean: 一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式


3.以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。


public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String var1) throws BeansException;
    <T> T getBean(String var1, Class<T> var2) throws BeansException;
    Object getBean(String var1, Object... var2) throws BeansException;
    <T> T getBean(Class<T> var1) throws BeansException;
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
    boolean containsBean(String var1);
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
    String[] getAliases(String var1);
}
public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType(); 
    boolean isSingleton(); 
} 


//FactoryBean实例
@Component public class FactoryBeanTest implements FactoryBean{
    private String type=”a”; 
    @Override
    public Object getObject() throws Exception { 
        if("a".equals(type)){ 
            return new A(); 
        }else{
            return new B(); 
        } 
    }
    @Override public Class getObjectType() { 
        if("a".equals(type)){ 
            return A.class; 
        }else{
            return B.class; 
        } 
    }
    @Override public boolean isSingleton() { 
        return true; 
    } 
}
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testBeans.xml");
String[] names = applicationContext.getBeanDefinitionNames();
Arrays.stream(names).forEach(e->System.out.println(e));
//此处获取的是类A的实例
Object realBean = applicationContext.getBean("factoryBeanTest");
System.out.println(realBean.getClass().getName());
//此处获取的是FactoryBeanTest的实例
FactoryBeanTest factoryBeanTest = (FactoryBeanTest)
applicationContext.getBean("&factoryBeanTest");
((ClassPathXmlApplicationContext) applicationContext).close();
目录
相关文章
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
26天前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
141 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
14天前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
4天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
35 9
|
2天前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
4 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
11天前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
23 4
|
14天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
55 3
|
27天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
27天前
|
Java 数据库连接 Maven
Spring基础1——Spring(配置开发版),IOC和DI
spring介绍、入门案例、控制反转IOC、IOC容器、Bean、依赖注入DI
Spring基础1——Spring(配置开发版),IOC和DI
|
2月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)