java - 抽象类实现内的 Autowired 对象出现 NullPointerException

标签 java spring design-patterns abstract autowired

我的代码有一个奇怪的问题。 我已经实现了模板方法模式来提供同一算法的不同实现。 所以我创建了抽象类:

@Service
public abstract class ExcelRdi_Iupr_Sl {
    @Autowired
    private Environment env;

    private static String PROPERTY_NAME_FILESYSTEM_EXCELPATH = "temporary.excelpath";
    private XSSFWorkbook workbook; 
    private XSSFCellStyle unlockedNumericStyle;
    private XSSFSheet sheet;

    /**
     * Create the excel file with the RDI,IUPR or SL
     * @param car
     * @param fileName
     * @return
     * @throws Exception
     */
    public final String retrieve(Car car, String fileName) throws Exception{
        writeInitialize();
        Map<Integer,Integer> defMap = firstTwoRow(car, sheet);
        elaboration(car, sheet, unlockedNumericStyle, defMap);
        return writeFile(car, fileName);
    }

    /**
     * Import the excel file with the RDI,IUPR or SL
     * @param file
     * @param car
     * @throws Exception
     */
    public final int update(MultipartFile file, Car car) throws Exception{
        String filePath = saveFile(file, car);
        readInitialize(filePath);
        return updateRdi(car, sheet);
    }

    /**
     * Save the imported file into file system inside a temporary folder
     * @param file
     * @param car
     * @return
     * @throws Exception
     */
    private String saveFile(MultipartFile multipartFile, Car car) throws Exception {
        String path = env.getRequiredProperty(PROPERTY_NAME_FILESYSTEM_EXCELPATH) + File.separator + String.valueOf(System.currentTimeMillis());
        File file = new File (path+ File.separator + multipartFile.getOriginalFilename());
        file.getParentFile().mkdirs();
        multipartFile.transferTo(file);
        return file.getAbsolutePath();
    }

    /**
     * Initialize the object for the creating procedure
     */
    protected void writeInitialize(){
        //initialize class variable
    }

    /**
     * Initialize the object for the reading procedure
     * @param filePath
     * @throws Exception
     */
    protected void readInitialize(String filePath) throws Exception{
        //read ile and initialize class variable

    }

    /**
     * Write the created file inside the file system
     * @param car
     * @param fileName
     * @return
     * @throws Exception
     */
    private String writeFile(Car car, String fileName) throws Exception{        
        //write on file
    }

    /**
     * This excel creating method has to be implemented by the concrete class
     * @param car
     * @param sheet
     * @throws Exception
     */
    protected abstract Map<Integer,Integer> firstTwoRow(Car car, XSSFSheet sheet) throws Exception;

    /**
     * This excel creating method has to be implemented by the concrete class
     * @param car
     * @param sheet
     * @param defMap
     * @throws Exception
     */
    protected abstract void elaboration(Car car, XSSFSheet sheet, XSSFCellStyle unlockedNumericStyle, Map<Integer,Integer> defMap);

    /**
     * This excel import method has to be implemented by the concrete class
     * @param car
     * @param sheet
     * @return number of elaborated row
     * @throws Exception
     */
    protected abstract int updateRdi(Car car, XSSFSheet sheet) throws Exception;
}

扩展上述类的类之一具有以下实现:

@Component
public class ExcelRdi extends ExcelRdi_Iupr_Sl {
    @Autowired
    private RdiServices rdiServices;
    @Autowired
    private AcquisitionServices acquisitionServices;
    static final Logger LOG = LoggerFactory.getLogger(ExcelRdi.class);

    /**
     * Create the first two row of excel file
     */
    @Override
    protected Map<Integer,Integer> firstTwoRow(Car car, XSSFSheet sheet) throws Exception {
        //code
    }

    /**
     * Create the row with RDI for excel file
     */
    @Override
    protected void elaboration(Car car, XSSFSheet sheet, XSSFCellStyle unlockedNumericStyle, Map<Integer,Integer> defRdiMap) {
        //code
    }

