环境 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 = 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);
这段代码也有很多类重载了:
文章图片
选择:
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
对象,利用Filed
的set
方法来对字段进行注入赋值。推荐阅读
- 第五节:SpringBoot常用注解介绍
- 第四节:SpringBoot中web模版数据渲染展示
- SpringBoot2022【草稿】
- 聊聊springboot项目全局异常处理那些事儿
- 第一节:创建SpringBoot项目并运行HelloWorld
- springboot管理系统[基于员工角色和文件权限的分级的后台管理系统源码]
- SpringBoot之@ComponentScan和@SpringBootApplication扫描覆盖问题
- mybatis|记mybatis查询null字段导致的NPE
- SpringBoot|SpringBoot 整合 druid数据源
- springboot项目配置application添加图片映射 (windows and linux 都可使用)