参考源

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. Vue2-组件通讯传值

    Vue2组件通讯传值 方法 Slot插槽--父向子内容分发,子组件只读 mixin混入--定义公共变量或方法,mixin数据不共享,组件中mixin实例互不影响 provide+inject--依赖注 ...

  2. Oracle数据库丢失表排查思路

    Oracle数据库丢失表排查思路 说明:由于系统采用ID取模分表法进行Oracle数据存储,某日发现Oracle数据库中缺少对应的几张业务数据表,遂进行相关问题查询,简单记录一下排查思路: 由于我们代 ...

  3. JAVA - 类的加载过程

    JAVA - 类的加载过程 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象 ...

  4. Java注解和反射

    1.注解(Annotation) 1.1.什么是注解(Annotation) 注解不是程序本身,可以在程序编译.类加载和运行时被读取,并执行相应的处理.注解的格式为"@注释名(参数值)&qu ...

  5. SAP 复制Client

    原文链接:https://fenginfo.com/102.html 枫竹丹青 SCCL 复制客户端 进入了客户端复制主界面,首先选择参数文件(Selected Profile),虽然此条目为灰色的但 ...

  6. openssl客户端编程:一个不起眼的函数导致的SSL会话失败问题

    我们目前大部分使用的openssl库还是基于TLS1.2协议的1.0.2版本系列,如果要支持更高的TLS1.3协议,就必须使用openssl的1.1.1版本或3.0版本.升级openssl库有可能会导 ...

  7. arcgis创建postgre企业级数据库

    什么是企业级地理数据库? 企业级地理数据库(ArcSD Enterprise,sde)是和 arcGIS 套件集成程度最高的地理数据库:创建时需要用到安装 arcGIS Server 时的 [ecp ...

  8. JS中通过id或者class获取文本内容

    一.JS通过id获取文本内容 二.JS通过class获取文本内容

  9. Ubuntu14.04.6配置阿里源

    Ubuntu14.04.6配置阿里源 这两天上手 Ubuntu 系统,因为公司用的是 14.04.6 版本,所以有了一些踩坑记录. 起因是安装完系统我需要安装一个搜狗输入法,过程得安装 fcitx,需 ...

  10. JDBC:获取自增长键值的序号

    1.改变的地方  实践: package com.dgd.test; import java.io.FileInputStream; import java.io.FileNotFoundExcept ...