SpringBoot学习笔记:@Autowired

环境

MacBook pro
java 8
springboot 2.0+

前言

学习笔记

@Autowired

今天参考Spring基础(2):放弃XML,走向注解,
这篇文章温习spring时,对@Autowired注入方式产生了疑惑。

因为我写了一个如下类:

package com.supper.javaconfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author yutao
 * @since 2020/4/13 3:44 下午
 */
@Component
public class PersonC {

    @Autowired
    private CarC carC;

    public CarC getCarC() {
        return carC;
    }

    @Override
    public String toString() {
        return "PersonC{" +
                "carC=" + carC +
                '}';
    }
}
package com.supper.javaconfig;

import org.springframework.stereotype.Component;

/**
 * @author yutao
 * @since 2020/4/13 3:44 下午
 */
@Component
public class CarC {
}

configuration类:

package com.supper.javaconfig;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author yutao
 * @since 2020/4/13 4:17 下午
 */
@Configuration // 表示这个Java类充当xml配置文件
@ComponentScan("com.supper.javaconfig") // 相当于XML中的标签
public class AppConfig {
}

Test类:

package com.supper;

import com.supper.javaconfig.AppConfig;
import com.supper.javaconfig.PersonC;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author yutao
 * @since 2020/4/13 4:16 下午
 */
public class ConfigTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

        PersonC personC = (PersonC) applicationContext.getBean("personC");
        System.out.println(personC);
        System.out.println(personC.getCarC());
    }
}

之后显示,PersonC中的CarC注入成功。

PersonC{carC=com.supper.javaconfig.CarC@7494f96a}
com.supper.javaconfig.CarC@7494f96a

疑问

① 我没有提供相关的setter方法和构造方法,spring是怎么注入进去的?
② 就是提供了CarC的setter方法,spring也不会调用它来进行注入。

带着这样的疑问,只能去源码里找答案了。

首先我们找到AbstractAutowireCapableBeanFactory这个类,因为@Autewired的注解解析会调用里面的:

@Override
public void autowireBean(Object existingBean) {
	// Use non-singleton bean definition, to avoid registering bean as dependent bean.
	RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
	bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
	bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), getBeanClassLoader());
	BeanWrapper bw = new BeanWrapperImpl(existingBean);
	initBeanWrapper(bw);
	populateBean(bd.getBeanClass().getName(), bd, bw);
}

在找到populateBean(bd.getBeanClass().getName(), bd, bw);,进一步往下看:
这个方法很长,我就贴出部分代码:

if (hasInstAwareBpps || needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}

看到pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);这段代码,然后发现其有很多类重载了:
在这里插入图片描述
我们看到:AutowiredAnnotationBeanPostProcessor这个类中的postProcessPropertyValues方法:

@Override
public PropertyValues postProcessPropertyValues(
		PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

	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;
}

再看到inject方法:

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> elementsToIterate =
			(this.checkedElements != null ? this.checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		boolean debug = logger.isDebugEnabled();
		for (InjectedElement element : elementsToIterate) {
			if (debug) {
				logger.debug("Processing injected element of bean '" + beanName + "': " + element);
			}
			element.inject(target, beanName, pvs);
		}
	}
}

发现element.inject(target, beanName, pvs);这段代码也有很多类重载了:
在这里插入图片描述
选择:AutowiredAnnotationBeanPostProcessor

@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
	Field field = (Field) this.member;
	Object value;
	if (this.cached) {
		value = resolvedCachedArgument(beanName, this.cachedFieldValue);
	}
	else {
		DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
		desc.setContainingClass(bean.getClass());
		Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
		TypeConverter typeConverter = beanFactory.getTypeConverter();
		try {
			value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
		}
		catch (BeansException ex) {
			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
		}
		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)) {
							if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
								this.cachedFieldValue = new ShortcutDependencyDescriptor(
										desc, autowiredBeanName, field.getType());
							}
						}
					}
				}
				else {
					this.cachedFieldValue = null;
				}
				this.cached = true;
			}
		}
	}
	if (value != null) {
		ReflectionUtils.makeAccessible(field);
		// 就是这段代码
		field.set(bean, value);
	}
}
}

最后看到field.set(bean, value);这段代码。

也就是明白了,为什么不提供setter方法和构造方法也能注入成功。
ReflectionUtils.makeAccessible(field); 这段代码是把访问权限限制解开了,
所以即使我们类设置了private访问权限,反射照样可以获取得字段属性。

总结

@Autowired注解 ,spring利用反射获取到Filed对象,利用Filedset方法来对字段进行注入赋值。

你可能感兴趣的:(springboot)