diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java index 1b04ebdcf..7ffdb0fb0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java @@ -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 implements AnnotationSynt @SuppressWarnings("unchecked") @Override public A synthesize(Class 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; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java index e4c8bc94c..5ef49f1ec 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java @@ -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; } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java index ab7ae9ad6..c104856d7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java @@ -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; /** *

带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现, @@ -47,16 +47,19 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes @Override public T getAttributeValue(String attributeName, Class attributeType, Collection 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; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java index 233d84940..60545f9c1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java @@ -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)); } // ========================= 代理方法 ========================= diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java b/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java new file mode 100644 index 000000000..04c2a9267 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/TestIssueI8CLBJ.java @@ -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 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 ""; + } +}