All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.excel.aspect.spring.ExcelHelperAround Maven / Gradle / Ivy

There is a newer version: 5.2.0
Show newest version
package gu.sql2java.excel.aspect.spring;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;

import gu.sql2java.BaseBean;
import gu.sql2java.SimpleLog;
import gu.sql2java.excel.ExcelGenerator;
import gu.sql2java.excel.annotations.ExcelSheet;
import gu.sql2java.excel.config.SheetConfig;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicReference;

import javax.servlet.http.HttpServletResponse;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.nullToEmpty;
import static net.gdface.bean.BeanPropertySupport.isEmpty;
import static gu.sql2java.excel.utils.MethodSupport.*;

@Component
public class ExcelHelperAround{
    private static final String SUFFIX_ZIP = ".zip";
    private static final String SUFFIX_XLSX = ".xlsx";
	private static final ThreadLocal sheetConfig = new ThreadLocal<>();
	private static final ThreadLocal forceFlag = new ThreadLocal<>();
	/** 不需要注入的参数名  */
	private static final ImmutableSet unmodifiedFields=ImmutableSet.of("hideColumns","defaultIncludeColumns");

	private static ReturnValueParserDefaultImpl DEFAULT_PARSER = new ReturnValueParserDefaultImpl();

	/** 所有在IOC注册中的{@link ReturnValueParser}接口实例  */
	@Autowired
	private Map returnValueParses = new HashMap<>(); 	
	/**
	 * 返回值类型对应的{@link ReturnValueParser}实例
	 */
	private final LoadingCache, ReturnValueParser> parserCache = 
			CacheBuilder.newBuilder().build(
				new CacheLoader, ReturnValueParser>(){
					@Override
					public ReturnValueParser load(Class key) throws Exception {
						return Iterables.tryFind(returnValueParses.values(), r->r.getReturnType().isAssignableFrom(key)).or(DEFAULT_PARSER);
				}}); 

