BeanFactoryPostProcessor

在前面几个章节,笔者有介绍过BeanFactoryPostProcessor,在spring在解析BeanDefinition之后,正式初始化bean之前,会回调我们编写的BeanFactoryPostProcessor接口,接口会传入beanFactory对象,我们可以新增或修改BeanDefinition。spring初始化bean一个典型的流程,就是根据我们标记在类上的@Component生成一个BeanDefinition,BeanDefinition中包含类名或class对象,然后根据class对象生成实例。如果我们编写两个Service:UserService和OrderService,并在类上标注@Component,再编写一个BeanFactoryPostProcessor接口,在接口中我们拿到UserService的BeanDefinition,并修改class为OrderService,那么我们从spring容器中获取userService这个bean,它的类型是UserService呢还是OrderService呢?来看下面的示例:

package org.example.service;

import org.springframework.stereotype.Component;

@Component
public class OrderService {
} package org.example.service; import org.springframework.stereotype.Component; @Component
public class UserService {
}

  

在Test1BeanFactoryPostProcessor类中,我们获取userService的BeanDefinition,并打印它的class对象,这里应该是UserService,然后我们再设置BeanDefinition的class为OrderService

Test1BeanFactoryPostProcessor.java

package org.example.service;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.stereotype.Component; @Component
public class Test1BeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("userService");
System.out.println("UserService beanDefinition class:" + beanDefinition.getBeanClass());
beanDefinition.setBeanClass(OrderService.class);
}
}

  

MyConfig.java

package org.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan("org.example.service")
public class MyConfig {
}

  

测试用例:

    @Test
public void test01() {
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("userService class:" + ac.getBean("userService").getClass());
}

  

运行结果:

UserService beanDefinition class:class org.example.service.UserService
userService class:class org.example.service.OrderService

  

可以看到,我们从spring容器获取userService对应的bean,但是打印bean的类型却为OrderService。这段代码在实际开发中意义并不大,只是为了揭露spring的实现。

那么笔者这里有个疑问,spring是在何时解析BeanDefinition?何时回调BeanFactoryPostProcessor?何时初始化bean?回顾测试用例test01的两行代码,我们可以确定,前面的解析、回调、初始化一定是在test01里两句代码的某一句完成的,那么究竟是那一句呢?是创建应用上下文,还是打印userService对应的class对象呢?

为了定位上面的问题,我们对UserService和OrderService稍作修改,我们在两个类的构造函数中增加打印:

public class UserService {
public UserService() {
System.out.println("UserService init...");
}
} public class OrderService {
public OrderService() {
System.out.println("OrderService init...");
}
}

  

测试用例:

    @Test
public void test02() {
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
}

  

运行结果:

UserService beanDefinition class:class org.example.service.UserService
OrderService init...
OrderService init...

  

结果打印了两次OrderService构造函数的内容,一次spring根据OrderService对应的BeanDefinition进行初始化,一次是我们修改userService对应的BeanDefinition的class为OrderService,spring根据class进行初始化。而test02只有一行初始化应用上下文的代码,至此我们可以确定,spring的解析BeanDefinition、回调BeanFactoryPostProcessor、初始化bean都在初始化应用上下文完成。当然,spring的应用上下文实现,有:AnnotationConfigApplication、ContextClassPathXmlApplicationContext……等等,但大部分的应用上下文实现都是在初始化的时候完成解析BeanDefinition、回调BeanFactoryPostProcessor、初始化bean这三步。

我们在调用AnnotationConfigApplication的配置类构造函数AnnotationConfigApplicationContext(Class<?>... componentClasses)时,这个函数内部会先调用默认的无参构造方法,初始化reader和scanner两个对象。调用完默认构造方法后,接着调用register(Class<?>... componentClasses)将我们的配置类注册进reader,这一步其实是根据我们的配置类生成BeanDefinition并注册进spring容器。之后调用继承自父类AbstractApplicationContext的refresh()方法。

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;

	public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
} public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
} public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
…… }

  

于是我们进入到AbstractApplicationContext的refresh()方法,这个方法首先在<1>处调用obtainFreshBeanFactory()获取一个beanFactory对象,在<2>、<3>会把beanFactory作为参数传入其他方法,<2>处我们单看方法名invokeBeanFactoryPostProcessors可以知道这里是调用BeanFactoryPostProcessor接口,我们之前编写的BeanFactoryPostProcessor实现类,就是在<2>处进行回调。<3>处单看方法名不太好理解,但如果看注释就可以知道,初始化“剩余且非懒加载”的单例对象,换言之:我们的dao、service、controller都是在这一层完成bean的初始化以及属性注入。这里的“剩余”很有意思,当我们基于spring框架进行开发,大部分的bean都是单例对象,包括我们之前的配置类(MyConfig)、BeanFactoryPostProcessor在spring容器中都会有对应的BeanDefinition和bean,我们知道要调用一个类的方法,首先要有那个类的对象,在<2>处的invokeBeanFactoryPostProcessors可以回调我们编写的BeanFactoryPostProcessor实现类,说明在<2>处就已经进行一部分bean的初始化了,这部分bean就是BeanFactoryPostProcessor的实现类。

