SpringAOP私有方法導致@Autowire注入失敗原理


SpringAOP私有方法導致@Autowire注入失敗原理

1、問題描述

@RestController()
@RequestMapping("/tradeadmin")
@Slf4j
public class TradeAdminController {

    @Autowired
    private  orderQueryExternalService;

    @Autowired
    private OrderOperateExternalService orderOperateExternalService;

    @RequestMapping("/queryOrderLines")
    @ResponseBody
    @ExceptionHandler
    public Page<TradeOrderLineVO> queryOrderLines(TradeOrderQueryRequest request) {
        return orderQueryExternalService;
    }

    @RequestMapping("/buyerConfirmGoods")
    @ResponseBody
    @ExceptionHandler
    private BaseResult<Boolean> buyerConfirmGoods(HttpServletRequest request, Long buyerId, String tradeOrderLineStr) throws Exception {
       return  orderQueryExternalService
    }
}

第一個方法沒問題的 public

image.png

第2個方法有問題的 private,可以看到持有屬性為空

image.png

2、理清spring和springmvc的關系

  • tomcat啟動流程見下圖,先啟動spring ,后啟動springmvc

  • spring處理注解@Service @AspectJ ,springmvc處理@RequestMapping

image.png

3、為什么autowire注入的屬性為空

  • spring處理autowire注解在bean實例化時候,初始化之前。
  • controller生成代理在 bean初始化之后生成,此時雖然代理繼承controller的屬性,有autowire注解,但是spring已經不處理autowire注解了
  • 和private沒有關系

---------->>

  • 原來的controller在spring中正常生成,autowire生效
  • 代理在spring中是后來才加入到容器中,autowire不生效

image.png

4、springAop處理public和private

  • 上面controller有private方法,cglib代理的時候只處理public方法,生成對象如下圖,參考https://www.cnblogs.com/wyq1995/p/10945034.html

  • 觸發controller的public方法時候,發現屬性field1是正常。 調用邏輯是先走代理的test1,然后代理使用自己持有的原生controller對象調用test1。原生controller中已經通過autowire注入filed1

  • 觸發controller的private方法時候,發現屬性filed1是null。 調用邏輯是走代理的private方法,由於test2方法沒有重寫,走原生controller的test2。這時候test2去獲取field1,此時獲取的是子類(也就是代理)中的filed1

5、springMvc為何最后url映射到private方法並且可以調用,明明沒有代理private方法

代理繼承了controller,照理說不會擁有controller的private方法test1,也無法調用test1,最后卻調用了test1?

  • springmvc處理@requestMapping注解,處理該注解的時候不管方法是private還是public修飾,將url映射到method對象,緩存起來。

    當用戶請求url時,springmvc拿到url對應的method對象(即test1),然后method.invoke(object ,args),但是invoke傳入的object是代理,代理是原生controller的子類,雖然無法訪問test1,但是此處通過反射調用代理的test1,在test1中會獲取field1,而代理中的field1為null了。

訣竅:通過父類拿到私有方法privateCall的method對象,因為代理對象繼承父類Dog,無法通過代理對象拿到父類的私有方法。

接着通過反射,調用代理privateCall獲取某個私有屬性為null

image.png

image.png

image.png

6、spring處理注解@Service @AspectJ

2.1 處理注解@Service

入口:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

image.png

准備生成bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

image.png

實例化bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

image.png

進入autowire的注解處理器代碼中org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

image.png

至此 在初始化之前將autowire屬性注入

2.2 處理注解@AspectJ

aspectj相關的bean處理器

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

image.png

找到bean適用的攔截器,假設controller中有某個自定義注解@self,而aspect定義攔截規則為@self做攔截,此時會將controller做攔截,生成代理對象見第2個圖

image.png

7、springmvc處理注解 @RequestMapping

org.springframework.web.servlet.DispatcherServlet作為servlet+applicationContext啟動

調用refresh方法,找到handlerMapping

image.png

從context中找到所有的handlerMapping

image.png

而注冊RequestMappingHandlerMapping完之后會調用初始化方法 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

會拿到context中所有的bean,isHandler()檢測是否有兩個注解。

  • 此時context中對象已經被代理了,比如之前的controller已經被代理了

image.png

image.png

接下來構建url到method對象的map org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod

image.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM