简介
本文介绍Spring的IOC常见的问题。
注入为null
使用@Autowired注入bean的时候,有时注入失败,bean为null,主要有以下几种原因:
- 注入的类没有加入容器。比如:没加@Component,没被加入到SpringBoot包扫描路径
- 在特殊bean中注入:Filter、Listener
- 不在一个上下文中
- 静态方法使用静态注入的变量
特殊bean中注入
原因
因为Filter和Listener加载顺序优先于普通bean,所以使用@Autowired肯定为null了
解决方案
用ApplicationContext去获取bean。
法1:根据Class获取
法2:根据bean名称(注意名称为实现类而不是接口)去获取bean
工具类写法
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextHolder.context = context; } public static ApplicationContext getContext() { return context; } }
不在同一个上下文
案例1:websocket
复现
websocket 里面使用 @Autowired 注入 service 或 bean 时,报空指针异常,service 为 null。
解决方案:将要注入的 service 改成 static,就不会为null了
@Controller @ServerEndpoint(value="/chatSocket") public class ChatSocket { // 这里使用静态,让 service 属于类 private static ChatService chatService; // 注入的时候,给类的 service 注入 @Autowired public void setChatService(ChatService chatService) { ChatSocket.chatService = chatService; } }
分析
概述:spring管理的都是单例(singleton),和 websocket (多对象)相冲突。
详解:项目启动时会初始化 websocket (非用户连接的),spring 同时会为其注入 service,该对象的 service 不是 null,被成功注入。但是,由于 spring 默认管理的是单例,所以只会注入一次 service。当新用户进入聊天时,系统又会创建一个新的 websocket 对象,这时矛盾出现了:spring 管理的都是单例,不会给第二个 websocket 对象注入 service,所以导致只要是用户连接创建的 websocket 对象,都不能再注入了。
像 controller 里面有 service, service 里面有 dao。因为 controller,service ,dao 都是单例,所以注入时不会报 null。但是 websocket 不是单例,所以使用spring注入一次后,后面的对象就不会再注入了,会报null。
案例2:TokenMapper
复现
写一个工具类,里面用了@Autowired注入了TokenMapper以及TokenService时,在TokenMapper的findFirstById()方法一直报 java.lang.nullpointerexception 异常
解决方案
@Component // 关键1,将该工具类注册为组件 public class TokenUtil { @Autowired private TokenMapper tokenMapper; @Autowired private TokenService tokenService; public static TokenUtil tokenUtil; // 关键2 public TokenUtil() { } // 关键3 @PostConstruct public void init() { tokenUtil = this; tokenUtil.tokenMapper = this.tokenMapper; } // ... }
1. public static TokenUtil tokenUtil; // 注意这个为 public 不然没有权限
分析
静态方法使用静态注入的变量
问题
一般工具类中的方法都是静态的,而在静态方法中又不能直接使用注入的mapper。如:
//注入 @Autowired private SmsLogMapper mapper; //方法 public static String sendSms(String mobile) { //这里不能直接用mapper,因为mapper不是静态 }
如果把mapper改为静态的,则在方法中使用时,mapper就是null。
解决方法
在工具类上添加@Component注解将工具类实例到spring容器中;
使用@PostConstruct注解初始化工具类和mapper,如下:
@Component public class SendSms{ @Autowired private SmsLogMapper mapper; //当前工具类 private static SendSms sendSmsUtil; //解决静态方法中不能直接用mapper的问题 @PostConstruct public void init() { sendSmsUtil = this; sendSmsUtil.mapper = this.mapper; } }
请先
!