2.Spring IOC

Spring IOC

1.实现原理详解之IOC容器体系结构

(1)体系结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lGCYLcWb-1686813131978)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220701104547043.png)]

(2)BeanFactory和BeanRegistry

BeanFactory:工厂模式,定义了Bean的基本功能规范

BeanRegistry:提供手工向IOC容器注册Bean的能力(Bean指BeanDefinition)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWWbMKzX-1686813131979)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220623111338068.png)]

  • ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包括某一 Bean 等方法;
  • HierarchicalBeanFactory:父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器; 通过 HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
  • AutowireCapableBeanFactory:定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法;
  • ConfigurableBeanFactory:是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;

(3)BeanDefinition

BeanDefinition:Spring 配置文件中每一个<bean>节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示。它定义了各种Bean对象及其相互的关系

BeanDefinitionReader:BeanDefinition的解析器。用于解析配置文件(resource)

BeanDefinitionParseDelegate:从Document对象中解析出BeanDefinition对象

BeanDefinitionHolder:BeanDefinition的封装类。用来保存BeanDefinition,name,alias等

BeanDefinitionRegistry:将解析后得到的BeanDefinition对象注册到Bean容器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-km5yvS0U-1686813131980)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220623170106242.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1wll7no-1686813131981)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220623170117090.png)]

(4)ApplicationContext

IoC容器的接口类是ApplicationContext,很显然它必然继承BeanFactory对Bean规范(最基本的ioc容器的实现)进行定义。而ApplicationContext表示的是应用的上下文,除了对Bean的管理外,还至少应该包含了

  • 访问资源: 对不同方式的Bean配置(即资源)进行加载。(实现ResourcePatternResolver接口)
  • 国际化: 支持信息源,可以实现国际化。(实现MessageSource接口)
  • 应用事件: 支持应用事件。(实现ApplicationEventPublisher接口)

在考虑ApplicationContext接口的实现时,关键的点在于,不同Bean的配置方式(比如xml,groovy,annotation等)有着不同的资源加载方式,这便衍生除了众多ApplicationContext的实现类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wD3XmPga-1686813131982)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220623171133193.png)]

第一,从类结构设计上看, 围绕着是否需要Refresh容器衍生出两个抽象类:

  • GenericApplicationContext: 是初始化的时候就创建容器,往后的每次refresh都不会更改
  • AbstractRefreshableApplicationContext: AbstractRefreshableApplicationContext及子类的每次refresh都是先清除已有(如果不存在就创建)的容器,然后再重新创建;AbstractRefreshableApplicationContext及子类无法做到GenericApplicationContext混合搭配从不同源头获取bean的定义信息

第二, 从加载的源来看(比如xml,groovy,annotation等), 衍生出众多类型的ApplicationContext, 典型比如:

  • FileSystemXmlApplicationContext: 从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。
  • ClassPathXmlApplicationContext: 从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式
  • AnnotationConfigApplicationContext: 从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式
  • ConfigurableApplicationContext: 扩展于 ApplicationContext,它新增加了两个主要的方法: refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用close()则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利,但作为开发者,我们并不需要过多关心这些方法。

第三, 更进一步理解:

设计者在设计时AnnotationConfigApplicationContext为什么是继承GenericApplicationContext? 因为基于注解的配置,是不太会被运行时修改的,这意味着不需要进行动态Bean配置和刷新容器,所以只需要GenericApplicationContext。

而基于XML这种配置文件,这种文件是容易修改的,需要动态性刷新Bean的支持,所以XML相关的配置必然继承AbstractRefreshableApplicationContext; 且存在多种xml的加载方式(位置不同的设计),所以必然会设计出AbstractXmlApplicationContext, 其中包含对XML配置解析成BeanDefination的过程。

2.实现原理详解之IOC容器初始化过程

(1)如何将Bean从XML配置中解析后放到IoC容器中的?

①初始化入口
 // create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    this(configLocations, true, (ApplicationContext)null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    // 设置Bean资源加载器和环境
    super(parent);

    // 设置配置路径
    this.setConfigLocations(configLocations);

    // 解析配置,初始化容器
    if (refresh) {
        this.refresh();
    }
}
②设置资源加载器和环境

