<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:Spring 為什么不支持 static 字段的注入?

          共 19649字,需瀏覽 40分鐘

           ·

          2024-05-24 14:46

          來(lái)源:juejin.cn/post/7283803914645569536

          ?? 歡迎加入小哈的星球 ,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / Java 學(xué)習(xí)路線 / 一對(duì)一提問 / 學(xué)習(xí)打卡 /  每月贈(zèng)書


          新項(xiàng)目:仿小紅書(微服務(wù)架構(gòu))正在更新中... , 全棧前后端分離博客項(xiàng)目 2.0 版本完結(jié)啦, 演示鏈接http://116.62.199.48/ 。全程手摸手,后端 + 前端全棧開發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了261小節(jié),累計(jì)43w+字,講解圖:1806張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有1400+小伙伴加入(早鳥價(jià)超低)



          我們都知道Spring在創(chuàng)建一個(gè)bean的時(shí)候,還要去填充bean的屬性

          大致流程如下:

          • 反射創(chuàng)建bean——createBeanInstance
          • 填充bean——populateBean
          • 初始化bean——initializeBean(包括前后置增強(qiáng))
          • 注冊(cè)bean的銷毀方法——registerDisposableBeanIfNecessary

          這個(gè)填充bean的邏輯是在populateBean

          protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
             // ...

             PropertyDescriptor[] filteredPds = null;
             if (hasInstAwareBpps) {
                if (pvs == null) {
                   pvs = mbd.getPropertyValues();
                }
                for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                   // here
                   PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                   if (pvsToUse == null) {
                      if (filteredPds == null) {
                         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                      }
                      pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                      if (pvsToUse == null) {
                         return;
                      }
                   }
                   pvs = pvsToUse;
                }
             }
             // ...

             if (pvs != null) {
                applyPropertyValues(beanName, mbd, bw, pvs);
             }
          }

          而除了applyPropertyValues可以填充bean的屬性外

          更多的填充邏輯(字段注入)應(yīng)該是在InstantiationAwareBeanPostProcessor中的postProcessProperties里面,字段注入就是常用的@Autowired、@Resource注解

          InstantiationAwareBeanPostProcessor是一個(gè)接口

          它的子類中實(shí)現(xiàn)Autowired注入的是AutowiredAnnotationBeanPostProcessor,實(shí)現(xiàn)Resource注入的是CommonAnnotationBeanPostProcessor

          接下來(lái)分析一下AutowiredAnnotationBeanPostProcessor是怎么進(jìn)行字段注入的

          // AutowiredAnnotationBeanPostProcessor.postProcessProperties
          @Override
          public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
             // 找到需要Autowired的元數(shù)據(jù)(字段、方法) 
             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;
          }

          以上這段代碼是AutowiredAnnotationBeanPostProcessor實(shí)現(xiàn)的postProcessProperties

          流程就是先找到需要通過(guò)findAutowiringMetadata找到需要Autowired的元數(shù)據(jù)(字段、方法) ,然后再inject

          先看看findAutowiringMetadata

          // AutowiredAnnotationBeanPostProcessor.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.
             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;
          }

          這個(gè)needsRefresh簡(jiǎn)單看看就好,我們是第一次進(jìn)入這個(gè)方法,所以這個(gè)metadata是null,那么這個(gè)方法返回的是true

          public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
             return (metadata == null || metadata.needsRefresh(clazz));
          }

          那么會(huì)進(jìn)入到這個(gè)方法buildAutowiringMetadata

          private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
             if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
                return InjectionMetadata.EMPTY;
             }

             List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
             Class<?> targetClass = clazz;

             do {
                final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
                // 處理字段
                ReflectionUtils.doWithLocalFields(targetClass, field -> {
                   MergedAnnotation<?> ann = findAutowiredAnnotation(field);
                   if (ann != null) {
                      if (Modifier.isStatic(field.getModifiers())) {
                         if (logger.isInfoEnabled()) {
                            logger.info("Autowired annotation is not supported on static fields: " + field);
                         }
                         return;
                      }
                      boolean required = determineRequiredStatus(ann);
                      currElements.add(new AutowiredFieldElement(field, required));
                   }
                });
                // 處理方法
                ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                   Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                   if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                      return;
                   }
                   MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
                   if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                      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);
                         }
                      }
                      boolean required = determineRequiredStatus(ann);
                      PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                      currElements.add(new AutowiredMethodElement(method, required, pd));
                   }
                });

                elements.addAll(0, currElements);
                // 獲取父類,繼續(xù)找
                targetClass = targetClass.getSuperclass();
             }
             while (targetClass != null && targetClass != Object.class);

             return InjectionMetadata.forElements(elements, clazz);
          }

          這邊傳入的clazz就是bean的Class,忘記了可以找上面的代碼看一下

          這里源碼寫了很多,我們暫時(shí)只關(guān)心注入字段的那一塊

          ReflectionUtils.doWithLocalFields(targetClass, field -> {
             MergedAnnotation<?> ann = findAutowiredAnnotation(field);
             if (ann != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                   if (logger.isInfoEnabled()) {
                      logger.info("Autowired annotation is not supported on static fields: " + field);
                   }
                   return;
                }
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
             }
          });

          處理字段的時(shí)候進(jìn)入了ReflectionUtilsdoWithLocalFields方法

          // ReflectionUtils.doWithLocalFields
          public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
             for (Field field : getDeclaredFields(clazz)) {
                try {
                   fc.doWith(field);
                }
                catch (IllegalAccessException ex) {
                   throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
                }
             }
          }

          繼續(xù)追溯一下可以得知,這里是獲取clazz的所有字段并進(jìn)行處理,這個(gè)FieldCallback是一個(gè)函數(shù)式接口,它的實(shí)現(xiàn)就是外面?zhèn)鬟M(jìn)來(lái)的這段代碼

          field -> {
             MergedAnnotation<?> ann = findAutowiredAnnotation(field);
             if (ann != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                   if (logger.isInfoEnabled()) {
                      logger.info("Autowired annotation is not supported on static fields: " + field);
                   }
                   return;
                }
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
             }
          }

          那么在這段代碼里面,又去找這個(gè)字段有沒有被@Autowired修飾

          // AutowiredAnnotationBeanPostProcessor.findAutowiredAnnotation
          @Nullable
          private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
             MergedAnnotations annotations = MergedAnnotations.from(ao);
             // autowiredAnnotationTypes包含 @Autowired,@Value,@Inject
             for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
                MergedAnnotation<?> annotation = annotations.get(type);
                if (annotation.isPresent()) {
                   return annotation;
                }
             }
             return null;
          }

          如果有@Autowired修飾,那么lambda中的ann不為null

          最關(guān)鍵的地方來(lái)了,接下來(lái)會(huì)判斷這個(gè)字段是不是static的,如果是,那么會(huì)發(fā)出警告,并且直接返回,不進(jìn)行注入了

          field -> {
             MergedAnnotation<?> ann = findAutowiredAnnotation(field);
             if (ann != null) {
                // 判斷是否被static修飾
                if (Modifier.isStatic(field.getModifiers())) {
                   if (logger.isInfoEnabled()) {
                      logger.info("Autowired annotation is not supported on static fields: " + field);
                   }
                   return;
                }
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
             }
          }

          最終注入的邏輯在InjectionMetadata的inject中

          // InjectionMetadata.inject
          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) {
                   element.inject(target, beanName, pvs);
                }
             }
          }

          element的邏輯就暫時(shí)省略了,大概就是如果是字段,那么通過(guò)反射去注入,如果是方法,也通過(guò)反射去執(zhí)行

          @Resource與以上大致同理

          總結(jié)

          總的來(lái)說(shuō),就一句話,spring在使用字段注入對(duì)靜態(tài)字段進(jìn)行注入時(shí),會(huì)忽略掉這個(gè)字段,不去注入

          也就是說(shuō)Spring是有能力去注入靜態(tài)字段的,但是Spring沒有選擇注入,為什么呢?

          可能是因?yàn)镾pring的設(shè)計(jì)理念是管理bean對(duì)象,只有屬于對(duì)象的字段Spring才去進(jìn)行管理,如果是static的話,那么這個(gè)字段屬于類了,這個(gè)時(shí)候Spring去進(jìn)行管理貌似不符合它的設(shè)計(jì)理念,所以Spring直接忽略掉了;另外如果一個(gè)bean修改了這個(gè)字段,那么所有bean的這個(gè)字段都會(huì)受到影響,因?yàn)檫@個(gè)字段是屬于類的,這個(gè)時(shí)候可能就會(huì)問題

          那么有沒有辦法實(shí)現(xiàn)靜態(tài)字段注入呢?

          可以的,在方法中打上@Autowired注解,在方法里面去對(duì)靜態(tài)字段進(jìn)行賦值,當(dāng)然這個(gè)方法也不能是靜態(tài)的,否則也會(huì)被spring會(huì)忽略掉

          不過(guò)如果能夠不對(duì)靜態(tài)字段注入就盡量不要注入,因?yàn)閟pring本身就不鼓勵(lì)我們這么做,這種不鼓勵(lì)已經(jīng)深入到代碼里面了

          ?? 歡迎加入小哈的星球 ,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / Java 學(xué)習(xí)路線 / 一對(duì)一提問 / 學(xué)習(xí)打卡 /  每月贈(zèng)書


          新項(xiàng)目:仿小紅書(微服務(wù)架構(gòu))正在更新中... , 全棧前后端分離博客項(xiàng)目 2.0 版本完結(jié)啦, 演示鏈接http://116.62.199.48/ 。全程手摸手,后端 + 前端全棧開發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了261小節(jié),累計(jì)43w+字,講解圖:1806張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有1400+小伙伴加入(早鳥價(jià)超低)



              
                 

          1. 我的私密學(xué)習(xí)小圈子~

          2. @Transactional 中使用線程鎖導(dǎo)致了鎖失效,驚了!

          3. 終于有人把灰度發(fā)布架構(gòu)設(shè)計(jì)講明白了

          4. 面試官:Spring Boot 的啟動(dòng)原理是什么?

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下在看,加個(gè)星標(biāo),這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          點(diǎn)“在看”支持小哈呀,謝謝啦

          瀏覽 534
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产性交黄片 | 啪啪网站在线观看 | 婷婷爱爱网 | 久久久久久电影成人电影 | 无码h |