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;
}
分割线:项目中非单例依赖问题如何解决
- 生成代理对象产生的循环依赖
这类循环依赖问题解决方法很多,主要有:
- 使用@Lazy注解,延迟加载
- 使用@DependsOn注解,指定加载先后关系
- 修改文件名称,改变循环依赖类的加载顺序
- 使用@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)]
-
实例化阶段
- 实现BeanFactoryPostProcesor接口,调用postProcessBeanFactory方法【这一步能够对 BeanFactory 中的 BeanDefinition 或 BeanDefinition 的元数据进行修改。】
- 实现InstantiationAwareBeanPostProcesor接口,调用postProcessBeforeInstantiation方法
调用bean的构造器
(⭐如果是有参构造函数,spring会去容器中找有没有bean,没有的话参数值就为null)- 没有@Autowired注解:优先选择无参构造函数,没有的话就会报错
- 一个@Autowired注解:选择用@Autowired注解标注的构造函数
- 多个@Autowired注解:全部的@Autowired注解都要required=false,否则报错。选择方法形参最多的构造函数
- 实现InstantiationAwareBeanPostProcesor接口,调用postProcessAfterInstantiation方法
-
属性赋值阶段
- 实现InstantiationAwareBeanPostProcesor接口,调用postProcessProperties方法注入bean的属性值
- 实现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 实例的引用。
-
…
-
初始化阶段
- 实现BeanPostProcessor接口,调用postProcessBeforeInitialization方法【对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。】
- 实现InitializingBean 接口,调用 afterPropertiesSet() 方法。(或者执行有@PostConstruct注解的方法替代,或者xml里配置init-method的方法替代)
- 实现BeanPostProcessor接口,调用postProcessAfterInitialization方法【此后,bean就能够被应用程序使用】
- 如果是singleton,从spring缓存池里面拿
- 如果是prototype,将准备就绪的bean交给使用者
-
销毁阶段(这个阶段多实例bean不被spring管理,所以不会调用destory方法)
- 实现DiposableBean接口,调用destory方法。(或者执行有@PreDestroy注解的方法替代destory方法)
Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类
- Bean自身的方法:包括构造器和一些自己定义的方法
- Bean级生命周期方法:包括xxxAware接口的方法和InitializationBean接口、DiposableBean接口的afterPropertiesSet()和destory()方法【可以被@PostConstruct注解和@PreDestory注解替换】。
- 容器级生命周期方法:InstantiationAwareBeanPostProcessor接口BeanPostProcessor接口的几个方法。
- 工厂级生命周期方法:BeanFactoryPostProcesor接口的方法【包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的子类工厂后置处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。】
④重点:Spring如何推断构造方法创建bean
链接:https://blog.csdn.net/tjd5214/article/details/120066029
总结:
- 如果bean只有一个构造函数,那么就用那个构造函数
- 如果bean有多个构造函数,会优先寻找用@Autowired标注的构造函数,如果没有就会找无参构造函数如果都没有就会报错no default construct found
stProcessor接口的几个方法。
4. 工厂级生命周期方法:BeanFactoryPostProcesor接口的方法【包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的子类工厂后置处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。】
④重点:Spring如何推断构造方法创建bean
链接:https://blog.csdn.net/tjd5214/article/details/120066029
总结:
- 如果bean只有一个构造函数,那么就用那个构造函数
- 如果bean有多个构造函数,会优先寻找用@Autowired标注的构造函数,如果没有就会找无参构造函数如果都没有就会报错no default construct found