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); 这段代码,然后发现其有很多类重载了:
SpringBoot学习笔记(@Autowired)
文章图片

我们看到:AutowiredAnnotationBeanPostProcessor这个类中的postProcessPropertyValues方法:
@Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = https://www.it610.com/article/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 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); 这段代码也有很多类重载了:
SpringBoot学习笔记(@Autowired)
文章图片

选择:AutowiredAnnotationBeanPostProcessor
@Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { value = https://www.it610.com/article/resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set> autowiredBeanNames = new LinkedHashSet>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { value = https://www.it610.com/article/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访问权限,反射照样可以获取得字段属性。
总结 【SpringBoot学习笔记(@Autowired)】@Autowired注解 ,spring利用反射获取到Filed对象,利用Filedset方法来对字段进行注入赋值。

    推荐阅读