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
第2個方法有問題的 private,可以看到持有屬性為空
2、理清spring和springmvc的關系
-
tomcat啟動流程見下圖,先啟動spring ,后啟動springmvc
-
spring處理注解@Service @AspectJ ,springmvc處理@RequestMapping
3、為什么autowire注入的屬性為空
- spring處理autowire注解在bean實例化時候,初始化之前。
- controller生成代理在 bean初始化之后生成,此時雖然代理繼承controller的屬性,有autowire注解,但是spring已經不處理autowire注解了
- 和private沒有關系
---------->>
- 原來的controller在spring中正常生成,autowire生效
- 代理在spring中是后來才加入到容器中,autowire不生效
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
6、spring處理注解@Service @AspectJ
2.1 處理注解@Service
入口:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
准備生成bean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
實例化bean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
進入autowire的注解處理器代碼中org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
至此 在初始化之前將autowire屬性注入
2.2 處理注解@AspectJ
aspectj相關的bean處理器
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
找到bean適用的攔截器,假設controller中有某個自定義注解@self,而aspect定義攔截規則為@self做攔截,此時會將controller做攔截,生成代理對象見第2個圖
7、springmvc處理注解 @RequestMapping
org.springframework.web.servlet.DispatcherServlet作為servlet+applicationContext啟動
調用refresh方法,找到handlerMapping
從context中找到所有的handlerMapping
而注冊RequestMappingHandlerMapping完之后會調用初始化方法 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
會拿到context中所有的bean,isHandler()檢測是否有兩個注解。
- 此時context中對象已經被代理了,比如之前的controller已經被代理了
接下來構建url到method對象的map org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod