IoC

控制反转,Ioc意味着将对象交给容器控制,而不是传统的在你的对象内部直接控制。
并管理它们的完整生命周期。降低了耦合度

构成了Spring框架IoC容器的基础:

  • org.springframework.beans 包
  • org.springframework.context包


配置注入方式

将Spring配置到应用开发中有以下三种方式:

  1. 基于XML的配置
  2. 基于注解的配置

主要的命名空间:context、beans、jdbc、tx、aop、mvc和aso

  1. 基于Java的配置

什么是依赖注入?
在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码中将组件和服务连接在
一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC容器将它们装配在一起。

可以通过多少种方式完成依赖注入?
在Java中依赖注入有以下三种实现方式:
1. 构造器注入
2. Setter方法注入
3. 注解注入
对于依赖关系无需变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。

IoC 容器

BeanFactory就像一个包含bean集合的工厂类。它会在客户端要求时实例化bean。ApplicationContext-ApplicationContext接口扩展了BeanFactory接口。它在BeanFactory基础上提供了一些额外的功能。

BeanFactory ApplicationContext
它使用懒加载 它使用即时加载
它使用语法显式提供资源对象 它自己创建和管理资源对象
不支持国际化 支持国际化
不支持基于依赖的注解 支持基于依赖的注解

列举** IoC **的一些好处。

它将最小化应用程序中的代码量。
它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例或 JNDI 查找机制。
它以最小的影响和最少的侵入机制促进松耦合。

实现原理

1、xml配置文件
2、dom4j解析XML文件
3、工厂模式
4、反射机制

IoC实现的两种方式

image.png

1. BeanFactory接口

org.springframework.beans.factory.BeanFactory
BeanFactory接口是Spring IoC 容器的核心接口。 内部使用的接口
BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。

采用懒加载, 加载配置文件时不创建对象, 一般在使用时才会创建对象

2. ApplicationContext接口

开发人员使用
ApplicationContex接口对BeanFactory(是一个子接口)进行了扩展,在BeanFactory的基础上添加了其他功能,比如与Spring的AOP更容易集成,也提供了处理message resource的机制(用于国际化)、事件传播以及应用层的特别配置,比如针对Web应用的WebApplicationContext。
加载配置文件时创建对象
BeanFactory和ApplicationContext有什么区别?
BeanFactory 可以理解为含有bean集合的工厂类。

  1. BeanFactory 包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
  2. BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。
  3. BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。

从表面上看,application context如同bean factory一样具有bean定义、bean关联关系的设置,根据请求分发bean的功能。但application context在此基础上还提供了其他的功能。
1.提供了支持国际化的文本消息
2.统一的资源文件读取方式
3.已在监听器中注册的bean的事件

以下是三种较常见的 ApplicationContext 实现方式:
1、ClassPathXmlApplicationContext:从classpath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
2、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
3、XmlWebApplicationContext:由Web应用的XML文件读取上下文。

Bean

什么是 Bean

它们是构成用户应用程序主干的对象。
Bean由SpringIoC容器管理。
它们由SpringIoC容器实例化,配置,装配和管理。Bean是基于用户提供给容器的配置元数据创建。

  • Bean : 在配置中定义的Bean的类型就是返回的类型
  • FactoryBean : image.png

    Bean的创建过程????????

    什么是BeanDefinition

    BeanDefinition——spring bean的建模对象
    BeanDefintion可以描述springbean当中的scope、lazy,以及属性和方法等等其他信息
    https://blog.csdn.net/java_lyvee/article/details/102633067

    Spring Bean 作用域

    Singleton(默认)-每个SpringIoC容器仅有一个单实例。加载配置文件时创建
    Prototype-每次请求都会产生一个新的实例。 懒加载
    Request-每一次网络请求都会产生一个新的实例,并且该bean仅在当前网络请求内有效。
    Session-每一次网络请求都会产生一个新的bean,同时该bean仅在当前session内有效。
    Global-session-类似于标准的HTTPSession作用域,不过它仅仅在基于portlet的web应用中才有意义。

仅当用户使用支持Web的ApplicationContext时,最后三个才可用

Portlet规范定义了全局Session的概念,它被所有构成某个portletweb应用的各种不同的portlet所共享。在globalsession作用域中定义的bean被限定于全局portletSession的生命周期范围内。如果你在web中使用globalsession作用域来标识bean,那么web会自动当成session类型来使用。

单例 Bean 的优点

1.减少了新生成实例的消耗
新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。
2.减少jvm垃圾回收
由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。
3.可以快速获取到bean
因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。

单例Beans不是线程安全的

