在Spring框架中,我们通常将bean对象交给spring容器,然后使用注解的方式引入,不用手动的去创建类。
如在service类上加@Service,在controller类上加@Controller,通过@Autowired注解引入调用bean对象的方法
service类:
public interface ISupplierService {
public SupplierBase getSupplier(Long id);
}
@Service
public class SupplierServiceImpl implements ISupplierService {
@Autowired
private ISupplierDao supplierDao;
@Override
public SupplierBase getSupplier(Long id) {
return supplierDao.getSupplier(id);
}
}
controller类:
@Controller
@RequestMapping(value="/supplier")
public class SupplierManagementAction{
@Autowired
private ISupplierService service;
@RequestMapping(value="/supplierIndex")
public String supplierIndex(ModelMap modelMap, Long supplierId) throws Exception{
if(null!=id && id.trim().length()>0 ){
SupplierBase supplierBase = service.getSupplierByRegionType(supplierId);
if(null!=supplierBase){
modelMap.put("supplierBase", supplierBase);
}
}
return "supplier/supplierIndex";
}
}
但是,最近在项目中遇到这种情况:
导出excel数据,首先根据条件查询符合要求的数据,然后在Controller中使用new的方式得到普通类,最后将数据传到普通类中,专门处理excel构建。部分代码如下:
controller类:
@Controller
@RequestMapping(value="/sproduct")
public class SupplierProductManagementAction{
@RequestMapping(value="/exportExcel", method=RequestMethod.POST)
public ModelAndView exportExcel(@ModelAttribute PmSupplierBase productFromBean,ModelMap model,HttpSession session) {
int pageIndex = 1;
if(null!=productFromBean.getBatchNum()){
pageIndex = productFromBean.getBatchNum().intValue();
}
productFromBean.setBusinessType(super.getBusinessType());
productFromBean.setExportExcel(true);
Page<PmSupplierBase> supplierList = productService.queryPmSupplierProduct(productFromBean, excelMaxHang, pageIndex,getBty());
SProductExportExcel productExcel = new SProductExportExcel();
return new ModelAndView(productExcel,"pmData",supplierList.getResults());
}
}
用于处理生成excel的普通类:
public class SProductExportExcel extends AbstractExcelView {
@Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Date currDate = new Date();
HSSFSheet sheet = workbook.createSheet("PSupplier导出(" + DateUtils.dateFormatToString(currDate, "yyyy-MM-dd") + ")");
HSSFRow sheetRow = sheet.createRow(0);
int index = 0;
HSSFCellStyle colorStyle = workbook.createCellStyle();
colorStyle.setFillForegroundColor(HSSFColor.YELLOW.index);
colorStyle.setFillBackgroundColor(HSSFColor.YELLOW.index);
colorStyle.setFillPattern(HSSFColor.YELLOW.index);
HSSFCell cell = sheetRow.createCell(index++);
cell.setCellStyle(colorStyle);
cell.setCellValue("产品编码");
cell = sheetRow.createCell(index++);
cell.setCellStyle(colorStyle);
cell.setCellValue("产品名称");
@SuppressWarnings("unchecked")
List<PmSupplierBase> dataList = (List<PmSupplierBase>)model.get("pmData");
int num = null==dataList?0:dataList.size();
List<CommonTypeBase> noPrList = null;
if(num >= 1){
noPrList = (List<CommonTypeBase>)dataList.get(0).getNoPurchaseReasonList();
}
String[] noPrArray = StringUtil.ListToArray(noPrList, "typeName");
Map<String,String> noPrMap = (Map<String,String>)StringUtil.ListToMap(noPrList, "typeValue", "typeName");
DVConstraint noPrConstraint = DVConstraint.createExplicitListConstraint(noPrArray);
//四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList noPrRegions = new CellRangeAddressList(1,num,30,30);
//数据有效性对象
HSSFDataValidation noPrDataValidation = new HSSFDataValidation(noPrRegions, noPrConstraint);
sheet.addValidationData(noPrDataValidation);
PmSupplierBase base = null;
for (int i = 0; i < num; i++) {
base = dataList.get(i);
sheetRow = sheet.createRow(i+1);
if(null==base)
continue;
index = 0;
sheetRow.createCell(index++).setCellValue(null==base.getProductNo()?"":base.getProductNo());
sheetRow.createCell(index++).setCellValue(null==base.getSupplierProductName()?"":base.getSupplierProductName());
}
String filename = "PSupplier" + DateUtils.dateFormatToString(currDate, "yyyyMMddHHmmss") + ".xls";//设置下载时客户端Excel的名称
OutputStream ouputStream = null;
try {
filename = HelpUtil.encodeFilename(filename, request);//处理中文文件名
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + filename);
ouputStream = response.getOutputStream();
workbook.write(ouputStream);
ouputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(null!=ouputStream){
ouputStream.close();
}
}
}
}
现在excel中有一列数据需要调用service类中的方法查询将结果展示,然后同事A就直接在SProductExportExcel 类中通过注解直接引入
public class SProductExportExcel extends AbstractExcelView {
/**同事A添加的*/
@Autowired
IMerwareConstantService merwareConstantService;
@Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//....
//....
//同事A添加的一行代码
String merchantName = merwareConstantService.getMNameByMId(base.getMerchantId());
}
}
结果我拉取代码测试导出,后台直接报错:java.lang.NullPointException
最开始怀疑是数据的问题,在获取数据计算的时候未判断空导致的这个异常,但是debug发现是
merwareConstantService.getMNameByMId(base.getMerchantId());
这行代码merwareConstantService为空导致的。
分析代码:
如果想用@autowired,那么这个类本身也应该是在spring的管理下,即SProductExportExcel 也要标注为一个component(或Service),这样spring才知道要注入依赖。
而在controller中通过new的方式获取的SProductExportExcel,并没有交给spring管理,通过注解引入方式获取不到spring的bean对象
那么问题的关键就在于获取merwareConstantService的方式。
而获取spring的bean对象,还可以通过ApplicationContext.getBean()。
解决方案如下:
创建一个工具类,用于获取bean对象
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
/**
* 适用于springbean使用注解@Service("XXXService")
* 获取接口对象 参数传入 XXXService
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return applicationContext.getBean(beanName);
}
/**
* 适用于springbean使用注解@Service
* 获取接口对象 参数传入 XXXService.class 不是 XXXServiceImpl.class
* @param c
* @return
*/
public static Object getBean(Class c){
return applicationContext.getBean(c);
}
}
然后将此bean加到spring容器中,applicationContext.xml中添加:
<bean id="springContextUtil" class="com.yao.purchasing.business.impl.SpringContextUtil"></bean>
在SProductExportExcel中就通过这个工具类获取service:
public class SProductExportExcel extends AbstractExcelView {
/**同事A添加的*/
//@Autowired
//IMerwareConstantService merwareConstantService;
@Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//更改新的获取方式
ApplicationContext context = SpringContextUtil.getApplicationContext();
IMerwareConstantService merwareConstantService = (IMerwareConstantService)context.getBean(IMerwareConstantService.class);
//....
//....
//同事A添加的一行代码
String merchantName = merwareConstantService.getMNameByMId(base.getMerchantId());
}
}
再次测试导出,成功。
有一点要注意下:
IMerwareConstantService merwareConstantService = (IMerwareConstantService)context.getBean(IMerwareConstantService.class);
刚开始我传入的是类名
context.getBean(“IMerwareConstantService”);
仍然获取不到service,网上查资料发现getBean()具体传入”类名”还是 类名.class是有区别的,要根据自己的service的注解来定。
如:
@Service
public class MerwareConstantServiceImpl implements IMerwareConstantService{}
那么context.getBean()传入类名.class
@Service(“IMerwareConstantService”)
public class MerwareConstantServiceImpl implements IMerwareConstantService{}
那么context.getBean()传入类名