fix 修复合成注解在并发环境无法保证正确缓存属性值的问题 Gitee#I8CLBJ

This commit is contained in:
huangchengxing 2023-11-02 18:28:15 +08:00
parent 40067cda4a
commit 5e264d0be8
5 changed files with 99 additions and 20 deletions

View File

@ -4,13 +4,13 @@ import cn.hutool.core.annotation.scanner.AnnotationScanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
/**
* {@link AnnotationSynthesizer}的基本实现
@ -158,11 +158,19 @@ public abstract class AbstractAnnotationSynthesizer<T> implements AnnotationSynt
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A synthesize(Class<A> annotationType) {
return (A)synthesizedProxyAnnotations.computeIfAbsent(annotationType, type -> {
final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType);
return ObjectUtil.isNull(synthesizedAnnotation) ?
null : synthesize(annotationType, synthesizedAnnotation);
});
A annotation = (A)synthesizedProxyAnnotations.get(annotationType);
if (Objects.nonNull(annotation)) {
return annotation;
}
synchronized (synthesizedProxyAnnotations) {
annotation = (A)synthesizedProxyAnnotations.get(annotationType);
if (Objects.isNull(annotation)) {
final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType);
annotation = synthesize(annotationType, synthesizedAnnotation);
synthesizedProxyAnnotations.put(annotationType, annotation);
}
}
return annotation;
}
}

View File

@ -14,7 +14,7 @@ import java.lang.reflect.Method;
*/
public class CacheableAnnotationAttribute implements AnnotationAttribute {
private boolean valueInvoked;
private volatile boolean valueInvoked;
private Object value;
private boolean defaultValueInvoked;
@ -45,8 +45,12 @@ public class CacheableAnnotationAttribute implements AnnotationAttribute {
@Override
public Object getValue() {
if (!valueInvoked) {
valueInvoked = true;
value = ReflectUtil.invoke(annotation, attribute);
synchronized (this) {
if (!valueInvoked) {
valueInvoked = true;
value = ReflectUtil.invoke(annotation, attribute);
}
}
}
return value;
}

View File

@ -3,10 +3,10 @@ package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.multi.RowKeyTable;
import cn.hutool.core.map.multi.Table;
import cn.hutool.core.util.ObjectUtil;
import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
/**
* <p>带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现
@ -47,16 +47,19 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes
@Override
public <T> T getAttributeValue(String attributeName, Class<T> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations) {
Object value = valueCaches.get(attributeName, attributeType);
// 此处理论上不可能出现缓存值为nul的情况
if (ObjectUtil.isNotNull(value)) {
return (T)value;
if (Objects.isNull(value)) {
synchronized (valueCaches) {
value = valueCaches.get(attributeName, attributeType);
if (Objects.isNull(value)) {
value = synthesizedAnnotations.stream()
.filter(ma -> ma.hasAttribute(attributeName, attributeType))
.min(annotationComparator)
.map(ma -> ma.getAttributeValue(attributeName))
.orElse(null);
valueCaches.put(attributeName, attributeType, value);
}
}
}
value = synthesizedAnnotations.stream()
.filter(ma -> ma.hasAttribute(attributeName, attributeType))
.min(annotationComparator)
.map(ma -> ma.getAttributeValue(attributeName))
.orElse(null);
valueCaches.put(attributeName, attributeType, value);
return (T)value;
}
}

View File

@ -95,7 +95,7 @@ public class SynthesizedAnnotationProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return Opt.ofNullable(methods.get(method.getName()))
.map(m -> m.apply(method, args))
.orElseGet(() -> ReflectUtil.invoke(this, method, args));
.orElseGet(() -> ReflectUtil.invoke(proxy, method, args));
}
// ========================= 代理方法 =========================

View File

@ -0,0 +1,64 @@
package cn.hutool.core.annotation;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* @author huangchengxing
*/
public class TestIssueI8CLBJ {
@Test
public void test() throws NoSuchFieldException {
Field field = Foo.class.getDeclaredField("name");
Assert.assertNotNull(field);
Annotation[] annotations = field.getDeclaredAnnotations();
Assert.assertTrue(annotations.length > 0);
TestAnnotation annotation = AnnotationUtil.getSynthesizedAnnotation(TestAnnotation.class, annotations);
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 30; i++) {
Thread thread = new Thread(() -> {
try {
String valueFieldName = annotation.valueFieldName();
System.out.println("valueFieldName:" + valueFieldName);
} catch (Exception e) {
e.printStackTrace();
}
});
threadList.add(thread);
thread.start();
}
try {
for (Thread thread : threadList) {
thread.join();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static class Foo {
private Integer id;
@TestAnnotation("name")
private String name;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
private @interface TestAnnotation {
String value() default "";
@Alias("value")
String valueFieldName() default "";
}
}