公众号[JavaQ]原创,专注分享Java基础原理分析、实战技术、微服务架构、分布式系统构建,诚邀点赞关注!
面试官:Spring框架中的@Autowired注解可以标注在哪些地方?
小小白:@Autowired注解可以被标注在构造函数、属性、setter方法或配置方法上,用于实现依赖自动注入。
面试官:有没有研究过@Autowired注解的实现原理?
小小白:看过它的实现源码。
面试官:那你说一下@Autowired注解的工作原理?
小小白:@Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor实现的,查看该类的源码会发现它实现了MergedBeanDefinitionPostProcessor接口,进而实现了接口中的postProcessMergedBeanDefinition方法,@Autowired注解正是通过这个方法实现注入类型的预解析,将需要依赖注入的属性信息封装到InjectionMetadata类中,InjectionMetadata类中包含了哪些需要注入的元素及元素要注入到哪个目标类中,在Spring容器启动的过程中初始化单例bean的时候通过populateBean方法实现对属性的注入。
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, beanType, (PropertyValues)null);
metadata.checkConfigMembers(beanDefinition);
}
}
public class InjectionMetadata {
private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
private final Class> targetClass;
private final Collection injectedElements;
private volatile Set checkedElements;
面试官:AutowiredAnnotationBeanPostProcessor类的postProcessMergedBeanDefinition方法是在什么时候被调用的?
小小白:Spring容器在启动的时候会执行AbstractApplicationContext类的refresh方法,在refresh方法执行的过程中先注册AutowiredAnnotationBeanPostProcessor,然后在对非延迟初始化的单例bean进行初始化时,会间接调用。具体实现细节分析如下。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 重点看这里:在这里对AutowiredAnnotationBeanPostProcessor注册
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 重点看这里:对非延迟初始化的单例bean进行初始化
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
refresh方法中registerBeanPostProcessors(beanFactory)完成了对AutowiredAnnotationBeanPostProcessor的注册,当执行finishBeanFactoryInitialization(beanFactory)方法对非延迟初始化的单例bean进行初始化时,会执行到AbstractAutowireCapableBeanFactory类的doCreateBean方法,在这个方法中有如下这么一段代码。
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
在这段代码中会执行applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName),深入到这个applyMergedBeanDefinitionPostProcessors方法中。
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);
}
}
}
可以看到,if的条件判断逻辑是否属于MergedBeanDefinitionPostProcessor,而AutowiredAnnotationBeanPostProcessor正好实现了MergedBeanDefinitionPostProcessor接口,所以在这里调用AutowiredAnnotationBeanPostProcessor类的postProcessMergedBeanDefinition方法。
面试官:你在说一下注入的过程?
小小白:使用AutowiredFieldElement实现对标注在属性上的注入,使用AutowiredMethodElement对标注在方法上的注入。注入过程:根据需要注入的元素的描述信息,按类型或名称查找需要的依赖值,如果依赖没有实例化先实例化依赖,然后使用反射进行赋值。
面试官:@Resource或者@Autowired注解有什么区别?
小小白:虽然@Resource和@Autowired都可以书写标注在属性或者该属性的setter方法之上,但是@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果想按照名称来注入,则需要结合@Qualifier一起使用;@Resource注解是由JDK提供,而@Autowired是由Spring提供。
往期推荐:
大厂都聊分布式系统,面试不知道分布式锁如何聊下去
面试官:SpringBoot中关于日志工具的使用,我想问你几个常见问题
面试被问为什么使用Spring Boot?答案好像没那么简单
面试官:Spring框架内置了哪些可扩展接口,咱们一个一个聊
Spring声明式事务处理的实现原理,来自面试官的穷追拷问
Spring MVC相关面试题就是无底洞,反正我是怕了
说实话,面试这么问Spring框架的问题,我快扛不住了
没使用加号拼接字符串,面试官竟然问我为什么
面试官一步一步的套路你,为什么SimpleDateFormat不是线程安全的
都说ThreadLocal被面试官问烂了,可为什么面试官还是喜欢继续问
Java注解是如何玩转的,面试官和我聊了半个小时
如何去除代码中的多次if而引发的一连串面试问题
String引发的提问,我差点跪了
就写了一行代码,被问了这么多问题
面试官:JVM对锁进行了优化,都优化了啥?
synchronized连环问
支持原创,我点【在看】