public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
……
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
……
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//<1>
……
try {
……
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);//<2>
……
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);//<3>
……
} catch (BeansException ex) {
……
} finally {
……
}
}
}
…… }

  

我们总结一下,当我们调用AnnotationConfigApplicationContext(Class<?>... componentClasses)构造函数时,会先初始化reader和scanner两个对象,然后将配置类注册到reader后,再调用refresh()进行BeanDefinition的解析、单例bean的实例化。

现在,我们来逐行分析AnnotationConfigApplicationContext(Class<?>... componentClasses)这个构造函数,首先这个构造函数会调用默认构造函数,进行reader和scanner的初始化,AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner接受一个BeanDefinitionRegistry接口类型的参数,而AnnotationConfigApplicationContext本身则实现了BeanDefinitionRegistry接口。

	public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

  

从接口名BeanDefinitionRegistry本身我们可以知道,这个接口是用来注册BeanDefinition,而接口所要求的实现,从上至下允许我们注册一个beanName和BeanDefinition、根据beanName移除BeanDefinition,根据beanName获取BeanDefinition、获取所有BeanDefinition对应的beanName,获取BeanDefinition的数量,判断beanName是否已被使用。BeanDefinition之于beanName就如bean之于beanName一样,一个BeanDefinition至少有一个beanName,同理一个bean至少有一个beanName,因为BeanDefinition和bean都可以有别名。

public interface BeanDefinitionRegistry extends AliasRegistry {

	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException; void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; boolean containsBeanDefinition(String beanName); String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String beanName); }

  

AnnotatedBeanDefinitionReader之所以要传入一个BeanDefinitionRegistry进行初始化,是因为在初始化这个对象时,AnnotatedBeanDefinitionReader就会把一些BeanDefinition注册到BeanDefinitionRegistry,由于AnnotationConfigApplicationContext实现了BeanDefinitionRegistry接口,所以AnnotatedBeanDefinitionReader会把BeanDefinition注册到AnnotationConfigApplicationContext。

在调用构造函数AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)创建reader对象时,经过一系列的调用,会来到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source)方法,在这个方法里会创建BeanDefinition并将beanName和BeanDefinition注册到BeanDefinitionRegistry。

public abstract class AnnotationConfigUtils {
……
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
……
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);//<1>
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));//<2>
} if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);//<3>
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));//<4>
} // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);//<5>
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));//<6>
} // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
} return beanDefs;
} private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);//<7>
return new BeanDefinitionHolder(definition, beanName);
}
……
}

  

  • 代码<1>创建一个ConfigurationClassPostProcessor的BeanDefinition,这个类实现了BeanDefinitionRegistryPostProcessor接口,它会根据我们指定的扫描路径去扫描组件。
  • 代码<3>创建一个AutowiredAnnotationBeanPostProcessor的BeanDefinition,这个类主要是用于注入标记了@Autowired和@Inject的属性。
  • 代码<5>创建一个ConfigurationClassPostProcessor的BeanDefinition,这个类主要是用于注入标记了@Resource的属性。
  • 在代码的<2>、<4>、<6>会调用registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName)将beanName和BeanDefinition注册到BeanDefinitionRegistry。
  • 代码<7>处接收到registry、beanName、BeanDefinition会调用registry的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法,对beanName和BeanDefinition进行映射。

上面的registry是我们之前创建的AnnotationConfigApplicationContext对象,如果我们查看AnnotationConfigApplicationContext的registerBeanDefinition方法,会发现这个方法是继承父类GenericApplicationContext,而GenericApplicationContext的registerBeanDefinition实现又是调用DefaultListableBeanFactory类的registerBeanDefinition来完成。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
……
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
……
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException { this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
……
}

  

