死磕Spring之IoC篇 - @Autowired 等注解的实现原理
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
开始阅读这一系列文章之前,建议先查看《深入了解 Spring IoC(面试题)》这一篇文章
该系列其他文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》
@Autowired 等注解的实现原理
在上一篇《Bean 的属性填充阶段》文章中讲到,在创建一个 Bean 的实例对象后,会对这个 Bean 进行属性填充。在属性填充的过程中,获取到已定义的属性值,然后会通过 InstantiationAwareBeanPostProcessor 对该属性值进行处理,最后通过反射机制将属性值设置到这个 Bean 中。在 Spring 内部有以下两个 InstantiationAwareBeanPostProcessor 处理器:
- AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 注解标注的属性,获取对应属性值
- CommonAnnotationBeanPostProcessor,会解析 @Resource 注解标注的属性,获取对应的属性值
本文将会分析这两个处理器的实现,以及涉及到的相关对象
这两个处理器在哪被注册?
在前面的《解析自定义标签(XML 文件)》 和 《BeanDefinition 的解析过程(面向注解)》文章中可以知道,在 XML 文件中的 <context:component-scan />
标签的处理过程中,会底层借助于 ClassPathBeanDefinitionScanner
扫描器,去扫描指定路径下符合条件(@Component 注解)的 BeanDefinition 们,关于 @ComponentScan 注解的解析也是借助于这个扫描器实现的。扫描过程如下:
// ClassPathBeanDefinitionScanner.java
public int scan(String... basePackages) {
// <1> 获取扫描前的 BeanDefinition 数量
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// <2> 进行扫描,将过滤出来的所有的 .class 文件生成对应的 BeanDefinition 并注册
doScan(basePackages);
// Register annotation config processors, if necessary.
// <3> 如果 `includeAnnotationConfig` 为 `true`(默认),则注册几个关于注解的 PostProcessor 处理器(关键)
// 在其他地方也会注册,内部会进行判断,已注册的处理器不会再注册
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// <4> 返回本次扫描注册的 BeanDefinition 数量
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
在第 <3>
步会调用 AnnotationConfigUtils 的 registerAnnotationConfigProcessors(BeanDefinitionRegistry)
方法,如下:
// AnnotationConfigUtils.java
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
/**
* Register all relevant annotation post processors in the given registry.
* @param registry the registry to operate on
* @param source the configuration source element (already extracted)
* that this registration was triggered from. May be {@code null}.
* @return a Set of BeanDefinitionHolders, containing all bean definitions
* that have actually been registered by this call
*/
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);
// 处理 Spring 应用上下文中的配置类
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));
}
// 处理 @Autowired 以及 @Value 注解
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));
}
// (条件激活)处理 JSR-250 注解 @Resource,如 @PostConstruct、@PreDestroy 等
// 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));
}
// Processor 对象(条件激活)处理 JPA 注解场景
// 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));
}
// 处理标注 @EventListener 的 Spring 事件监听方法
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));
}
// 用于 @EventListener 标注的事件监听方法构建成 ApplicationListener 对象
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;
}
在这个方法中可以看到会注册 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 两个处理器,然后在 Spring 应用上下文刷新阶段会将其初始化并添加至 AbstractBeanFactory 的 beanPostProcessors
集合中,那么接下来我们先来分析这两个处理器
回顾 Bean 的创建过程
第一步:回到《Bean 的创建过程》文章中的“对 RootBeanDefinition 加工处理”小节,会调用这个方法:
// AbstractAutowireCapableBeanFactory.java
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
调用所有 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition 方法对 RootBeanDefinition 进行加工处理,例如:
- AutowiredAnnotationBeanPostProcessor,会先解析出
@Autowired
和@Value
注解标注的属性的注入元信息,后续进行依赖注入 - CommonAnnotationBeanPostProcessor,会先解析出
@Resource
注解标注的属性的注入元信息,后续进行依赖注入,它也会找到@PostConstruct
和@PreDestroy
注解标注的方法,并构建一个 LifecycleMetadata 对象,用于后续生命周期中的初始化和销毁
第二步:回到《Bean 的创建过程》文章中的“属性填充”小节,该过程会进行下面的处理:
// <5> 通过 InstantiationAwareBeanPostProcessor 处理器(如果有)对 `pvs` 进行处理
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
// <5.1> 遍历所有的 BeanPostProcessor
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// 如果为 InstantiationAwareBeanPostProcessor 类型
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// <5.2> 调用处理器的 `postProcessProperties(...)` 方法,对 `pvs` 进行后置处理
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// <5.3> 如果上一步的处理结果为空,可能是新版本导致的(Spring 5.1 之前没有上面这个方法),则需要兼容老版本
if (pvsToUse == null) {
// <5.3.1> 找到这个 Bean 的所有 `java.beans.PropertyDescriptor` 属性描述器(包含这个属性的所有信息)
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// <5.3.2> 调用处理器的 `postProcessPropertyValues(...)` 方法,对 `pvs` 进行后置处理
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
// <5.3.3> 如果处理后的 PropertyValues 对象为空,直接 `return`,则不会调用后面的 InstantiationAwareBeanPostProcessor 处理器,也不会进行接下来的属性填充
if (pvsToUse == null) {
return;
}
}
// <5.4> 将处理后的 `pvsToUse` 复制给 `pvs`
pvs = pvsToUse;
}
}
}
这里不会调用所有 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法对 pvs
(MutablePropertyValues)属性值对象进行处理,例如:
- AutowiredAnnotationBeanPostProcessor,会根据前面解析出来的
@Autowired
和@Value
注解标注的属性的注入元信息,进行依赖注入 - CommonAnnotationBeanPostProcessor,会根据前面解析出来的
@Resource
注解标注的属性的注入元信息,进行依赖注入
可以看到@Autowired
、@Value
和 @Resource
注解的实现就是基于这两个处理器实现的,接下来我们来看看这两个处理器的具体实现
AutowiredAnnotationBeanPostProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
,主要处理 @Autowired
和 @Value
注解进行依赖注入
体系结构
可以看到 AutowiredAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 和 InstantiationAwareBeanPostProcessor 两个接口
构造方法
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
/**
* 保存需要处理的注解
*/
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
private String requiredParameterName = "required";
private boolean requiredParameterValue = true;
private int order = Ordered.LOWEST_PRECEDENCE - 2;
@Nullable
private ConfigurableListableBeanFactory beanFactory;
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
/**
* 缓存需要注入的字段元信息
*/
private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
/**
* Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's
* standard {@link Autowired @Autowired} annotation.
* <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
* if available.
*/
@SuppressWarnings("unchecked")
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
}
可以看到会添加 @Autowired
和 @Value
两个注解,如果存在 JSR-330 的 javax.inject.Inject
注解,也是支持的
postProcessMergedBeanDefinition 方法
postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
方法,找到 @Autowired
和 @Value
注解标注的字段(或方法)的元信息,如下:
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 找到这个 Bean 所有需要注入的属性(@Autowired 或者 @Value 注解)
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
直接调用 findAutowiringMetadata(...)
方法获取这个 Bean 的注入元信息对象
1. findAutowiringMetadata 方法
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
// 生成一个缓存 Key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// 先尝试从缓存中获取
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 是否需要刷新,也就是判断缓存是否命中
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 加锁,再判断一次
if (metadata != null) {
metadata.clear(pvs);
}
// 构建一个需要注入的元信息对象
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
首先尝试从缓存中获取这个 Bean 对应的注入元信息对象,没有找到的话则调用 buildAutowiringMetadata(final Class<?> clazz)
构建一个,然后再放入缓存中
2. buildAutowiringMetadata 方法
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
// <1> 创建 `currElements` 集合,用于保存 @Autowired、@Value 注解标注的字段
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// <2> 遍历这个 Class 对象的所有字段
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// <2.1> 找到该字段的 @Autowired 或者 @Value 注解,返回 `ann` 对象,没有的话返回空对象,则直接跳过不进行下面的操作
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
// <2.2> 进行过滤,static 修饰的字段不进行注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// <2.3> 获取注解中的 `required` 配置
boolean required = determineRequiredStatus(ann);
// <2.4> 根据该字段和 `required` 构建一个 AutowiredFieldElement 对象,添加至 `currElements`
currElements.add(new AutowiredFieldElement(field, required));
}
});
// <3> 遍历这个 Class 对象的所有方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// <3.1> 尝试找到这个方法的桥接方法,没有的话就是本身这个方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// <3.2> 如果是桥接方法则直接跳过
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// <3.3> 找到该方法的 @Autowired 或者 @Value 注解,返回 `ann` 对象,没有的话返回空对象,则直接跳过不进行下面的操作
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// <3.4> 进行过滤,static 修饰的方法不进行注入
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
// <3.5> 获取注解中的 `required` 配置
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// <3.6> 构建一个 AutowiredMethodElement 对象,添加至 `currElements`
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
// <4> 找到父类,循环遍历
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// <5> 根据从这个 Bean 解析出来的所有 InjectedElement 对象生成一个 InjectionMetadata 注入元信息对象,并返回
return new InjectionMetadata(clazz, elements);
}
过程如下:
- 创建
currElements
集合,用于保存@Autowired
、@Value
注解标注的字段 - 遍历这个 Class 对象的所有字段
- 找到该字段的
@Autowired
或者@Value
注解,返回ann
对象,没有的话返回空对象,则直接跳过不进行下面的操作 - 进行过滤,static 修饰的字段不进行注入
- 获取注解中的
required
配置 - 根据该字段和
required
构建一个 AutowiredFieldElement 对象,添加至currElements
- 找到该字段的
- 遍历这个 Class 对象的所有方法
- 尝试找到这个方法的桥接方法,没有的话就是本身这个方法
- 如果是桥接方法则直接跳过
- 找到该方法的
@Autowired
或者@Value
注解,返回ann
对象,没有的话返回空对象,则直接跳过不进行下面的操作 - 进行过滤,static 修饰的方法不进行注入
- 获取注解中的
required
配置 - 构建一个 AutowiredMethodElement 对象,添加至
currElements
- 找到父类,循环遍历
- 根据从这个 Bean 解析出来的所有 InjectedElement 对象生成一个 InjectionMetadata 注入元信息对象,并返回
整个过程很简单,就是解析出所有 @Autowired
或者 @Value
注解标注的方法或者字段,然后构建一个 InjectionMetadata 注入元信息对象
postProcessProperties 方法
postProcessProperties(PropertyValues pvs, Object bean, String beanName)
方法,根据 @Autowired
和 @Value
注解标注的字段(或方法)的元信息进行依赖注入,如下:
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 找到这个 Bean 的注入元信息对象
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 进行注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
先找到这个 Bean 的注入元信息对象,上面已经讲过了,然后调用其 inject(...)
方法,这里先来看到 InjectionMetadata
这个对象
InjectionMetadata 注入元信息对象
org.springframework.beans.factory.annotation.InjectionMetadata
,某个 Bean 的注入元信息对象
public class InjectionMetadata {
private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
private final Class<?> targetClass;
/**
* 需要注入的字段(或方法)的元信息
*/
private final Collection<InjectedElement> injectedElements;
@Nullable
private volatile Set<InjectedElement> checkedElements;
public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
}
可以看到注入方法非常简单,就是遍历所有的 InjectedElement 对象,调用他们的 inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)
方法
AutowiredFieldElement
AutowiredAnnotationBeanPostProcessor 的私有内部类,注入字段对象,如下:
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
/** 是否必须 */
private final boolean required;
/** 是否缓存起来了 */
private volatile boolean cached = false;
/** 缓存的对象 */
@Nullable
private volatile Object cachedFieldValue;
public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// <1> 获取 `field` 字段
Field field = (Field) this.member;
Object value;
// <2> 如果进行缓存了,则尝试从缓存中获取
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
// <3> 否则,开始进行解析
else {
// <3.1> 创建一个依赖注入描述器 `desc`
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
/**
* <3.2> 通过 {@link org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency} 方法
* 找到这个字段对应的 Bean(们)
*/
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
// <3.3> 和缓存相关,如果有必要则将本次找到的注入对象缓存起来,避免下次再进行解析
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
// <4> 如果获取到该字段对应的对象,则进行属性赋值(依赖注入)
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
直接看到 inject(...)
方法,注入的过程如下:
- 获取
field
字段 - 如果进行缓存了,则尝试从缓存中获取
- 否则,开始进行解析
- 创建一个依赖注入描述器
desc
- 【核心】通过
DefaultListableBeanFactory#resolveDependency(...)
方法,找到这个字段对应的 Bean(们) - 和缓存相关,如果有必要则将本次找到的注入对象缓存起来,避免下次再进行解析
- 创建一个依赖注入描述器
- 如果获取到该字段对应的对象,则进行属性赋值(依赖注入),底层就是通过反射机制为该字段赋值
可以看到整个的核心在于通过 DefaultListableBeanFactory#resolveDependency(...)
方法找到字段对应的 Bean,这里也许是一个集合对象,所以也可能找到的是多个 Bean,该方法在后面进行分析
AutowiredMethodElement
AutowiredAnnotationBeanPostProcessor 的私有内部类,注入方法对象,如下:
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
/** 是否必须 */
private final boolean required;
/** 是否缓存起来了 */
private volatile boolean cached = false;
/** 缓存的方法参数对象 */
@Nullable
private volatile Object[] cachedMethodArguments;
public AutowiredMethodElement(Method method, boolean required, @Nullable PropertyDescriptor pd) {
super(method, pd);
this.required = required;
}
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (checkPropertySkipping(pvs)) {
return;
}
// <1> 获取 `method` 方法
Method method = (Method) this.member;
// <2> 如果进行缓存了,则尝试从缓存中获取方法参数对象
Object[] arguments;
if (this.cached) {
// Shortcut for avoiding synchronization...
arguments = resolveCachedArguments(beanName);
}
// <3> 否则,开始进行解析
else {
// <3.1> 获取方法的参数类型集合 `paramTypes`,根据参数位置确定参数
Class<?>[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
// <3.2> 构建一个依赖注入描述器数组 `descriptors`,用于保存后续创建的对象
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
Set<String> autowiredBeans = new LinkedHashSet<>(paramTypes.length);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
// <3.3> 根据参数顺序遍历该方法的参数
for (int i = 0; i < arguments.length; i++) {
// <3.3.1> 为第 `i` 个方法参数创建一个 MethodParameter 对象
MethodParameter methodParam = new MethodParameter(method, i);
// <3.3.2> 创建依赖描述器 `currDesc`,并添加至 `descriptors` 数组
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
/**
* <3.3.3> 通过 {@link org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency} 方法
* 找到这个方法参数对应的 Bean(们)
*/
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
// <3.4> 和缓存相关,如果有必要则将本次找到的方法参数对象缓存起来,避免下次再进行解析
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
Object[] cachedMethodArguments = new Object[paramTypes.length];
System.arraycopy(descriptors, 0, cachedMethodArguments, 0, arguments.length);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == paramTypes.length) {
Iterator<String> it = autowiredBeans.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
}
// <4> 如果找到该方法的参数(们),则进行属性赋值(依赖注入)
if (arguments != null) {
try {
ReflectionUtils.makeAccessible(method);
// 通过反射机制调用该方法
method.invoke(bean, arguments);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}
直接看到 inject(...)
方法,注入的过程如下:
- 获取
method
方法 - 如果进行缓存了,则尝试从缓存中获取方法参数对象
- 否则,开始进行解析
- 获取方法的参数类型集合
paramTypes
,根据参数位置确定参数 - 构建一个依赖注入描述器数组
descriptors
,用于保存后续创建的对象 - 根据参数顺序遍历该方法的参数
- 为第
i
个方法参数创建一个 MethodParameter 对象 - 创建依赖描述器
currDesc
,并添加至descriptors
数组 - 【核心】通过
DefaultListableBeanFactory#resolveDependency(...)
方法,找到这个方法参数对应的 Bean(们) - 和缓存相关,如果有必要则将本次找到的方法参数对象缓存起来,避免下次再进行解析
- 为第
- 获取方法的参数类型集合
- 如果找到该方法的参数(们),则进行属性赋值(依赖注入),底层就是通过反射机制调用该方法
可以看到整个的核心也是通过 DefaultListableBeanFactory#resolveDependency(...)
方法找到方法参数对应的 Bean,该方法在后面进行分析
CommonAnnotationBeanPostProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
,主要处理 @Resource
注解进行依赖注入,以及 @PostConstruct
和 @PreDestroy
生命周期注解的处理
体系结构
可以看到 CommonAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor 和 InstantiationAwareBeanPostProcessor 两个接口,还实现了 DestructionAwareBeanPostProcessor 接口,用于生命周期中的初始化和销毁的处理
构造方法
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
@Nullable
private static Class<? extends Annotation> webServiceRefClass;
@Nullable
private static Class<? extends Annotation> ejbRefClass;
static {
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
webServiceRefClass = clazz;
}
catch (ClassNotFoundException ex) {
webServiceRefClass = null;
}
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
ejbRefClass = clazz;
}
catch (ClassNotFoundException ex) {
ejbRefClass = null;
}
}
/**
* Create a new CommonAnnotationBeanPostProcessor,
* with the init and destroy annotation types set to
* {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
* respectively.
*/
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
}
public class InitDestroyAnnotationBeanPostProcessor
implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
protected transient Log logger = LogFactory.getLog(getClass());
/**
* 初始化注解,默认为 @PostConstruct
*/
@Nullable
private Class<? extends Annotation> initAnnotationType;
/**
* 销毁注解,默认为 @PreDestroy
*/
@Nullable
private Class<? extends Annotation> destroyAnnotationType;
private int order = Ordered.LOWEST_PRECEDENCE;
@Nullable
private final transient Map<Class<?>, LifecycleMetadata> lifecycleMetadataCache = new ConcurrentHashMap<>(256);
public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
this.initAnnotationType = initAnnotationType;
}
public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
this.destroyAnnotationType = destroyAnnotationType;
}
}
可以看到会设置初始化注解为 @PostConstruct
,销毁注解为 @PreDestroy
,这两个注解都是 JSR-250 注解;另外如果存在 javax.xml.ws.WebServiceRef
和 javax.ejb.EJB
注解也是会进行设置的
postProcessMergedBeanDefinition 方法
postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
方法,找到 @PostConstruct
和 @PreDestroy
注解标注的方法,并构建 LifecycleMetadata 对象,找到 @Resource
注解标注的字段(或方法)的元信息,如下:
// CommonAnnotationBeanPostProcessor.java
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 先调用父类的方法,找到 @PostConstruct 和 @PreDestroy 注解标注的方法,并构建 LifecycleMetadata 对象
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 找到 @Resource 注解标注的字段(或方法),构建一个 InjectionMetadata 对象,用于后续的属性注入
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
整个的过程原理和 AutowiredAnnotationBeanPostProcessor 差不多,先从缓存中获取,未命中则调用对应的方法进行构建,下面先来看看父类中的方法
buildLifecycleMetadata 方法
// InitDestroyAnnotationBeanPostProcessor.java
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}
整个过程比较简单,找到这个 Bean 中 @PostConstruct
和 @PreDestroy
注解标注的方法,然后构建一个 LifecycleMetadata 生命周期元信息对象
buildResourceMetadata 方法
// CommonAnnotationBeanPostProcessor.java
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
整个过程也比较简单,解析出这个 Bean 带有 @Resource
注解的所有字段(或方法),构建成对应的 ResourceElement 对象,然后再构建成一个 InjectionMetadata 注入元信息对象
postProcessProperties 方法
postProcessProperties(PropertyValues pvs, Object bean, String beanName)
方法,根据 @Resource
注解标注的字段(或方法)的元信息进行依赖注入,如下:
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
// 进行注入
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
先找到这个 Bean 的注入元信息对象,上面已经讲过了,然后调用其 inject(...)
方法,该对象上面已经讲过了,实际就是调用其内部 InjectedElement 的 inject(...)
方法
postProcessBeforeInitialization 方法
初始化 Bean 的时候会先执行 @PostConstruct
标注的初始化方法
// InitDestroyAnnotationBeanPostProcessor.java
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 找到 @PostConstruct 和 @PreDestroy 注解标注的方法们所对应的 LifecycleMetadata 对象
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 执行 @PostConstruct 标注的初始化方法
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
// InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate =
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
for (LifecycleElement element : initMethodsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
postProcessBeforeDestruction 方法
销毁 Bean 的时候先执行 @PreDestroy
注解标注的销毁方法
// InitDestroyAnnotationBeanPostProcessor.java
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
// 找到 @PostConstruct 和 @PreDestroy 注解标注的方法们所对应的 LifecycleMetadata 对象
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 执行 @PreDestroy 标注的销毁方法
metadata.invokeDestroyMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
String msg = "Destroy method on bean with name '" + beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
}
}
// InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;
Collection<LifecycleElement> destroyMethodsToUse =
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);
if (!destroyMethodsToUse.isEmpty()) {
for (LifecycleElement element : destroyMethodsToUse) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
ResourceElement
CommonAnnotationBeanPostProcessor 的私有内部类,@Resource 注入字段(或方法)对象
构造方法
protected abstract class LookupElement extends InjectionMetadata.InjectedElement {
/** Bean 的名称 */
protected String name = "";
/** 是否为默认的名称(通过注解定义的) */
protected boolean isDefaultName = false;
/** Bean 的类型 */
protected Class<?> lookupType = Object.class;
@Nullable
protected String mappedName;
public LookupElement(Member member, @Nullable PropertyDescriptor pd) {
super(member, pd);
}
public final DependencyDescriptor getDependencyDescriptor() {
if (this.isField) {
return new LookupDependencyDescriptor((Field) this.member, this.lookupType);
}
else {
return new LookupDependencyDescriptor((Method) this.member, this.lookupType);
}
}
}
private class ResourceElement extends LookupElement {
/** 是否延迟加载 */
private final boolean lazyLookup;
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
Resource resource = ae.getAnnotation(Resource.class);
String resourceName = resource.name();
Class<?> resourceType = resource.type();
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName = this.member.getName();
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
resourceName = Introspector.decapitalize(resourceName.substring(3));
}
}
else if (embeddedValueResolver != null) {
resourceName = embeddedValueResolver.resolveStringValue(resourceName);
}
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.name = (resourceName != null ? resourceName : "");
this.lookupType = resourceType;
String lookupValue = resource.lookup();
this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
Lazy lazy = ae.getAnnotation(Lazy.class);
this.lazyLookup = (lazy != null && lazy.value());
}
}
ResourceElement 的构造方法会通过 @Resource
注解和该字段(或方法)解析出基本信息
可以看到还继承了 InjectionMetadata 的静态内部类 InjectedElement,我们先来看到这个类的 inject(...)
方法
inject 方法
public abstract static class InjectedElement {
protected final Member member;
protected final boolean isField;
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
} else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
@Nullable
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return null;
}
}
不管是字段还是方法,底层都是通过反射机制进行赋值或者调用,都会调用 getResourceToInject(...)
方法获取到字段值或者方法参数
getResourceToInject 方法
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
如果是延迟加载,则调用 buildLazyResourceProxy(...)
方法返回一个代理对象,如下:
protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return element.lookupType;
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
return getResource(element, requestingBeanName);
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
if (element.lookupType.isInterface()) {
pf.addInterface(element.lookupType);
}
ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
return pf.getProxy(classLoader);
}
否则,调用 getResource(...)
方法获取注入对象
getResource 方法
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
if (StringUtils.hasLength(element.mappedName)) {
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
if (this.alwaysUseJndiLookup) {
return this.jndiFactory.getBean(element.name, element.lookupType);
}
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the 'resourceFactory' property");
}
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
前面的判断忽略掉,直接看到最后会调用 autowireResource(...)
方法,并返回注入信息
autowireResource 方法
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set<String> autowiredBeanNames;
String name = element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
else {
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}
@Resource
注解相比于 @Autowired
注解的处理更加复杂点,不过我们这里可以看到也会调用 DefaultListableBeanFactory#resolveDependency(...)
方法,找到对应的注入对象,该方法在后面进行分析
1. resolveDependency 处理依赖方法
resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, TypeConverter typeConverter)
方法,找到对应的依赖 Bean,该方法在《Bean 的创建过程》中也提到了,获取 Bean 的实例对象时,构造器注入的参数也是通过该方法获取的,本文的依赖注入底层也是通过该方法实现的,这里我们对该方法一探究竟
// DefaultListableBeanFactory.java
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// <1> 设置参数名称探测器,例如通过它获取方法参数的名称
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
// <2> 如果依赖类型为 Optional 类型
if (Optional.class == descriptor.getDependencyType()) {
// 调用 `createOptionalDependency(...)` 方法,先将 `descriptor` 注入表述器封装成 NestedDependencyDescriptor 对象
// 底层处理和下面的 `5.2` 相同
return createOptionalDependency(descriptor, requestingBeanName);
}
// <3> 否则,如果依赖类型为 ObjectFactory 或 ObjectProvider 类型
else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) {
// 返回一个 DependencyObjectProvider 私有内部类对象,并没有获取到实例的 Bean,需要调用其 getObject() 方法获取目标对象
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
// <4> 否则,如果依赖类型为 javax.inject.Provider 类型
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
// 返回一个 Jsr330Provider 私有内部类对象,该对象也继承 DependencyObjectProvider
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
// <5> 否则,通用的处理逻辑
else {
// <5.1> 先通过 AutowireCandidateResolver 尝试获取一个代理对象,延迟依赖注入则会返回一个代理对象
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
// <5.2> 如果上面没有返回代理对象,则进行处理,调用 `doResolveDependency(...)` 方法
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
过程如下:
- 设置参数名称探测器,例如通过它获取方法参数的名称
- 如果依赖类型为 Optional 类型,则调用
createOptionalDependency(...)
方法,先将descriptor
注入表述器封装成 NestedDependencyDescriptor 对象,底层处理和下面的5.2
相同 - 否则,如果依赖类型为 ObjectFactory 或 ObjectProvider 类型,直接返回一个
DependencyObjectProvider
私有内部类对象,并没有获取到实例的 Bean,需要调用其 getObject() 方法获取目标对象 - 否则,如果依赖类型为 javax.inject.Provider 类型,直接返回一个
Jsr330Provider
私有内部类对象,该对象也继承 DependencyObjectProvider - 否则,通用的处理逻辑
- 先通过 AutowireCandidateResolver 尝试获取一个代理对象,延迟依赖注入则会返回一个代理对象
- 如果上面没有返回代理对象,则进行处理,调用
doResolveDependency(...)
方法
我们需要关注的是上面的第 5.2
步所调用 doResolveDependency(...)
方法,这一步是底层实现
2. doResolveDependency 底层处理依赖方法
// DefaultListableBeanFactory.java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 设置当前线程的注入点,并返回上次的注入点,属于嵌套注入的一个保护点
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// <1> 针对给定的工厂给定一个快捷实现的方式,暂时忽略
// 例如考虑一些预先解析的信息,在进入所有 Bean 的常规类型匹配算法之前,解析算法将首先尝试通过此方法解析快捷方式
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
// 返回快捷的解析信息
return shortcut;
}
// 依赖的类型
Class<?> type = descriptor.getDependencyType();
// <2> 获取注解中的 value 对应的值,例如 @Value、@Qualifier 注解配置的 value 属性值,注意 @Autowired 没有 value 属性配置
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
// <2.1> 解析注解中的 value,因为可能是占位符,需要获取到相应的数据
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
// <2.2> 进行类型转换,并返回
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
// <3> 解析复合的依赖对象(Array、Collection、Map 类型),获取该属性元素类型的 Bean 们
// 底层和第 `4` 原理一样,这里会将 `descriptor` 封装成 MultiElementDescriptor 类型
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// <4> 查找与类型相匹配的 Bean 们
// 返回结果:key -> beanName;value -> 对应的 Bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// <5> 如果一个都没找到
if (matchingBeans.isEmpty()) {
// <5.1> 如果 @Autowired 配置的 required 为 true,表示必须,则抛出异常
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
// <5.2> 否则,返回一个空对象
return null;
}
String autowiredBeanName;
Object instanceCandidate;
// <6> 如果匹配的 Bean 有多个,则需要找出最优先的那个
if (matchingBeans.size() > 1) {
// <6.1> 找到最匹配的那个 Bean,通过 @Primary 或者 @Priority 来决定,或者通过名称决定
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
// <6.2> 如果没有找到最匹配的 Bean,则抛出 NoUniqueBeanDefinitionException 异常
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
// <6.3> 获取到最匹配的 Bean,传值引用给 `instanceCandidate`
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
// <7> 否则,只有一个 Bean,则直接使用其作为最匹配的 Bean
else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
// <8> 将依赖注入的 Bean 的名称添加至方法入参 `autowiredBeanNames` 集合,里面保存依赖注入的 beanName
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// <9> 如果匹配的 Bean 是 Class 对象,则根据其 beanName 依赖查找到对应的 Bean
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
// <10> 返回依赖注入的 Bean
return result;
}
finally {
// 设置当前线程的注入点为上一次的注入点,因为本次注入结束了
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
依赖处理的过程稍微有点复杂,如下:
针对给定的工厂给定一个快捷实现的方式,暂时忽略
例如考虑一些预先解析的信息,在进入所有 Bean 的常规类型匹配算法之前,解析算法将首先尝试通过此方法解析快捷方式
获取注解中的 value 对应的值,例如
@Value
、@Qualifier
注解配置的 value 属性值,注意 @Autowired 没有 value 属性配置- 解析注解中的 value,因为可能是占位符,需要获取到相应的数据
- 进行类型转换,并返回
解析复合的依赖对象(Array、Collection、Map 类型),获取该属性元素类型的 Bean 们,调用
resolveMultipleBeans(...)
方法底层和下面第
4
步原理一样,这里会将descriptor
封装成 MultiElementDescriptor 类型,如果找到了则直接返回查找与类型相匹配的 Bean 们,调用
findAutowireCandidates(...)
方法返回结果:key -> beanName;value -> 对应的 Bean
如果一个都没找到
- 如果
@Autowired
配置的 required 为 true,表示必须,则抛出异常 - 否则,返回一个空对象
- 如果
如果匹配的 Bean 有多个,则需要找出最优先的那个
- 找到最匹配的那个 Bean,通过
@Primary
或者@Priority
来决定,或者通过名称决定,调用determineAutowireCandidate(...)
方法 - 如果没有找到最匹配的 Bean,则抛出 NoUniqueBeanDefinitionException 异常
- 获取到最匹配的 Bean,传值引用给
instanceCandidate
- 找到最匹配的那个 Bean,通过
否则,只有一个 Bean,则直接使用其作为最匹配的 Bean
将依赖注入的 Bean 的名称添加至方法入参
autowiredBeanNames
集合,里面保存依赖注入的 beanName如果匹配的 Bean 是 Class 对象,则根据其 beanName 依赖查找到对应的 Bean
返回依赖注入的 Bean
关于上面第 3
步对于符合依赖对象的处理这里不做详细分析,因为底层和第 4
步一样,接下来分析上面第 4
、6
步所调用的方法
findAutowireCandidates 方法
findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor)
方法,找到符合条件的依赖注入的 Bean 们,如下:
// DefaultListableBeanFactory.java
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
// <1> 从当前上下文找到该类型的 Bean 们(根据类型)
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
// <2> 定义一个 Map 对象 `result`,用于保存符合条件的 Bean
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
/**
* <3> 遍历 Spring 内部已处理的依赖对象集合,可以跳到 AbstractApplicationContext#prepareBeanFactory 方法中看看
* 会有一下几个内置处理对象:
* BeanFactory 类型 -> 返回 DefaultListableBeanFactory
* ResourceLoader、ApplicationEventPublisher、ApplicationContext 类型 -> 返回 ApplicationContext 对象
*/
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// <4> 遍历第 `1` 步找到的 Bean 的名称们
for (String candidate : candidateNames) {
// <4.1> 如果满足下面两个条件,则添加至 `result` 集合中
if (!isSelfReference(beanName, candidate) // 如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)
&& isAutowireCandidate(candidate, descriptor)) { // 符合注入的条件
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// <5> 如果没有找到符合条件的 Bean,则再尝试获取
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
// <5.1> 再次遍历第 `1` 步找到的 Bean 的名称们
for (String candidate : candidateNames) {
// <5.2> 如果满足下面三个条件,则添加至 `result` 集合中
if (!isSelfReference(beanName, candidate) // 如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)
&& isAutowireCandidate(candidate, fallbackDescriptor) // 符合注入的条件
&& (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) { // 不是复合类型,或者有 @Qualifier 注解
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// <6> 如果还没有找到符合条件的 Bean,则再尝试获取
// 和上面第 `5` 步的区别在于必须是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)
if (result.isEmpty() && !multiple) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate)
&& (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate))
&& isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
// <7> 返回 `result`,符合条件的 Bean
return result;
}
过程大致如下:
从当前上下文找到该类型的 Bean 们(根据类型)
定义一个 Map 对象
result
,用于保存符合条件的 Bean遍历 Spring 内部已处理的依赖对象集合,例如你依赖注入 BeanFactory 类型的对象,则拿到的是 DefaultListableBeanFactory 对象,依赖注入 ResourceLoader、ApplicationEventPublisher、ApplicationContext 类型的对象, 拿到的就是当前 Spring 上下文 ApplicationContext 对象
遍历第
1
步找到的 Bean 的名称们如果满足下面两个条件,则添加至
result
集合中如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)、符合注入的条件
如果没有找到符合条件的 Bean,则再尝试获取
再次遍历第
1
步找到的 Bean 的名称们如果满足下面三个条件,则添加至
result
集合中如果不是自引用(这个 Bean 不是在需要依赖它的 Bean 的内部定义的)、符合注入的条件、不是复合类型,或者有
@Qualifier
注解
如果还没有找到符合条件的 Bean,则再尝试获取,和上面第
5
步的区别在于必须是自引用(这个 Bean 是在需要依赖它的 Bean 的内部定义的)返回
result
,符合条件的 Bean
总结下来:从当前上下文找到所有该类型的依赖注入对象然后返回,注意,如果你依赖注入的对象就是本身这个 Bean 内部定义的对象有特殊处理。
例如注入一个集合对象,元素类型的 Bean 有一个是定义在本身这个 Bean 的内部,如果仅有这个 Bean 则会注入进行;如果除了本身这个 Bean 内部定义了,其他地方也定义了,那么本身这个 Bean 内部定义的 Bean 是不会被注入的;因为是自引用的 Bean 不会优先考虑,除非一个都没找到,才会尝试获取自引用的 Bean
determineAutowireCandidate 方法
determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor)
方法,找到最匹配的那个依赖注入对象,如下:
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
// <1> 尝试获取一个 @Primary 注解标注的 Bean,如果有找到多个则会抛出异常
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
// <2> 如果第 `1` 步找到了则直接返回
if (primaryCandidate != null) {
return primaryCandidate;
}
// <3> 尝试找到 @Priority 注解优先级最高的那个 Bean,如果存在相同的优先级则会抛出异常
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
// <4> 如果第 `3` 步找到了则直接返回
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
// <5> 兜底方法,遍历所有的 Bean
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
// <5.1> 如果满足下面其中一个条件则直接返回
if ((beanInstance != null
&& this.resolvableDependencies.containsValue(beanInstance)) // 该 Bean 为 Spring 内部可处理的 Bean,例如 ApplicationContext
|| matchesBeanName(candidateName, descriptor.getDependencyName())) { // 名称相匹配
return candidateName;
}
}
// <6> 上面都没选出来则返回一个空对象
return null;
}
如果找到了多个匹配的依赖注入对象,则需要找到最匹配的那个 Bean,过程大致如下:
- 尝试获取一个
@Primary
注解标注的 Bean,如果有找到多个则会抛出异常 - 如果第
1
步找到了则直接返回 - 尝试找到
@Priority
注解优先级最高的那个 Bean,如果存在相同的优先级则会抛出异常 - 如果第
3
步找到了则直接返回 - 兜底方法,遍历所有的 Bean
- 如果满足下面其中一个条件则直接返回:该 Bean 为 Spring 内部可处理的 Bean(例如 ApplicationContext、BeanFactory)、名称相匹配
- 上面都没选出来则返回一个空对象
总结
本文讲述了 @Autowired
、@Value
和 @Resource
等注解的实现原理,在《Bean 的创建过程》中我们可以了解到,在 Spring Bean 生命周期的很多阶段都可以通过相应的 BeanPostProcessor 处理器进行扩展,其中《Bean 的属性填充阶段》会通过 InstantiationAwareBeanPostProcessor
对 Bean 进行处理,有以下两个处理器:
- AutowiredAnnotationBeanPostProcessor,主要处理
@Autowired
和@Value
注解进行依赖注入 - CommonAnnotationBeanPostProcessor,主要处理
@Resource
注解进行依赖注入,以及@PostConstruct
和@PreDestroy
生命周期注解的处理
原理就是找到注解标注的字段(或方法),创建对应的注入元信息对象,然后根据该元信息对象进行注入(反射机制),底层都是通过 DefaultListableBeanFactory#resolveDependency
方法实现的,找到符合条件的 Bean(根据类型),然后筛选出最匹配的那个依赖注入对象。
疑问:@Bean 等注解的实现原理又是怎样的呢?别急,在后续文章进行分析
死磕Spring之IoC篇 - @Autowired 等注解的实现原理的更多相关文章
- 死磕Spring之IoC篇 - @Bean 等注解的实现原理
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - 文章导读
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - Bean 的创建过程
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - Spring 应用上下文 ApplicationContext
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - BeanDefinition 的加载阶段(XML 文件)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - BeanDefinition 的解析阶段(XML 文件)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - 解析自定义标签(XML 文件)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - BeanDefinition 的解析过程(面向注解)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 死磕Spring之IoC篇 - 开启 Bean 的加载
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
随机推荐
- Codeforces Round #479 (Div. 3) F. Consecutive Subsequence (DP)
题意:给你一个长度为\(n\)的序列,求一个最长的\({x,x+1,x+2,.....,x+k-1}\)的序列,输出它的长度以及每个数在原序列的位置. 题解:因为这题有个限定条件,最长序列是公差为\( ...
- 已处理证书链,但是在不受信任提供程序信任的根证书中终止 - Windows 7安装.Net Framework 4.6.2时出现此问题
https://blog.csdn.net/inchat/article/details/104294302
- 【ybt金牌导航1-2-6】【luogu P2467】地精部落
地精部落 题目链接:ybt金牌导航1-2-6 / luogu P2467 题目大意 有一个排列,要使得每个位置要么都比两边高,要么比两边低. 而且一定要以一高一低的方式排列. 两边的只用比旁边的那个高 ...
- PAT L2-004. 这是二叉搜索树吗?【前序遍历转化为后序遍历】
一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点, 其左子树中所有结点的键值小于该结点的键值: 其右子树中所有结点的键值大于等于该结点的键值: 其左右子树都是二叉搜索树. 所谓二叉搜索 ...
- c# App.xaml
随着wpf自动创建的,是项目的起始点..Net先再App里找,找到了window然后开启window,项目真正的起始点是在App里. 这两个 (App 的xaml和cs文件)和MainWindow 的 ...
- docker安装CentOS7及JNI使用相关过程记录
docker pull centos:centos7(拉取镜像) docker run -itd --name centos-test centos:centos7 (运行容器) docker exe ...
- Gym 101128F Landscaping(网络流)题解
题意:n*m的地,从有高地和低地,从高地走到低地或者从低地走到高地花费a,把高地和低地互相改造一次花费b.现在要走遍每一行每一列,问最小花费 思路:超级源点连接所有低地,容量b:所有地向四周建边,容量 ...
- 007.NET5 Log4Net组件使用
NET 5 Log4Net组件使用 1. Nuget引入程序集:log4net + Microsfot.Extensions.Logging.Log4Net.AspNetCore 2. 准备配置文件 ...
- how to remove duplicates of an array by using js reduce function
how to remove duplicates of an array by using js reduce function ??? arr = ["a", ["b& ...
- Online analog video interview
Online analog video interview 在线模拟视频面试 English 口语 https://www.pramp.com/#/ https://www.pramp.com/faq ...