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
第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