下面,我们再来看看DefaultListableBeanFactory的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** Whether to allow re-registration of a different definition with the same name. */
private boolean allowBeanDefinitionOverriding = true;
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
/** Names of beans that have already been created at least once. */
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
……
public boolean isAllowBeanDefinitionOverriding() {
return this.allowBeanDefinitionOverriding;
} @Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");//<1>
……
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {//<2>
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
……
this.beanDefinitionMap.put(beanName, beanDefinition);//<3>
}
else {//<4>
if (hasBeanCreationStarted()) {//<5>
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {//<6>
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
……
} protected boolean hasBeanCreationStarted() {
return !this.alreadyCreated.isEmpty();
} protected void markBeanAsCreated(String beanName) {//<7>
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
clearMergedBeanDefinition(beanName);
this.alreadyCreated.add(beanName);
}
}
}
}
……
}

    

  • 在<1>处判断beanName和beanDefinition都不为空,因为这两者只要有其一为空都是不符合常理的,我们不会指定一个bean的名字为null,而beanDefinition如果为null,那就更不会生成bean。
  • 如果beanName已存在对应的beanDefinition,就会进入到<2>处的分支。这里会调用isAllowBeanDefinitionOverriding()方法判断是否允许重载beanDefinition,isAllowBeanDefinitionOverriding()返回allowBeanDefinitionOverriding字段,这个字段默认为true,即允许beanName重复,会接着执行到<3>处,为beanName和新的beanDefinition在beanDefinitionMap建立映射关系。
  • 如果beanName在beanDefinitionMap不存在映射,则会进入到分支<4>处。
  • 到达<4>处的分支后,会先进行hasBeanCreationStarted()的判断,从这个方法名我们可以知道,这个是判断是否存已被创建的bean,而hasBeanCreationStarted()方法的实现也是非常简单的,如果alreadyCreated不为空则返回true,事实上,当spring创建一个bean时,会调用<7>处的markBeanAsCreated(String beanName)方法,将beanName加入到alreadyCreated集合里。
  • 如果spring容器中存在已创建的bean,在注册beanDefinition会进入到<5>处对beanDefinitionMap加上同步锁,然后在建立beanName和beanDefinition在beanDefinitionMap的映射,并创建一个updatedDefinitions对象,将旧的beanDefinitionNames列表和新的beanName加入到updatedDefinitions,再更新beanDefinitionNames指向updatedDefinitions。为什么spring会有这一步操作呢?其实在spring容器初始化完毕,程序开始提供服务,是可以向容器注册beanDefinition获取对应的bean。换句话说,你用spring写了个电商系统,电商系统可以一边向玩家提供下单,一边注册beanDefinition并生成对应的bean,甚至可以多线程注册beanDefinition生成bean,所以才要对beanDefinitionMap加上同步锁,而markBeanAsCreated(String beanName)里加上同步锁也是同样的道理。
  • 如果在注册beanDefinition时spring容器还没有生成bean,则会调用<6>处的方法,这里并没有用synchronized来防止多线程并发注册,这是因为这个时候spring认为容器还没完全启动起来,不会有多个请求并发进行,所以不需要用synchronized,仅仅是简单的在beanDefinitionMap建立映射,将新的beanName加入到beanDefinitionNames。而spring刚启动时,在注册beanDefinition时也是先使用<6>处的代码,直到spring生成一个bean调用markBeanAsCreated(String beanName)方法,之后又注册beanDefinition,spring判断alreadyCreated不为空,才调用<5>处的代码。

现在,我们来总结下,在我们用AnnotationConfigApplicationContext(Class<?>... componentClasses)创建ApplicationContext对象时,AnnotationConfigApplicationContext会先调用无参构造方法,在无参构造方法中进行AnnotatedBeanDefinitionReader的初始化,AnnotatedBeanDefinitionReader的构造参数需要传入一个BeanDefinitionRegistry的实现,BeanDefinitionRegistry是用来注册beanName和beanDefinition,而AnnotationConfigApplicationContext刚好实现了BeanDefinitionRegistry接口。

在创建AnnotatedBeanDefinitionReader对象的时候,会把ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor这些类构造成RootBeanDefinition并注册到BeanDefinitionRegistry。而AnnotatedBeanDefinitionReader所需要的BeanDefinitionRegistry实现,即是我们最开始创建的AnnotationConfigApplicationContext对象,也就是说,之前说的那些BeanDefinition最终会注册到AnnotationConfigApplicationContext。

然而,AnnotationConfigApplicationContext也并不是在自身完成beanName和BeanDefinition的注册的,而是内部生成一个DefaultListableBeanFactory类型的引用,借助DefaultListableBeanFactory来完成beanName和BeanDefinition的注册。DefaultListableBeanFactory在注册beanName和BeanDefinition的时候,会先判断beanName和BeanDefinition是否为空,为空就要报错,如果都不为空,再判断beanName在DefaultListableBeanFactory中是否已存在对应的BeanDefinition,如果存在再判断是否允许重载?默认是允许重载。如果已存在,且允许重载,则重新在DefaultListableBeanFactory的beanDefinitionMap建立映射,如果已存在却不允许重载,则抛出异常。当然,这种重载的情况是极少存在的。