    /**
     * Import the Excel file
     * @throws Exception 
     */
    @Override
    @Transactional(rollbackFor= Exception.class)
    protected int updateRdi(Car car, XSSFSheet sheet) throws Exception{
        //COde
    }
}

因此,从通过 Controller 接收请求的服务中,我有一个简单的开关:

@Autowired
private ExcelRdi excelRdi;
@Autowired
private ExcelIupr excelIupr;
@Autowired
private ExcelSl excelSl;

@Override
    public String getExcel(Car car, String type) throws Exception {
        String fileName = type + "$"+car.getFleet().getFleetName().getFleetName() +"$"+ car.getFleet().getApplication()+"$"+ car.getCarType().getIdCarType()+car.getId() +"$"+car.getIdCar()+".xlsx";                   
        switch (type){
        case "RDI": return excelRdi.retrieve(car, fileName);
        case "IUPR": return excelIupr.retrieve(car, fileName);
        case "SL": return excelSl.retrieve(car, fileName);
        default: throw new FileFormatException("You can create only RDI, SL or IUPR file!");
        }
    }

    @Override
    public int importExcel(MultipartFile file, Car car, String type) throws Exception {
        //Check if the file has the correct name before import
        String fileName = type + "$"+car.getFleet().getFleetName().getFleetName() +"$"+ car.getFleet().getApplication()+"$"+ car.getCarType().getIdCarType()+car.getId() +"$"+car.getIdCar()+".xlsx";                   
        if (!file.getOriginalFilename().equals(fileName))
            throw new FileFormatException("The file is not for this car");
        switch (type){
        case "RDI": return excelRdi.update(file, car);
        case "IUPR": return excelIupr.update(file, car);
        case "SL": return excelSl.update(file, car);
        default: throw new FileFormatException("You can create only RDI, SL or IUPR file!");
        }

    }

这就是问题所在:

  1. 如果我将 protected 设置为 updateRdi 一切正常,但 @Transactional 注释需要公共(public)方法,否则它将不起作用
  2. _如果我将 updateRdi 设为 public,我会在 firstTwoRowsheet 对象上收到 nullPointerException code> 和 env 变量相同。 你知道我为什么会有这种行为以及如何解决这个问题吗?非常感谢

最佳答案

问题在于您的 receiveupdate 方法被标记为 final。此外,如果您观察日志记录,您将看到一条警告,指出无法代理 final 方法。

由于没有接口(interface),因此将创建一个基于类的代理。此代理将为所有 public 方法添加行为(例如 @Transactional)。它通过重写实际方法、应用建议并调用非代理实例(驻留在代理内部)上的方法来实现此目的。

但是,由于 final 性质,这种情况不会发生,并且该方法将在代理上调用,而不是在代理内的实际对象上调用。由于代理不是 Spring bean,所有 @Autowired 字段都是 null,因此您的 NullPointerException

如果您在 update 方法中放置断点,您可以观察到此行为,您将在调试器中看到所有实例字段均为 null 并且类名类似于 YOurClass$CgLibProxy。还有一个字段 target (如果我没记错的话),其中包含注入(inject)了依赖项的实际 Spring bean。

问题的解决方案实际上非常简单,只需从方法中删除 final 即可。

关于java - 抽象类实现内的 Autowired 对象出现 NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48344538/

相关文章:

java - 正确使用 TransportClient 进行 Elasticsearch

java - 减去数组中的元素

java - 从另一个类文件运行 Java .class 文件(更具体)

java - 如何使用Hibernate缓存表数据并加速查询?

haskell - 计算期间在环境中隐式携带 STRef

java - 如何搭建仿真架构,OO设计

design-patterns - 访客vs仆人vs命令模式

java - 尝试使用 itext :Exception in thread "main" java. lang.NoClassDefFoundError 签署 pdf 时出错:org/bouncycaSTLe/cert/X509CertificateHolder

java - 无法在 Apple MacPro 上启动新的 Android 应用程序

java - 无法将组件 Autowiring 到实用程序类中