本文最后更新于365 天前,其中的信息可能已经过时,如有错误请发送邮件到[email protected]
问题出现
问题描述:在Spring Boot环境下使用策略模式,调用 Spring 的 Bean 出现了空指针异常。
// 这是一个很经典的策略模式实现,但是如果这么些就一定会出现上面所述BUG
@Service
public class BussinessServiceImpl implements BussinessService {
private final Map<Integer, Strategy> strategyMap;
@Autowired
public BussinessServiceImpl() {
this.searchMap = new HashMap<>();
// 添加具体的策略实现类
searchMap.put(1, new ConcreteStrategy1());
searchMap.put(2, new ConcreteStrategy2());
}
@Override
public void search(Integer type, DTO dto) {
Strategy strategy = strategyMap.get(type);
if (ObjUtil.isNull(strategy)) {
return;
}
strategy.options(dto);
}
}
/**
* 在抽象策略,尽量使用抽象类,不要使用接口,原因在于,有些方法可以提取到父类中使用
*/
@Component
public abstract class Strategy {
@Resource
private BussinessMapper bussinessMapper; // 这里的bussinessMapper一定为空
// 抽象的业务方法
public abstract void options(DTO dto);
// 公共的方法,这里如果是接口就没办法做这种实现了
protected void commonMethod(){
bussinessMapper.do();
}
}
/**
* 具体策略实现类
*/
@Component
public class ConcreteStrategy1 extends Strategy {
@Resource
private BussinessMapper bussinessMapper; // 这里的bussinessMapper一定为空
@Override
public void options(DTO dto) {
// do something
bussinessMapper.do();
}
}
解决方案
问题原因
问题就出现在BussinessServiceImpl中,在构造方法中创建了具体策略的实现类,但是new的类并不在Spring的Bean管理中,因此这些类中调用Spring的Bean一定会出现空指针异常。
方案一:使用 @Autowired 注入 Strategy 的实现类
@Service
@Slf4j
public class BussinessServiceImpl implements BussinessService {
private final Map<Integer, Strategy> strategyMap;
@Autowired
public BussinessServiceImpl(ConcreteStrategy1 concreteStrategy1, ConcreteStrategy2 concreteStrategy2) {
this.strategyMap = new HashMap<>();
strategyMap.put(1, concreteStrategy1);
strategyMap.put(2, concreteStrategy2);
}
}
方案二:使用 @PostConstruct 初始化 strategyMap
@Service
@Slf4j
public class BussinessServiceImpl implements BussinessService {
private final Map<Integer, Strategy> strategyMap;
@Autowired
private ConcreteStrategy1 concreteStrategy1;
@Autowired
private ConcreteStrategy2 concreteStrategy2;
@Autowired
public BussinessServiceImpl() {
this.strategyMap = new HashMap<>();
}
@PostConstruct
public void init() {
strategyMap.put(1, concreteStrategy1);
strategyMap.put(2, concreteStrategy2);
}
}
方案三:使用 @Component 和 @Qualifier
@Service
@Slf4j
public class BussinessServiceImpl implements BussinessService {
private final Map<Integer, Strategy> strategyMap;
@Autowired
public BussinessServiceImpl(@Qualifier("concreteStrategy1") ConcreteStrategy1 concreteStrategy1,
@Qualifier("concreteStrategy2") ConcreteStrategy2 concreteStrategy2) {
this.strategyMap = new HashMap<>();
strategyMap.put(1, concreteStrategy1);
strategyMap.put(2, concreteStrategy2);
}
}