如果beanName在注册时不存在已对应的BeanDefinition,那就要分两步判断了,一种是spring容器已存在bean,另一种是还不存在bean,首先在AnnotatedBeanDefinitionReader内部注册RootBeanDefinition的时候,走的是不存在bean的分支,也就是简单的把beanName和BeanDefinition存到beanDefinitionMap,beanName加入到beanDefinitionNames集合。

如果是spring中已存在bean的情况,为了防止多线程进行BeanDefinition的注册,spring对beanDefinitionMap加上同步锁,在同步代码块中保存beanName和BeanDefinition在beanDefinitionMap上的映射,将beanName加入到beanDefinitionNames。

Spring源码解析之BeanFactoryPostProcessor(一)的更多相关文章

  1. Spring源码解析之BeanFactoryPostProcessor(三)

    在上一章中笔者介绍了refresh()的<1>处是如何获取beanFactory对象,下面我们要来学习refresh()方法的<2>处是如何调用invokeBeanFactor ...

  2. Spring源码解析之BeanFactoryPostProcessor(二)

    上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDef ...

  3. Spring源码解析之ConfigurationClassPostProcessor(二)

    上一个章节,笔者向大家介绍了spring是如何来过滤配置类的,下面我们来看看在过滤出配置类后,spring是如何来解析配置类的.首先过滤出来的配置类会存放在configCandidates列表, 在代 ...

  4. Spring源码解读之BeanFactoryPostProcessor的处理

    前言 前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得.我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPost ...

  5. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  6. Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean

    Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean 七千字长文深刻解读,Spirng中是如何初始化单例bean的,和面试中最常问的Sprin ...

  7. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegistry和SingletonBeanRegistry接口. 这边主要提供了 ...

  8. spring 源码解析

    1. [文件] spring源码.txt ~ 15B     下载(167) ? 1 springн┤┬вио╬Ш: 2. [文件] spring源码分析之AOP.txt ~ 15KB     下载( ...

  9. Spring源码解析——循环依赖的解决方案

    一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...

随机推荐

  1. 二进制K8S集群使用Bootstrap Token 方式增加Node

    TLS Bootstraping:在kubernetes集群中,Node上组件kebelet和kube-proxy都需要与kube-apiserver进行通信,为了增加传输安全性,采用https方式, ...

  2. MinGW与Cygwin的关系与差别

    PART1 共同点 Cygwin / GCC和MinGW都是gcc在WINDOWS下的实现. gcc:它是一款原来只能在Linux系统上使用的开源C语言编译器,后来移植到了Windows操作系统上(以 ...

  3. golang RSA2加密/解密

    $go get github.com/wenzhenxi/gorsa test.go文件的内容 运行: $go run test.go package main import ( "fmt& ...

  4. webpack5文档解析(上)

    webpack5 声明:所有的文章demo都在我的仓库里 webpack5 起步 概念 webpack是用于编译JavaScript模块. 一个文件依赖另一个文件,包括静态资源(图片/css等),都会 ...

  5. monolog 日志

    1 安装 composer require monolog/monolog 2 使用 // 创建日志服务 $logger = new Logger('my_logger'); // 定义一个handl ...

  6. composer 阿里云加速 转

    阿里云 Composer 全量镜像 本镜像与 Packagist 官方实时同步,推荐使用最新的 Composer 版本. 最新版本: 1.10.8 下载地址: https://mirrors.aliy ...

  7. samesite-cookie详解(译文)

    Cookie是便于向网站添加持久化状态的方式之一.随着时间推移,它们的能力得到了扩展和进化,也造成了很多历史遗留问题.为了解决这个问题,浏览器产商(包括Chrome,Firefox,和Edge)改变了 ...

  8. SE第一次作业

    作业一.对软件工程的初步认识 下面是我对于软件工程的认识,结合自己的理解和课上听讲的内容 软件工程=软件+工程?软件工程是否就是简单的软件+工程呢?那么我们先来看下各自的概念. 那么什么叫软件呢,既然 ...

  9. Pytest学习(二) - 断言的使用

    一.前言 学习pytest总会习惯性的和unittest对比使用,自然就断言pytest和unittest也是有些区别的. 二.断言对比 unittest 断言 assertEqual(a, b) # ...

  10. 开源 Open Source

    FREE 开源不等于免费 代表自由 开源 Open Source软件和源代码提供给所有人,自由分发软件和源代码能够修改和创建衍生作品软件分类:商业   收费使用  代码不公开共享  免费用 代码不公开 ...