Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
gu.sql2java.excel.aspect.spring.ExcelHelperAround Maven / Gradle / Ivy
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