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

posted @ 2020-09-16 12:01  yunspirit  阅读(1654)  评论(0编辑  收藏  举报