调用父类容器AbstractApplicationContext的构造方法(super(parent)方法)为容器设置好Bean资源加载器

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    // 默认构造函数初始化容器id, name, 状态 以及 资源解析器
    this();

    // 将父容器的Environment合并到当前容器
    this.setParent(parent);
}

通过AbstractApplicationContext默认构造函数初始化容器id, name, 状态 以及 资源解析器

public AbstractApplicationContext() {
    this.logger = LogFactory.getLog(this.getClass());
    this.id = ObjectUtils.identityToString(this);
    this.displayName = ObjectUtils.identityToString(this);
    this.beanFactoryPostProcessors = new ArrayList();
    this.active = new AtomicBoolean();
    this.closed = new AtomicBoolean();
    this.startupShutdownMonitor = new Object();
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.applicationListeners = new LinkedHashSet();
    this.resourcePatternResolver = this.getResourcePatternResolver();
}
// Spring资源加载器
protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

通过AbstractApplicationContext的setParent(parent)方法将父容器的Environment合并到当前容器

public void setParent(@Nullable ApplicationContext parent) {
    this.parent = parent;
    if (parent != null) {
        Environment parentEnvironment = parent.getEnvironment();
        if (parentEnvironment instanceof ConfigurableEnvironment) {
            this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
        }
    }
}

③设置资源路径

在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位

public void setConfigLocations(@Nullable String... locations) {
    if (locations != null) {
        Assert.noNullElements(locations, "Config locations must not be null");
        this.configLocations = new String[locations.length];

        for(int i = 0; i < locations.length; ++i) {
            // 解析配置路径
            this.configLocations[i] = this.resolvePath(locations[i]).trim();
        }
    } else {
        this.configLocations = null;
    }
}
protected String resolvePath(String path) {
    // 从上一步Environment中解析
    return this.getEnvironment().resolveRequiredPlaceholders(path);
}
④解析资源,开始创建ioc容器

https://www.pdai.tech/md/spring/spring-x-framework-ioc-source-2.html#%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84%E4%B8%BB%E4%BD%93%E6%B5%81%E7%A8%8B

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8i4WCRVD-1686813131982)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220630162011150.png)]

⑤总结

现在通过上面的代码,总结一下IOC容器初始化的基本步骤:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CVlYN99t-1686813131983)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220701104636739.png)]

  • 初始化的入口在容器实现中的 refresh()调用来完成

  • 对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下:

    • 通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是 XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的

    • 通过 BeanDefinitionReader来完成定义信息的解析和 Bean 信息的注册, 往往使用的是XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件 - 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 - 他们都是为处理 BeanDefinitin 服务的

    • 容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的.

  • 然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC 容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在ServletContext 中的框架实现。

3.实现原理详解之IOC容器Bean实例化过程

上文,我们看了IOC设计要点和设计结构;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中存放的是Bean的定义即BeanDefinition放到beanDefinitionMap中,本质上是一个ConcurrentHashMap<String, Object>;并且BeanDefinition接口中包含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢?

①BeanFactory的getBean

https://www.pdai.tech/md/spring/spring-x-framework-ioc-source-3.html#beanfactory%E4%B8%ADgetbean%E7%9A%84%E4%B8%BB%E4%BD%93%E6%80%9D%E8%B7%AF

总结getBean实例化过程:doGetBean()方法

  • 解析bean的真正name,如果bean是工厂类,name前缀会加&,需要去掉
  • 无参单例先从缓存中尝试获取
  • 如果bean实例还在创建中,则直接抛出异常
  • 如果bean definition 存在于父的bean工厂中,委派给父Bean工厂获取
  • 标记这个beanName的实例正在创建
  • 确保它的依赖也被初始化(如果这个A-bean实例依赖的B-bean没有初始化,则B执行一遍doGetBean())
  • 真正创建
    • 单例
    • 原型(多实例)
    • 根据bean的scope创建
②重点:Spring如何解决单例\非单例对象的循环依赖

首先我们需要说明,Spring只是解决了单例模式下属性依赖的循环问题;Spring为了解决单例的循环依赖问题,使用了三级缓存。

一级缓存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
二级缓存
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