单例的bean一个很大的劣势就是他不能做到线程安全!!!,由于所有请求都共享一个bean实例,所以这个bean要是有状态的一个bean的话可能在并发场景下出现问题,而原型的bean则不会有这样问题(但也有例外,比如他被单例bean依赖),因为给每个请求都新创建实例。
但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类)
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

Spring Bean 的生命周期

Spring bean factory 负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。

  1. 初始化之后调用的回调方法。
  2. 销毁之前调用的回调方法。

Spring框架提供了以下四种方式来管理bean的生命周期事件:
InitializingBean和DisposableBean回调接口
针对特殊行为的其他Aware接口
Bean配置文件中的Custom init()方法和destroy()方法
@PostConstruct和@PreDestroy注解方式
使用customInit()和 customDestroy()方法管理bean生命周期的代码样例如下:

  1. <beans>
  2. <bean id="demoBean" class="com.howtodoinjava.task.DemoBean"
  3. init-method="customInit" destroy-method="customDestroy"></bean>
  4. </beans>

被Spring管理的对象叫做Bean。Bean的生成步骤如下:

  1. Spring扫描class得到BeanDefinition
  2. 根据得到的BeanDefinition去生成bean
  3. 初始化 首先根据class推断构造方法

实现InitializingBean接口,覆盖 afeterSetProperties 方法
配置 init 方法

  1. 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象
  2. 填充原始对象中的属性(依赖注入)
  3. BeanPostProcessor 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象
  4. 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例池拿即可

key:beanName,value:对象

  1. 销毁

实现DisposableBean接口

生命周期: https://www.jianshu.com/p/1dec08d290c1
链接

初始化

InitializingBean

InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

Spring加载bean的源码类AbstractAutowiredCapableBeanFactory

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
    //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }

        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    public Object run() throws Exception {
                        //直接调用afterPropertiesSet
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }
                },getAccessControlContext());
            } catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }                
        else {
            //直接调用afterPropertiesSet
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    if (mbd != null) {
        String initMethodName = mbd.getInitMethodName();
        //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

1、Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。
2、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
3、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

BeanFactoryPostProcessor和BeanPostProcessor

Spring容器的扩展点BeanFactoryPostProcessor
可以修改bean的定义(甚至改懒加载配置,class,构造器调用)

BeanPostProcessor:
https://www.jianshu.com/p/a90a3e617ba6
https://blog.csdn.net/caihaijiang/article/details/35552859

使用过程中会有这样的几个问题

1、可不可以在BeanFactoryPostProcessor去创建一个Bean,这样有什么问题?

从技术上来说这样是可以的,但是正常情况下我们不该这样做,这是因为可能会存在该执行的Bean工厂的后置处理器的逻辑没有被应用到这个Bean上。

2、BeanFactoryPostProcessor可以被配置为懒加载吗?

不能配置为懒加载,即使配置了也不会生效。我们将Bean工厂后置处理器配置为懒加载这个行为就没有任何意义

@ImportSelector

https://blog.csdn.net/elim168/article/details/88131614

循环依赖

单例Bean不存在循环依赖问题,

// 相互依赖
@Component
public class A {
    // A中注入了B
    @Autowired
    private B b;
}

@Component
public class B {
    // B中也注入了A
    @Autowired
    private A a;
}
// 依赖自己
@Component
public class A {
    // A中注入了A
    @Autowired
    private A a;
}

spring IoC - 图3
A的Bean在创建过程中,在进行依赖注入之前,先把A的原始Bean放入Map缓存(提早暴露,只要放到缓存了,其他Bean需要时就可以从缓存中拿了),放入缓存后,再进行依赖注入,在后续的生命周期中操作引用

从上面这个分析过程中可以得出,只需要一个缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?

这是难点,基于上面的场景想一个问题:如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。

1、Spring到底是如何解决循环依赖的呢,这里来一波文字的总结:

Spring通过三级缓存解决了循环依赖,
其中一级缓存为单例池(singletonObjects),
二级缓存为早期曝光对象earlySingletonObjects,
三级缓存为早期曝光对象工厂(singletonFactories)。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

2、为啥要用三级缓存,是否可以用二级缓存

在普通的循环依赖的情况下,三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP相关。AOP场景下的getEarlyBeanReference 会拿到一个代理的对象,但是不确定有没有依赖,需不需要用到这个依赖对象,所以先给一个工厂放到三级缓存里。
这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。

属性注入

自动装配
@Autowired:根据属性类型自动装配
@javax.annotation.Resource: 可以根据类型注入,可以根据名称注入
@Qualifier: 根据名称注入和@Autowired一起使用
@Value: 注入普通类型
根据名称
根据类型