运用了Aop的概念—Aop加强的使用
//范围
@Target(ElementType.METHOD)
//生存周期
@Retention(RetentionPolicy.RUNTIME)
//类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解-允许子类继承父类中的注解
@Inherited
//指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值
@Documented
public @interface MultiDataSourceTransactional {
/**
* 事务管理器数组
* @return
*/
String[] transactionManagers();
}
@Component
@Aspect
public class MultiDataSourceTransactionAspect {
/**
* 线程本地变量->栈->为了达到后进先出的效果
*/
private static final ThreadLocal<Stack<Map<DataSourceTransactionManager, TransactionStatus>>> THREAD_LOCAL = new ThreadLocal<>();
/**
* 事务管理器
*/
@Autowired
private ApplicationContext applicationContext;
private DefaultTransactionDefinition definition =new DefaultTransactionDefinition();
{
//非只读模式
definition.setReadOnly(false);
//事务隔离:数据库的
definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
//传播行为
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
/**
* 切面
*/
@Pointcut("@annotation(com.learnta.dms.service.annotation.MultiDataSourceTransactional)")
public void pointcut(){ }
/**
* 声明事务
*/
@Before("pointcut() && @annotation(transactional)")
public void before(MultiDataSourceTransactional transactional){
// 根据设置的事务名称按顺序声明,并放到ThreadLocal里
String[] transactionManagerNames = transactional.transactionManagers();
Stack<Map<DataSourceTransactionManager, TransactionStatus>> pairStack = new Stack<>();
for (String transactionManagerName : transactionManagerNames) {
DataSourceTransactionManager transactionManager = applicationContext.getBean(transactionManagerName, DataSourceTransactionManager.class);
TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
Map<DataSourceTransactionManager, TransactionStatus> transactionMap = new HashMap<>();
transactionMap.put(transactionManager, transactionStatus);
pairStack.push(transactionMap);
}
THREAD_LOCAL.set(pairStack);
}
/**
* 提交事务
*/
@AfterReturning("pointcut()")
public void afterReturning(){
//栈顶弹出(后进先出)
Stack<Map<DataSourceTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
while (CollectionUtils.isNotEmpty(pairStack)) {
Map<DataSourceTransactionManager, TransactionStatus> pair = pairStack.pop();
pair.forEach((key,value)->key.commit(value));
}
THREAD_LOCAL.remove();
}
/**
* 回滚事务
*/
@AfterThrowing(value = "pointcut()")
public void afterThrowing(){
// ※栈顶弹出(后进先出)
Stack<Map<DataSourceTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
while (CollectionUtils.isNotEmpty(pairStack)) {
Map<DataSourceTransactionManager, TransactionStatus> pair = pairStack.pop();
pair.forEach((key, value) -> key.rollback(value));
}
THREAD_LOCAL.remove();
}
}
@MultiDataSourceTransactional(transactionManagers = {"defaultTransactionManager", "dmsTransactionManager"})
1.方法
/**
* 用于对象去重
* @param keyExtractor 需要去重的属性
* @param
* @return
*/
public static <T> Predicate<T> distinctByKeyExtractor(Function<? super T, ?> keyExtractor) {
//记录已有对象或者属性
ConcurrentSkipListMap<Object,Boolean> skipListMap = new ConcurrentSkipListMap<>();
//获取对象的属性值,且使用putIfAbsent判断存在则不添加到map而且返回数值不存在则添加返回null,value恒定为true
//JSONObject.toJSONString(keyExtractor.apply(t)) 是为了解决null参数和对象比较的问题
//在Stream distinct()中使用了支持null为key的hashSet来进行处理 java/util/stream/DistinctOps.java:90 但是没有解决对象比较的问题
//所以虽然序列化消耗性能但是也没有更好的办法
Predicate<T> predicate = t -> skipListMap.putIfAbsent(JSONObject.toJSONString(keyExtractor.apply(t)), Boolean.TRUE) == null;
return predicate;
}
2.用法
grades.stream().filter(CommonUtil.distinctByKeyExtractor(grade->Stream.of(grade.getGrade(),grade.getGradeName()).toArray())).collect(Collectors.toList())
/**
* 图片转换base64
* @param imageUrl 图片路径-可以自己拼接,也可以直接使用网络图片
* @return
*/
public static String encodeImageToBase64(String imageUrl) {
Long startImage = System.currentTimeMillis();
if (StringUtils.isBlank(imageUrl)) return null;
ByteArrayOutputStream outputStream = null;
try {
URL url = new URL(imageUrl);
BufferedImage bufferedImage = ImageIO.read(url);
outputStream = new ByteArrayOutputStream();
//png 是支持我自己服务器的格式 可以自己修改,其他格式我服务器会IO异常
ImageIO.write(bufferedImage,"png",outputStream);
} catch (Exception e) {
logger.error("picture transformation error exception={}", e);
//异常捕捉,是否需要返回看业务
//throw new ServerRuntimeException(ErrorStatus.IMAGE_ERROR);
}
BASE64Encoder encoder = new BASE64Encoder();
String imageBase64= encoder.encode(outputStream.toByteArray());
//去除转换过程中产生的换行
imageBase64= imageBase64.replaceAll("\r|\n", "");
Long endImage = System.currentTimeMillis();
logger.info("image time check={}",endImage-startImage);
return imageBase64;
}
/**
* base64转换file 七牛入参-我自己本身是为了继续调用七牛方便,本功能用于OCR识别中的图片转换给七牛存储起来
* @param base64
* @return
*/
public static File base64ToFile(String base64) {
if(base64==null||"".equals(base64)) {
return null;
}
Long fileStart = System.currentTimeMillis();
File file=null;
FileOutputStream out = null;
try {
byte[] buff=new BASE64Decoder().decodeBuffer(base64);
file = File.createTempFile("tmp", null);
out=new FileOutputStream(file);
out.write(buff);
} catch (IOException e) {
throw new ServerRuntimeException(ErrorStatus.FILE_ERROR);
}finally {
if(out!=null) {
try {
out.close();
} catch (IOException e) {
throw new ServerRuntimeException(ErrorStatus.FILE_ERROR);
}
}
}
Long fileEnd = System.currentTimeMillis();
logger.info("file time check={}",fileEnd-fileStart);
return file;
}
比较特殊的一种方式处理-适用于postman的form-data形式
httpclient的post请求
EntityUtils.toString()解析httpClient形式的entity
public static String getHash(File file,String token)throws Exception{
Long uploadStart = System.currentTimeMillis();
HttpClient httpclient = new DefaultHttpClient();
//QINIUURL 是我本地调取七牛云的地址url 配置在了yml里面
HttpPost httpPost = new HttpPost(QINIUURL);
//入参构建
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody("file",file);
builder.addTextBody("token",token);
HttpEntity entity = builder.build();
httpPost.setEntity(entity);
HttpResponse httpResponse = httpclient.execute(httpPost);
String hash = EntityUtils.toString(httpResponse.getEntity());
//我自己是解析截取-也可以用json toArray方式
hash = hash.substring(hash.indexOf("key")+6,hash.indexOf("}")-1);
Long uploadEnd = System.currentTimeMillis();
logger.info("qi niu hash={}",hash);
logger.info("upload time check={}",uploadEnd-uploadStart);
return hash;
后续持续更新–