第一层缓存(singletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象

第二层缓存(earlySingletonObjects):单例对象缓存池,已经实例化但尚未属性赋值,这里的对象是半成品对象

第三层缓存(singletonFactories): ObjectFactory<?>单例工厂的缓存,刚刚被建立放到堆里的对象

如下是获取单例中

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // Spring首先从singletonObjects(一级缓存)中尝试获取
  Object singletonObject = this.singletonObjects.get(beanName);
  // 若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            //若是仍是获取不到而且容许从singletonFactories经过getObject获取,则经过singletonFactory.getObject()(三级缓存)获取
              singletonObject = singletonFactory.getObject();
              //若是获取到了则将singletonObject放入到earlySingletonObjects,也就是将三级缓存提高到二级缓存中
              this.earlySingletonObjects.put(beanName, singletonObject);
              this.singletonFactories.remove(beanName);
          }
        }
    }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。若是获取不到,而且对象正在建立中,就再从二级缓存earlySingletonObjects中获取。若是仍是获取不到且容许singletonFactories经过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,若是获取到了则从三级缓存移动到了二级缓存。

从上面三级缓存的分析,咱们能够知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义以下:

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

在bean建立过程当中,有两处比较重要的匿名内部类实现了该接口。一处是Spring利用其建立bean的时候,另外一处就是:

// Create bean instance.
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            //org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//AbstractAutowireCapableBeanFactory#createBean -> doGetBean -> addSingletonFactory()
addSingletonFactory(beanName, new ObjectFactory<Object>() {
   @Override   public Object getObject() throws BeansException {
      return getEarlyBeanReference(beanName, mbd, bean);
   }});

此处就是解决循环依赖的关键,这段代码发生在createBeanInstance以后,也就是说单例对象此时已经被建立出来的。这个对象已经被生产出来了,虽然还不完美(尚未进行初始化的第二步和第三步),可是已经能被人认出来了(根据对象引用能定位到堆中的对象),因此Spring此时将这个对象提早曝光出来让你们认识,让你们使用。

好比“A对象setter依赖B对象,B对象setter依赖A对象”,A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中,此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),此时A对象继续走create流程,开始尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此可以经过ObjectFactory.getObject拿到A对象(半成品),然后A被放到二级缓存中。B执行完get(A)后B拿到A对象后顺利完成了初始化阶段一、二、三,彻底初始化以后将本身放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、三,最终A也完成了初始化,进去了一级缓存singletonObjects中


https://www.pdai.tech/md/spring/spring-x-framework-ioc-source-3.html#spring%E4%B8%BA%E4%BD%95%E4%B8%8D%E8%83%BD%E8%A7%A3%E5%86%B3%E9%9D%9E%E5%8D%95%E4%BE%8B%E5%B1%9E%E6%80%A7%E4%B9%8B%E5%A4%96%E7%9A%84%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96

分割线:构造器依赖,以下这个情况就会报循环依赖错误了,因为两个对象都在“初始化第一步”依赖对方

@Service
public class TestImpl {

    @Autowired
    private StudentService studentService;
}
@Service
public class StudentServiceImpl implements StudentService {

    private static final Log LOG = LogFactory.getLog(StudentServiceImpl.class);

    private static final String DIAGONAL = "/";
    
    @Autowired
    private TestImpl testImpl;
}



分割线:项目中非单例依赖问题如何解决

  • 生成代理对象产生的循环依赖

这类循环依赖问题解决方法很多,主要有:

  1. 使用@Lazy注解,延迟加载
  2. 使用@DependsOn注解,指定加载先后关系
  3. 修改文件名称,改变循环依赖类的加载顺序
  • 使用@DependsOn产生的循环依赖

这类循环依赖问题要找到@DependsOn注解循环依赖的地方,迫使它不循环依赖就可以解决问题。

  • 多例循环依赖

这类循环依赖问题可以通过把bean改成单例的解决。(多实例Bean是每次调用一次getBean都会执行一次构造方法并且给属性赋值,根本没有三级缓存,因此spring不能解决循环依赖。)

  • 构造器循环依赖

这类循环依赖问题可以通过使用@Lazy注解解决。

