往期热门文章:
1、再见Jenkins!一款更适合国人的自动化部署工具,贼带劲!!
2、MySQL中这14个小玩意,让人眼前一亮!!!
3、Spring 官方证实!大漏洞,JDK 9 及以上均受影响
4、阿里家属快当不下去了
5、C++ 首超 Java!地位不保?
本文来源:业余草
背景
最近,在公司 CodeReview 会上,我给众多同事布置了“家庭作业”。Controller 中的请求方法,通常我们都是 public 的,如果是 private 的、protected 的行不行,为什么?
后来一个同事比较认真,第二天早上测试后发现报错了,给我反馈说 private 方法的内部注入的 service 为 null,修改成 public 后就不会为 null。为什么会产生这个问题呢?这个同事没有回答出来,今天我抽空调试了一下源码,给大家总结一下,分享给大家!
首先简单模拟一下环境
public interface TestService {String getTestString();
}@Service("testService")
public class TestServiceImpl implements TestService {@Overridepublic String getTestString() {return "业余草";}
}@RestController
public class MainController {@Autowiredprivate TestService service;@RequestMapping("/testA")public String testA(){return service.getTestString();}@RequestMapping("/testB")private String testB(){return service.getTestString();}}
/testA
是 pulibc,/testB
是 pirvate,测试结果「均能返回"业余草"字符串」
测试和公司环境还有一个不太同的就是公司项目中有 Aop 切面处理访问日志的,还要添加一个 Aop。
@Aspect
@Component
public class WebLogAspect {private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);@Pointcut("execution(public * com.spring.controller..*.*(..))")public void controllerLog(){}@Before("controllerLog()")public void logBeforeController(JoinPoint joinPoint) {RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();logger.info("*************URL : " + request.getRequestURL().toString());logger.info("*************HTTP_METHOD : " + request.getMethod());logger.info("*************IP : " + request.getRemoteAddr());}
}
添加了一个 Aop 后测试:
/testA返回"业余草"字符串
/testB访问报错,service注入失败,为null
为什么使用 Aop 会导致 private 修饰的方法注入失败,查询了许多资料,网上有人说到 org.springframework.aop.support.AopUtils
中的代码使用的是Method[] methods = clazz.getMethods()
,即是只能拿到 public 方法。但是我使用的版本2.1.4.RELEASE
中已经使用Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
这就有点迷惑了。
会不会是切点注解中的修饰符匹配不到呢?将切点中的修饰符从 public 修改成 private。
@Pointcut("execution(private* com.spring.controller..*.*(..))")public void controllerLog(){}
测试结果:
/testA返回"业余草"字符串
/testB访问报错,service注入失败,为null
还是不行(就挺秃然的)。
哎,想偷懒还不行,只能我一行一行调试代码了。
目前结论
方法中没有用
@Autowired
或者@Resource
注入的对象。有无 Aop,任意修饰符都可以正常访问并且返回结果方法中使用了
@Autowired
或者@Resource
注入的对象
没有 Aop 切面的情况下,public
,protected
,private
都能正常的映射
在有 Aop 切面的情况下,public
,protected
可以正常映射,但是使用private
会报空指针异常,注入对象为 null。
最后经过我的一番折腾与调试之后,发现:
使用了 aop,也就是使用动态代理,你的 SpringBoot 版本为 2.1.4 release,底层默认调用的是 cglib 作为动态代理。
其本质是:调用某个类的方法时,实际上是先为该类生成一个子类,然后再在子类中通过反射等,达到方法拦截的目的。对于子类,其父类中,private
修饰的方法,子类如果与父类不在同一包下,是没有访问的权限的,此场景下,cglib 生成的子类,不会和父类在同一包下,也就是private
修饰的方法,不能进行动态代理,所以会报空指针异常。
往期热门文章:
1、IntelliJ IDEA终于支持对Redis 的可视化窗口操作了,真香!
2、ChatGPT能接入微信了!
3、Java 反射慢?它到底慢在哪?
4、当我去了不到 20 人的 IT 公司后。。。
5、GitHub 被超火的 ChatGPT 霸榜!
6、Java使用 try catch会影响性能?
7、Java使用 try catch会影响性能?
8、原来count(*)是接口性能差的真凶!
9、大公司病了,这也太形象了吧!!!
10、全球最大资源站创始人被抓,但网站还会继续活下去