问题描述
随着项目进展,需要对用户的操作进行日志记录,原有功能接口在这之前都可以正常使用,增加AOP日志后出现service层注入为null:
问题排查
根据详细的错误日志发现,代码中通过@Autowired注解注入的Service类对象为NULL。
原因
后来经过筛查以及和其他Controller层的类进行对比差异,发现接口方法的修饰符是private
,而且在这个接口上没有加AOP日志,于是我将其改为public
,最后进行测试,不会报错了。
错误代码
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserName")
private String getUserName() {
return userService.getUserName();
}
}
正确代码
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserName")
public String getUserName() {
return userService.getUserName();
}
}
问题复现
新建一个项目
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserName")
private String getUserName() {
return userService.getUserName();
}
}
问题分析
我们这里使用的是CGLIB代理,该代理的原理是:
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
既然CGLIB是通过生成子类的方式来创建代理,那么它生成的子类肯定就要继承父类。
关于Java中的继承,有一条很重要的特性就是:
子类拥有父类非 private 的属性、方法。那么也就是说如果父类中有private方法,生成的代理类中是看不到的。
换言之,由CGLIB创建的代理类,不会包含父类中的私有方法。另外由于CGLIB代理类的生成过程,决定了其成员(无论是private还是protected)均是null。
其他原因
1、该类没有托管给Spring 管理
一般在类的上面添加@Component就可以了
2、看你的xxxxxApplication是否在根目录,因为Springboot默认扫描的就是启动类下的目录,当然也可以通过过@ComponenScan注解去指定扫描范围
3、通过new 的方式创建出来的实例是没有交给Spring 进行管理的,没有被Spring 管理的实例,sSpring ring是无法自动注入bean的,所以为null
例如:
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SessionInterceptor()).excludePathPatterns("/static/**").addPathPatterns("/**");
}
}
public class SessionInterceptor implements HandlerInterceptor {
@Autowired
private IUserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
HttpSession session = httpServletRequest.getSession();
String username = (String)session.getAttribute("userName");
//运行时候userService是null
SysUser userInfo =userService.getUserInfoByUserName(username).get(0);
}