	/**
	 * ExcelHelper切面执行
* 对于有{@link ExcelSheet}注解的服务方法,自动根据注解创建{@link SheetConfig}实例, * 并将服务方法中与{@link ExcelSheet}注解定义的方法名同名的参数注入到上面的{@link SheetConfig}实例中. * 将服务方法返回的数据记录列表执行{@link #springWebExport(SheetConfig, Integer, String, ExcelGenerator)}方法输出excel格式数据
* 因为需要获取服务方法的参数名,所以需要服务方法所在项目Java编译器如下设置指定{@code -parameters}参数 *
	 * <build>
	 *     <pluginManagement>
	 *         <plugins>
     *             <plugin>
     *                 <groupId>org.apache.maven.plugins</groupId>
     *                 <artifactId>maven-compiler-plugin</artifactId>
     *                 <configuration>
     *                     <compilerArgs>
     *                         <compilerArg>-Xlint:unchecked</compilerArg>
     *                         <compilerArg>-parameters</compilerArg>
     *                     </compilerArgs>
     *                 </configuration>
     *             </plugin>
     *         </plugins>
     *     </pluginManagement>
	 * </build>
	 * 
* @param joinPoint * @return 返回执行服务方法调用的返回值 * @throws Throwable */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Object excelAround(ProceedingJoinPoint joinPoint) throws Throwable { /** 在调用服务方法前清除TLS变量 */ sheetConfig.remove(); forceFlag.remove(); MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); Method method = methodSignature.getMethod(); ExcelSheet excelSheet = method.getAnnotation(ExcelSheet.class); if(null == excelSheet){ /** 未激活ExcelHelper 直接返回原方法 */ return joinPoint.proceed(); } SheetConfig sheetConfig = getSheelConfig(method); AtomicReference getParameter = new AtomicReference(); AtomicReference exportFileName = new AtomicReference<>(); injectParameter(joinPoint, excelSheet, sheetConfig, method, getParameter, exportFileName); ReturnValueParser returnValueParser = parserCache.get(method.getReturnType()); if(null != getParameter.get()){ checkState(!Object.class.equals(sheetConfig.getBeanClass()),"NOT DEFINED sheetConfig.beanClass"); ExcelGenerator generator = new ExcelGenerator(sheetConfig.getBeanClass(), sheetConfig.getIncludeColumnsOrDefault()); Object webValue= springWebExport(sheetConfig, getParameter.get(), exportFileName.get(),generator); /** * 返回 getParameter 数据 */ return returnValueParser.onGetParameter(null, webValue); } Object returnValue = joinPoint.proceed(); if(!returnValueParser.isSuccess(returnValue)){ return returnValue; } final ReturnInfo targetReturnInfo = returnValueParser.parse(new ReturnInfo(returnValue, method)); final Class targetRawReturnType = TypeToken.of(targetReturnInfo.returnType).getRawType(); /** * 根据方法返回类型提取集合内的元素类型 */ final Class elementType; if(BlockingQueue.class.isAssignableFrom(targetRawReturnType)){ Type subType = ((ParameterizedType)targetReturnInfo.returnType).getActualTypeArguments()[0]; Class subRawType = TypeToken.of(subType).getRawType(); if(subRawType.isArray() || Iterable.class.isAssignableFrom(subRawType)){ elementType = extractElementType(targetRawReturnType,subType); }else { elementType = extractElementType(targetRawReturnType,targetReturnInfo.returnType); } }else { elementType = extractElementType(targetRawReturnType,targetReturnInfo.returnType); } if(Map.class.isAssignableFrom(elementType)){ }else if(BaseBean.class.isAssignableFrom(elementType)){ }else if(SheetConfig.mayBeJavaBean(elementType)){ }else { throw new IllegalArgumentException(String.format("UNSUPPORTED element type %s",elementType.getName())); } try { ExcelGenerator generator = new ExcelGenerator(targetReturnInfo.returnValue, elementType, sheetConfig.getIncludeColumnsOrDefault()); springWebExport(sheetConfig, getParameter.get(), exportFileName.get(),generator); return null; } catch (Exception e) { SimpleLog.log(e); returnValueParser.onError(returnValue, e); return returnValue; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private Class extractElementType(Class targetRawReturnType,Type returnType){ if(targetRawReturnType.isArray()){ return targetRawReturnType.getComponentType(); }else if(Iterable.class.isAssignableFrom(targetRawReturnType)){ /** * 使用TypeToken.getSuperType方法比直接通过 ParameterizedType.getActualTypeArguments * 获取元素类型有更好的适应性,可以支持从类似 JSONArray这样的非ParameterizedType类型提取元素类型 * */ TypeToken typeToken = TypeToken.of(returnType); TypeToken superType = typeToken.getSupertype(Iterable.class); return TypeToken.of(((ParameterizedType)superType.getType()).getActualTypeArguments()[0]) .getRawType(); }else { throw new IllegalArgumentException(String.format("UNSUPPORTED RETURN TYPE %s",targetRawReturnType.getName())); } } private SheetConfig getSheelConfig(Method method){ SheetConfig sheetConfig; if(null != ExcelHelperAround.sheetConfig.get()){ if(Boolean.TRUE.equals(ExcelHelperAround.forceFlag.get())){ sheetConfig = ExcelHelperAround.sheetConfig.get(); }else { /** * 如果调用 setSheetConfig,或setBeanClass方法定义了 SheetConfig, * 则将基于从服务方法注解生成的SheetConfig合并此对象 */ sheetConfig = new SheetConfig(method).merge(ExcelHelperAround.sheetConfig.get()); } }else { sheetConfig = new SheetConfig(method);/** 服务方法注解生成的Excel配置对象 */ } ExcelHelperAround.sheetConfig.set(sheetConfig); return sheetConfig; } /** * 为Spring AOP处理当前Excel导出指定的Excel配置对象 * @param sheetConfig * @param force 为{@code true}强制替换ExcelGenerator生成的{@link SheetConfig}对象 */ public static void setSheetconfig(SheetConfig sheetConfig, boolean force) { ExcelHelperAround.sheetConfig.set(sheetConfig); ExcelHelperAround.forceFlag.set(force); } /** * (非强制)为Spring AOP处理当前Excel导出指定的Excel配置对象 * @see #setSheetconfig(SheetConfig, boolean) */ public static void setSheetconfig(SheetConfig sheetConfig) { setSheetconfig(sheetConfig,false); } /** * (非强制)为Spring AOP处理当前Excel导出指定的阻塞队列超时时间(秒) * @param queueTimeout */ public static void setQueueTimeout(int queueTimeout){ getSheetconfig().setQueueTimeout(queueTimeout); } /** * (非强制)为Spring AOP处理当前Excel导出指定的阻塞队列记录总数 * @param totalRowCount */ public static void setTotalRowCount(long totalRowCount){ getSheetconfig().setTotalRowCount(totalRowCount); } /** * 返回为Spring AOP处理当前Excel导出的Excel配置对象 * @since 3.25.4 */ public static SheetConfig getSheetconfig(){ if(null == sheetConfig.get()){ sheetConfig.set(new SheetConfig()); } return sheetConfig.get(); } /** * 导出{@link ExcelGenerator}实例的数据记录生成Excel格式的数据输出到HTTP Response
* {@code getParameter}为{@code null}时,输出Excel数据, Response Content Type需要设置为{@code application/octet-stream} * {@code getParameter}不为{@code null}时,根据{@code getParameter}的值返回JSON格式的结果,Response Content Type需要设置为{@code application/json} * @param sheetConfig * @param getParameter Response Content Type需要设置为{@code application/json} *
    *
  • 1 返回所有可选的字段列表,参见{@link SheetConfig#getAvailableColumns()}
  • *
  • 2 返回默认输出的字段名及字段显示名称,参见{@link SheetConfig#getDefaultExportColumns()}
  • *
  • 3 返回默认输出的字段名,参见{@link SheetConfig#getDefaultExportColumnNames()}
  • *
* @param exportFileName 指定导出的excel文件名和格式后缀,不指定文件名则自动以日期命名, * 支持的输出格式后缀为.csv,xlsx,如果要输出zip压缩格式,在输出格式后缀增加.zip,如.csv.zip即输出,压缩的.csv文件, * 如果没有指定输出格式后缀默认输出不压缩的.xlsx. * @return {@code getParameter}不为{@code null}时返回数据,否则返回 {@code null} * @throws IOException */ private static Object springWebExport( SheetConfig sheetConfig, Integer getParameter,String exportFileName,ExcelGenerator generator) throws IOException { if(Boolean.TRUE.equals(ExcelHelperAround.forceFlag.get())){ /** forceFlag 为true时强制使用 TLS变量存储的对象 */ generator.setSheetConfig(ExcelHelperAround.sheetConfig.get()); }else { generator.getSheetConfig().merge(sheetConfig); if(null != ExcelHelperAround.sheetConfig.get()){ generator.getSheetConfig().merge(ExcelHelperAround.sheetConfig.get()); } } RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); checkState(requestAttributes instanceof ServletRequestAttributes,"ServletRequestAttributes is required"); HttpServletResponse response= ((ServletRequestAttributes)requestAttributes).getResponse(); if(null != getParameter){ response.setContentType(MediaType.APPLICATION_JSON_VALUE); Object returnValue; switch(getParameter){ case 1: /** 返回所有可输出字段及字段显示名称 */ returnValue = generator.getSheetConfig().getAvailableColumns(); break; case 2: /** 返回默认输出的字段名及字段显示名称 */ returnValue = generator.getSheetConfig().getDefaultExportColumns(); break; /** 返回默认输出的字段名 */ case 3: returnValue = generator.getSheetConfig().getDefaultExportColumnNames(); break; default: throw new IllegalArgumentException(String.format("INVALID getParameter %d",getParameter)); } return returnValue; } /** 执行EXCEL导出流程 */ response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); String formatSuffix = SUFFIX_XLSX; boolean zip = false; /** 设置导出文件名 */ exportFileName = nullToEmpty(exportFileName).trim(); if(exportFileName.isEmpty()){ exportFileName = makeDefaultExportFileName(sheetConfig); }else { if(exportFileName.endsWith(SUFFIX_ZIP)){ zip = true; exportFileName = exportFileName.substring(0,exportFileName.length() - SUFFIX_ZIP.length()); if(exportFileName.isEmpty()){ exportFileName = makeDefaultExportFileName(sheetConfig); } } int lastDot = exportFileName.lastIndexOf('.'); if(lastDot > 0){ formatSuffix = exportFileName.substring(lastDot); exportFileName = exportFileName.substring(0, lastDot); }else if(0 == lastDot){ formatSuffix = exportFileName; exportFileName = makeDefaultExportFileName(sheetConfig); } } response.setHeader("Content-Disposition", "attachment; filename=" + exportFileName + formatSuffix + (zip ? SUFFIX_ZIP: "")); generator.generate(response,formatSuffix, zip ? exportFileName + formatSuffix : null); return null; } private static String makeDefaultExportFileName(SheetConfig sheetConfig){ String prefix = firstNonNull(sheetConfig.getFileNamePrefix(),sheetConfig.getSheetName()); SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HHmmss"); String currentDateTime = dateFormatter.format(new Date()); return prefix + currentDateTime ; } /** * 将Spring Controller服务方法中与{@link ExcelSheet}注解定义的方法名同名的参数注入到{@link SheetConfig}实例中. * @param joinPoint * @param excelSheet * @param sheetConfig * @param method * @param getParameter [out]服务方法中的getParameter参数 * @param exportFileName [out]服务方法中的exportFileName参数 * @throws Exception */ private static void injectParameter(ProceedingJoinPoint joinPoint ,ExcelSheet excelSheet,SheetConfig sheetConfig,Method method,AtomicReference getParameter,AtomicReference exportFileName) throws Exception{ List methodNames = methodNamesOf(excelSheet); Parameter[] parameters = method.getParameters(); Object[] args = joinPoint.getArgs(); /** 将服务接口方法中的与 ExcelSheet 注解中字段名匹配的所有参数值注入sheetConfig */ for(int i = 0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy