参考源

https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click

https://www.bilibili.com/video/BV12Z4y197MU?spm_id_from=333.999.0.0

《Spring源码深度解析(第2版)》

版本

本文章基于 Spring 5.3.15


Spring IOC 主要有两种实现方式:XML注解

Spring 3.0 推出了注解注入,成为了现在的主流,也是官方推荐的。

这里分析注解方式。

AnnotationConfigApplicationContext(AppConfig.class)

AppConfig

package cn.sail.ioc;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class AppConfig { }

UserService

package cn.sail.ioc.service;

import cn.sail.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class UserService { @Autowired
UserDao userDao; public void test() {
System.out.println(userDao);
} }

UserDao

package cn.sail.ioc.dao;

import org.springframework.stereotype.Component;

@Component
public class UserDao { }

使用

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.test();

执行结果:cn.sail.ioc.dao.UserDao@1bd39d3c

接下来开始分析 AnnotationConfigApplicationContext(AppConfig.class)

由于 Spring 源码层级十分复杂,约定如下规则

  • 数字 类名:数字代表该类出现的顺序。
  • 类数字-数字 方法注释:数字代表该方法在类中执行的层级。

1 AnnotationConfigApplicationContext

由于其父类 AbstractApplicationContext 存在静态代码块,先进入父类的静态代码块。

2 AbstractApplicationContext

2-1 静态代码块

进入 ClassPathXmlApplicationContext 的构造方法,会先进入 AbstractApplicationContext 的静态代码块。

static {
/**
* 优先加载上下文关闭事件来防止奇怪的类加载问题
* WebLogic 8.1 在应用程序关闭的时候出现的 BUG
*/
ContextClosedEvent.class.getName();
}

这里是针对 WebLogic 8.1 的特殊处理,与主体逻辑不关,不用过于关注。

1 AnnotationConfigApplicationContext

由于继承了GenericApplicationContext,会先执行父类的构造方法。

3 GenericApplicationContext

public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}

初始化了内部的beanFactoryDefaultListableBeanFactory

1 AnnotationConfigApplicationContext

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 构造
this();
// 注册
register(componentClasses);
// 刷新
refresh();
}

1-1 构造

public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
// 读取被注解了的 Bean
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
/*
定义扫描器
可以用来扫描包或者类,继而转换成 Bean 定义信息
但实际上我们扫描包不是 scanner 这个对象,是 Spring 自己 new 的一个 ClassPathBeanDefinitionScanner
这里的 scanner 仅仅是为了程序员能够在外部调用 AnnotationConfigApplicationContext 对象 scan 方法
*/
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

1-2 读取被注解了的 Bean

AnnotatedBeanDefinitionReader(this)

4 AnnotatedBeanDefinitionReader

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 将处理注解的基础设施类放入
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

4-1 将处理注解的基础设施类放入

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)

5 AnnotationConfigUtils

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
// 将处理注解的基础设施类放入
registerAnnotationConfigProcessors(registry, null);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {

   DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
} Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
} // 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);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
} // 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;
}

1 AnnotationConfigApplicationContext

1-2 定义扫描器

ClassPathBeanDefinitionScanner(this)

6 ClassPathBeanDefinitionScanner

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this(registry, true);
}
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}

1 AnnotationConfigApplicationContext

1-1 注册

register(componentClasses)
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));
// 注册
this.reader.register(componentClasses);
registerComponentClass.end();
}

7 AnnotatedBeanDefinitionReader

public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
// 注册 Bean
registerBean(componentClass);
}
}

7-1 注册 Bean

registerBean(componentClass)
public void registerBean(Class<?> beanClass) {
// 进一步注册 Bean
doRegisterBean(beanClass, null, null, null, null);
}

7-2 进一步注册 Bean

doRegisterBean(beanClass, null, null, null, null)
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
/*
得到 bean 的描述信息
比如 bean 的注解,作用范围,是否懒加载,注入方式等
*/
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
// 被条件注解 @Conditional 注释的 bean 跳过注册
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
} abd.setInstanceSupplier(supplier);
/*
解析 bean 的 Scope
比如是否单例 singleton 还是其他
*/
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
// 生成beanName,默认就是类名小写
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 通过判断注解内容,设置一些公共属性,比如是否懒加载,优先级等
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
} BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

7-3 解析 bean 的 Scope

private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

this.scopeMetadataResolver.resolveScopeMetadata(abd)

8 AnnotationScopeMetadataResolver

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
// 获取 @Scope 注解的值,没有默认为 singleton
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}

8-1 获取 @Scope 注解的值

getString("value")

9 AnnotationAttributes

public String getString(String attributeName) {
// 获取必需属性
return getRequiredAttribute(attributeName, String.class);
}

9-1 获取必需属性

getRequiredAttribute(attributeName, String.class)
private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
Assert.hasText(attributeName, "'attributeName' must not be null or empty");
Object value = get(attributeName);
// 断言属性存在
assertAttributePresence(attributeName, value);
// 断言没有异常
assertNotException(attributeName, value);
if (!expectedType.isInstance(value) && expectedType.isArray() &&
expectedType.getComponentType().isInstance(value)) {
Object array = Array.newInstance(expectedType.getComponentType(), 1);
Array.set(array, 0, value);
value = array;
}
// 断言属性类型
assertAttributeType(attributeName, value, expectedType);
return (T) value;
}

7 AnnotatedBeanDefinitionReader

7-3 设置公共属性

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)

10 AnnotationConfigUtils

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
} if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
} AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}

7 AnnotatedBeanDefinitionReader

7-3 注册 Bean 定义信息

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry)
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
// 使用 beanName 做唯一标识注册
String beanName = definitionHolder.getBeanName();
// 注册 Bean 定义信息
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

11 GenericApplicationContext

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 注册 Bean 定义信息
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}

12 DefaultListableBeanFactory

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"); if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/*
注册前的最后一次校验
这里的校验不同于之前的 XML 文件校验,主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 校验
校验 methodOverrides 是否与工厂方法并存或者 methodOverrides 对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
/*
注册BeanDefinition,就是将BeanDefinition放入一个map中,key是beanName
注册之前,先查下是否被注册过
*/
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 如果对应的 BeanName 已经注册且在配置中配置了 bean 不允许被覆盖,则抛出异常(默认允许覆盖)
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 如果允许 BeanDefinition 的覆盖,那就向 beanDefinitionMap 中再次存一次值,覆盖之前的值
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
/*
检查 bean 的创建过程是否已经开始了
通过判断一个 set 集合是否为空,因为创建过的 bean 都会放到那个 set 中保存
*/
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
/*
如果项目已经运行了
由于 beanDefinitionMap 是一个全局变量,可能存在并发问题,所以要加锁处理
*/
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
// 更新 beanDefinitionNames 的 list
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 更新人工注册的单例集合
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
/*
仍然在启动注册阶段
注册 beanDefinition
*/
this.beanDefinitionMap.put(beanName, beanDefinition);
// 记录 beanName
this.beanDefinitionNames.add(beanName);
// 更新人工注册的单例集合
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
} if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有 beanName 对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}

12-1 更新人工注册的单例集合

removeManualSingletonName(beanName)
private void removeManualSingletonName(String beanName) {
updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName));
}
private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) {
// 判断是否已开始创建 Bean
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
if (condition.test(this.manualSingletonNames)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
action.accept(updatedSingletons);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
if (condition.test(this.manualSingletonNames)) {
action.accept(this.manualSingletonNames);
}
}
}

1 AnnotationConfigApplicationContext

1-1 刷新

refresh()

2 AbstractApplicationContext

public void refresh() throws BeansException, IllegalStateException {
// 同步监视器
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); /*
1 准备刷新的上下文环境。例如对系统属性或者环境变量进行准备及验证
设置容器的启动时间
设置关闭状态为 false
设置活跃状态为 true
获取 Environment 对象,并加载当前系统的属性值到 Environment 对象中并进行验证
准备监听器和事件的集合对象,默认为空的集合
*/
prepareRefresh(); /*
2 初始化 BeanFactory,并进行 XML 文件读取
创建容器对象:DefaultListableBeanFactory
加载 XML 配置文件的属性值到当前工厂中,最重要的就是 BeanDefinition
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /*
3 对 BeanFactory 进行各种功能填充
比如 @Qualifier 与 @Autowired 就是在这一步骤中增加的支持
*/
prepareBeanFactory(beanFactory); try {
/*
4 定义 Bean 工厂的增强器,子类覆盖方法做额外的处理(此处我们自己一般不做任何扩展工作,但是可以查看 web 中的代码是有具体实现的)
*/
postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); /*
5 执行 Bean 工厂的增强器,激活各种 beanFactory 处理器
*/
invokeBeanFactoryPostProcessors(beanFactory); /*
6 注册 Bean 增强器。注册拦截 Bean 创建的 Bean 处理器,这里只是注册,真正的调用是在 getBean 时候
*/
registerBeanPostProcessors(beanFactory);
beanPostProcess.end(); /*
7 为上下文初始化 message 源,即不同语言的消息体,国际化处理
*/
initMessageSource(); /*
8 初始化应用消息广播器,并放入 "applicationEventMulticaster" bean 中
*/
initApplicationEventMulticaster(); /*
9 特定刷新。初始化其他的 bean,留给子类扩展
*/
onRefresh(); /*
10 注册监听器。在所有注册的 bean 中查找 listen bean,注册到消息广播器中
*/
registerListeners(); /*
11 初始化剩下的单实例(非懒加载的)
*/
finishBeanFactoryInitialization(beanFactory); /*
12 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人
*/
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex);
}
// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// 重置active标志
cancelRefresh(ex);
throw ex;
} finally {
/*
13 清空缓存
*/
resetCommonCaches();
contextRefresh.end();
}
}
}

