@Autowired注入失效问题

背景:

在编写EasyExcel示例代码时,执行程序报NullPointerException,经排查发现是自定义的ExcelListener监听器中注入UserService失败导致。

还有一种情况是因为包没被扫到导致。Spring Boot项目的Bean装配默认规则是根据Application类(指项目入口类)所在的包位置从上往下扫描,该情况大家自行了解,本文不做赘述。

解决方案:

方案1:@Autowired(推荐)

调用方:

@Autowired
private ExcelListener excelListener;
EasyExcel.read(excel.getInputStream(), User.class, excelListener).sheet().doRead();

被调用方:

@Component
public class ExcelListener extends AnalysisEventListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelListener.class);

    @Autowired
    private UserService userService;

调用方通过Spring注入的方式自动注入ExcelListener ,而非new的方式,这样获取到的ExcelListener是被Spring容器管理的,进而可以在ExcelListener中通过@Autowired的方式成功获取到同样被Spring容器管理的UserService,因为UserService 的实现类中有"@Service"注解标记。
另外要记得被调用的类要加上"@Component"注解,不然该类依然不会被Spring容器管理,类中Autowired的属性仍将注入失败。

方案2:构造器注入

调用方:

EasyExcel.read(excel.getInputStream(), User.class, new ExcelListener()).sheet().doRead();

被调用方:

@Component
public class ExcelListener extends AnalysisEventListener {

    private static UserService userService;

    public ExcelListener() {
    }

    @Autowired
    public ExcelListener(UserService userService) {
        this.userService = userService;
    }

构造器注入的方式解决了当调用方调用一个未被Spring容器管理的类时的问题。
可以看到调用方是通过"new ExcelListener()"的方式调用的ExcelListener,该方式ExcelListener类是未被Spring容器管理的,通过构造器注入的方式可以解决该问题,但需注意的是"@Autowired"是写在有参构造器上的,而非属性,且私有属性应为"static"静态的,不然会注入失败。

方案3:创建上下文工具类获取SpringIOC容器中的类

定义工具类:

package com.lee.demo.easyexcel.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @ClassName: ApplicationContextUtil
 * @Author: Jimmy
 * @Date: 2020/1/15 14:43
 * @Description: 上下文工具类,用于获取SpringIOC容器中的类
 */
@Component
public class ApplicationContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    /**
     * 获取ApplicationContext上下文
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (ApplicationContextUtil.applicationContext == null) {
            ApplicationContextUtil.applicationContext = applicationContext;
        }
    }

    /**
     * 通过名称获取实体类
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
}

通过工具类获取实体:

public void saveData() {
    UserService userService = (UserServiceImpl) ApplicationContextUtil.getBean("userServiceImpl");
    userService.saveBatch(this.list,BATCH_COUNT);
}

常用的@Controller、@Service、@Repository、@Component都表示将Bean交给Spring管理,所以Spring的ApplicationContext肯定是有实现类UserServiceImpl的,通过定义的getBean()方法获取即可,参数即要获取的类的名称,注意首字母要小写。

参考:https://www.jianshu.com/p/f3c67ca457e6

你可能感兴趣的:(Spring,spring)