@Lazy注解:SpringIoC容器会在启动的时候实例化所有单实例 bean 。如果我们想要实现 Spring 在启动的时候延迟加载 bean,即在首次调用bean的时候再去执行初始化,就可以使用 @Lazy 注解来解决这个问题。

③重点:Spring中bean的生命周期

Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

如果在 <bean> 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用范围为 scope=“prototype”,则spring将该 Bean 创建好后交给调用者,调用者管理该 Bean 后续的生命周期,Spring 不再管理该 Bean。

生命周期图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eF2k2n8g-1686813131983)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220706105453892.png)]

  1. 实例化阶段

    1. 实现BeanFactoryPostProcesor接口,调用postProcessBeanFactory方法【这一步能够对 BeanFactory 中的 BeanDefinitionBeanDefinition 的元数据进行修改。】
    2. 实现InstantiationAwareBeanPostProcesor接口,调用postProcessBeforeInstantiation方法
    3. 调用bean的构造器(⭐如果是有参构造函数,spring会去容器中找有没有bean,没有的话参数值就为null)
      • 没有@Autowired注解:优先选择无参构造函数,没有的话就会报错
    • 一个@Autowired注解:选择用@Autowired注解标注的构造函数
      • 多个@Autowired注解:全部的@Autowired注解都要required=false,否则报错。选择方法形参最多的构造函数
    1. 实现InstantiationAwareBeanPostProcesor接口,调用postProcessAfterInstantiation方法
  2. 属性赋值阶段

    1. 实现InstantiationAwareBeanPostProcesor接口,调用postProcessProperties方法注入bean的属性值
    2. 实现xxxAware接口,调用接口方法
    • 第一类Aware接口

      • 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 或者name值。

      • 如果 Bean 实现了 BeanClassLoaderAware 接口,则 Spring 调用 setBeanClassLoader() 方法传入classLoader的引用。

      • 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

    • 第二类Aware接口

      • 如果 Bean 实现了 EnvironmentAware 接口,则 Spring 调用 setEnvironment() 方法传入当前 Environment 实例的引用。
      • 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
  3. 初始化阶段

    1. 实现BeanPostProcessor接口,调用postProcessBeforeInitialization方法【对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。】
    2. 实现InitializingBean 接口,调用 afterPropertiesSet() 方法。(或者执行有@PostConstruct注解的方法替代,或者xml里配置init-method的方法替代)
    3. 实现BeanPostProcessor接口,调用postProcessAfterInitialization方法【此后,bean就能够被应用程序使用】
      1. 如果是singleton,从spring缓存池里面拿
      2. 如果是prototype,将准备就绪的bean交给使用者
  4. 销毁阶段(这个阶段多实例bean不被spring管理,所以不会调用destory方法)

    1. 实现DiposableBean接口,调用destory方法。(或者执行有@PreDestroy注解的方法替代destory方法)

Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类

  1. Bean自身的方法:包括构造器和一些自己定义的方法
  2. Bean级生命周期方法:包括xxxAware接口的方法和InitializationBean接口、DiposableBean接口的afterPropertiesSet()和destory()方法【可以被@PostConstruct注解和@PreDestory注解替换】。
  3. 容器级生命周期方法:InstantiationAwareBeanPostProcessor接口BeanPostProcessor接口的几个方法。
  4. 工厂级生命周期方法:BeanFactoryPostProcesor接口的方法【包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的子类工厂后置处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。】
④重点:Spring如何推断构造方法创建bean

链接:https://blog.csdn.net/tjd5214/article/details/120066029

总结:

  1. 如果bean只有一个构造函数,那么就用那个构造函数
  2. 如果bean有多个构造函数,会优先寻找用@Autowired标注的构造函数,如果没有就会找无参构造函数如果都没有就会报错no default construct found

stProcessor接口的几个方法。
4. 工厂级生命周期方法:BeanFactoryPostProcesor接口的方法【包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的子类工厂后置处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。】

④重点:Spring如何推断构造方法创建bean

链接:https://blog.csdn.net/tjd5214/article/details/120066029

总结:

  1. 如果bean只有一个构造函数,那么就用那个构造函数
  2. 如果bean有多个构造函数,会优先寻找用@Autowired标注的构造函数,如果没有就会找无参构造函数如果都没有就会报错no default construct found
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值