先聊一下什么是代理模式?
代理模式 给某个对象提供一个代理对象,并由代理独享控制对原对象的引用。什么意思呢?代理模式就有点像我们生活中常见的中介。
举个例子,我想买辆二手车,第一种方式是自己去找车源,但是像质量检测等一系列的车辆过户流程都需要自己去办,我觉得这样太浪费精力和时间了。于是我想到了第二种方式,就是找一个中介公司,他们代替我办理过户流程,我只需要负责选择自己喜欢的汽车,然后付钱就行了,这就简单很多了。
画个UML图如下。
前面提到了我们选择中介是为了省事,其实在代码中使用代理模式也可以省事,因为不只有我这个对象会去找这个中介,其他需要买二手车的人也需要去找这个中介,那这个中介的作用就做到了重用了。
下面说在程序中为什么要使用代理模式
- 中介隔离:在某些情况下,一个客户不想或不能直接引用委托对象。
- 开闭原则,增加功能:当代码需要增加业务功能时,我们只需要修改代理类就行,符合开闭原则。
代理模式分为静态代理和动态代理。我们今天着重讲静态代理
下面进入猎杀时刻!!!!
使用静态代理模式实现公用的导出功能
先来一张设计图
先看代理接口
public interface ReportWithExcelTempAble<T> {/*** 获得报表数据* @param searchParam* @return*/default List<List<?>> getMultipleSheetData(T searchParam) {ArrayList<List<?>> result = Lists.newArrayList();result.add(getSingleSheetData(searchParam));return result;}default List<?> getSingleSheetData(T searchParam) {throw new RuntimeException("Not support method.");}Class<T> getSearchParamType();/*** 需要导出的文件名* @return*/default String getReportFileName(String tempName) {return tempName;}
}
getMultipleSheetData
、getReportFileName
都有默认的实现方法,因为大部分都是一个sheet的报表。
再看看代理类,代理主要是代理了报表在导出中的一系列操作,比如获取导出模板,设置导出名称等等。
package com.sf.esg.occp.core.common.support.report;import com.alibaba.fastjson.JSONObject;
import com.sf.erui.context.SpringContextUtil;
import com.sf.erui.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.List;public class ReportProxy implements ReportWithExcelTempAble<JSONObject> {private static Logger LOG = LoggerFactory.getLogger(ReportProxy.class);/*** 模板名称*/private String tempName;/*** 报表名称*/private String reportFileName;private ReportWithExcelTempAble delegateInstance;private ReportProxy() {}public String getReportFileName() {return reportFileName;}public static <T> ReportProxy of(String delegateName) {ReportProxy director = new ReportProxy();director.tempName = delegateName;director.delegateInstance = SpringContextUtil.getBean(StringUtil.toLowerCaseFirstOne(director.tempName), ReportWithExcelTempAble.class);director.reportFileName = director.delegateInstance.getReportFileName(director.tempName);return director;}@Overridepublic List<List<?>> getMultipleSheetData(JSONObject searchParam) {long start = System.currentTimeMillis();Object searchObj = JSONObject.toJavaObject(searchParam, delegateInstance.getSearchParamType());LOG.debug("组装数据费时:{}", System.currentTimeMillis() - start);return delegateInstance.getMultipleSheetData(searchObj);}@Overridepublic Class getSearchParamType() {return delegateInstance.getSearchParamType();}}
再来看看委托类:
public class BankTransactionExport implements ReportWithExcelTempAble<QueryBankTransactionVO> {@Autowiredprivate BankTransactionService bankTransactionService;@Overridepublic List<List<?>> getMultipleSheetData(QueryBankTransactionVO searchParam) {List<List<?>> lists = bankTransactionService.exportMultipleSheet(searchParam);return lists;}@Overridepublic Class<QueryBankTransactionVO> getSearchParamType() {return QueryBankTransactionVO.class;}@Overridepublic String getReportFileName(String tempName) {SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");String dateStr = format.format(new Date());return "SF银行流水认领明细" + dateStr;}
}
Controller层:
@RestController
@RequestMapping("/report/excel")
public class ReportController {public static final String FILE_SUFFIX = ".xlsx";public static final String TEMP_BASE_PATH = "temp/";public static final String PAGE_SIZE = "pageSize";public static final String CURRENT_PAGE = "currentPage";public static final int PAGE_SIZE_VALUE = 1000;private static Logger LOG = LoggerFactory.getLogger(ReportController.class);@GetMapping(params = {"Action=exportExcelByTemp"})public Response exportExcelByTemp(@RequestParam Map<String, Object> searchParam, HttpServletResponse resp) {LOG.info("Export report by temp:{}", searchParam);String tempName = (String) searchParam.get("tempName");try {ReportProxy reportDirector = ReportProxy.of(tempName);String tempPath = TEMP_BASE_PATH + tempName + FILE_SUFFIX;String fileName = URLEncoder.encode(reportDirector.getReportFileName(), "UTF-8");resp.setHeader("Content-Disposition", "filename=" + fileName + FILE_SUFFIX);resp.setContentType("APPLICATION/OCTET-STREAM");try (InputStream ins = new ClassPathResource(tempPath).getInputStream()) {ExcelWriterBuilder writeBuilder = EasyExcel.write(resp.getOutputStream()).withTemplate(ins);ExcelWriter writer = writeBuilder.autoCloseStream(false).registerConverter(new LocalDateTimeConverter()).build();JSONObject jsonObject = new JSONObject(searchParam);List<List<?>> data = reportDirector.getMultipleSheetData(jsonObject);ExcelUtils.pageExportByTemp(writer, data, tempPath);writer.finish();} catch (IOException e) {LOG.error("Excel Export Err : ", e);}return ResponseHelper.buildOk();} catch (IOException e) {LOG.error("Response get output stream err:{}", e);return ResponseHelper.buildFail("请联系管理员");}}
}
在前端使用get请求调用:/report/excel?Action=exportExcelByTemp&tempName="a.xmls"
最后别忘了,添加a.xmls在项目的 temp/
目录下,我用的Spring boot,对应代码中的目录如下