抽象类实现中的自动连线对象上的 NullPointerException



我的代码有一个奇怪的问题。 我已经实现了template method pattern,以提供同一藻类的不同实现。 所以我创建了抽象类:

@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
}
}

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

@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. 如果我protectedupdateRdi一切正常,但注释需要公共方法@Transactional否则它不起作用
  2. _If我publicfirstTwoRow对象上收到的updateRdinullPointerExceptionsheetenv变量也是如此。 你知道我为什么会有这种行为以及如何解决这个问题吗?非常感谢

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

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

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

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

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

最新更新