AbstractApplicationContext 中的 refresh() 是整个 IOC 的核心。

后续会对其中的 13 个主要方法做详细解析。

Spring源码 05 IOC 注解方式的更多相关文章

  1. Spring源码 04 IOC XML方式

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  2. spring源码浅析——IOC

    =========================================== 原文链接: spring源码浅析--IOC   转载请注明出处! ======================= ...

  3. Spring源码解析-ioc容器的设计

    Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...

  4. spring源码分析---IOC(1)

    我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...

  5. Spring源码 18 IOC refresh方法13

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  6. Spring源码 06 IOC refresh方法1

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  7. Spring源码 03 IOC原理

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  8. Spring源码 16 IOC refresh方法11

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  9. Spring源码 17 IOC refresh方法12

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

随机推荐

  1. KNN算法推理与实现

    Overview K近邻值算法 KNN (K - Nearest Neighbors) 是一种机器学习中的分类算法:K-NN是一种非参数的惰性学习算法.非参数意味着没有对基础数据分布的假设,即模型结构 ...

  2. centos6搭建mysql

    目前CentOS6.5及一下版本基本上被官方给放弃更新了,但是考虑到忠实粉丝迟迟不肯放手,所以还留了入口但是非常有限 1.搭建mysql 可参照:https://blog.csdn.net/huang ...

  3. GDKOI 2021 Day2 TG 总结

    又是爆炸的一天,炸多了本蒟蒻已经习以为常 但今天比昨天整整高了 40 分!!!!却还是没有 100 今天本蒟蒻本想模仿奆佬的打字速度,结果思路混乱让我无法开始 T1 不是吧怎么是期望 dp ,期望值怎 ...

  4. 如何优化PlantUML流程图(时序图)

    这篇文章用来介绍,如何画出好看的流程图. 1. 选择合适的组件 1.1 plantuml官方提供的组件 1.2 加载图片 1.2.1 加载本地图片 1.2.2 加载网络图片 1.2.3 图片资源 2. ...

  5. 一个ES设置操作引发的“血案”

    背景说明 ES版本 7.1.4 在ES生产环境中增加字段,一直提示Setting index.mapper.dynamic was removed after version 6.0.0错误.但是我只 ...

  6. Linux服务器启动jstatd服务

    Linux服务器启动jstatd服务 1.查找jdk所在目录 2.在jdk的bin目录下创建文件jstatd.all.policy touch jstatd.all.policy 3.写入安全配置 g ...

  7. VMware Workstation 虚拟机详细安装教程

    一.介绍篇 VMware Workstation 16 Pro是VMware(威睿公司)于2021年最新发布的一代虚拟机软件,软件的中文名是"VMware 工作站 16 专业版". ...

  8. MySQL 8.0 新特性梳理汇总

    一 历史版本发布回顾 从上图可以看出,基本遵循 5+3+3 模式 5---GA发布后,5年 就停止通用常规的更新了(功能不再更新了): 3---企业版的,+3年功能不再更新了: 3 ---完全停止更新 ...

  9. UiPath程序设计文档

    1. [RPA之家]添加数据列UiPath.Core.Activities.AddDataColumn 链接: https://pan.baidu.com/s/1RRMw4voqJru-fJSoC3W ...

  10. windows下docker部署报错

    报错信息:Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:8848 -> 0.0.0 ...