在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()传入类名

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