Skip to content

Commit 42e4a04

Browse files
chore: merge main into release [skip ci]
2 parents 4e79663 + 55103a2 commit 42e4a04

3 files changed

Lines changed: 215 additions & 4 deletions

File tree

.github/workflows/wiki-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919

2020
steps:
2121
- name: Checkout source repository
22-
uses: actions/checkout@v4
22+
uses: actions/checkout@v5
2323
with:
2424
fetch-depth: 1
2525

@@ -36,7 +36,7 @@ jobs:
3636
echo "Total pages: $(ls wiki-output/*.md | wc -l)"
3737
3838
- name: Checkout wiki repository
39-
uses: actions/checkout@v4
39+
uses: actions/checkout@v5
4040
with:
4141
repository: ${{ github.repository }}.wiki
4242
path: wiki-repo

microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java

Lines changed: 170 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import java.util.function.Predicate;
3939

4040
import static io.microsphere.collection.CollectionUtils.isEmpty;
41+
import static io.microsphere.collection.CollectionUtils.isNotEmpty;
42+
import static io.microsphere.collection.ListUtils.first;
4143
import static io.microsphere.collection.Lists.ofList;
4244
import static io.microsphere.collection.MapUtils.immutableEntry;
4345
import static io.microsphere.collection.MapUtils.toFixedMap;
@@ -533,7 +535,7 @@ public static boolean isMetaAnnotation(Annotation annotation,
533535
*/
534536
public static boolean isMetaAnnotation(Class<? extends Annotation> annotationType,
535537
Class<? extends Annotation> metaAnnotationType) {
536-
if (annotationType == null || metaAnnotationType == null || NATIVE_ANNOTATION_TYPES.contains(annotationType)) {
538+
if (annotationType == null || metaAnnotationType == null || isNativeAnnotationType(annotationType)) {
537539
return false;
538540
}
539541

@@ -1674,7 +1676,173 @@ public static boolean isCallerSensitivePresent() {
16741676
return nonNull(CALLER_SENSITIVE_ANNOTATION_CLASS);
16751677
}
16761678

1679+
/**
1680+
* Checks whether the specified annotation type is a native annotation type.
1681+
* <p>
1682+
* Native annotation types are those defined by the Java platform itself, such as
1683+
* {@link Target}, {@link Retention}, {@link Documented}, {@link Inherited},
1684+
* {@link Native}, and {@link Repeatable}. These annotations are typically used
1685+
* to define other annotations rather than being applied directly to business logic.
1686+
* </p>
1687+
*
1688+
* <h3>Example Usage</h3>
1689+
* <pre>{@code
1690+
* // Check standard Java meta-annotations
1691+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Target.class)); // true
1692+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Retention.class)); // true
1693+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Documented.class)); // true
1694+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Inherited.class)); // true
1695+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Native.class)); // true
1696+
* System.out.println(AnnotationUtils.isNativeAnnotationType(Repeatable.class)); // true
1697+
*
1698+
* // Check custom user-defined annotations
1699+
* @Target(ElementType.TYPE)
1700+
* @Retention(RetentionPolicy.RUNTIME)
1701+
* public @interface MyCustomAnnotation {}
1702+
*
1703+
* System.out.println(AnnotationUtils.isNativeAnnotationType(MyCustomAnnotation.class)); // false
1704+
*
1705+
* // Handle null input safely
1706+
* System.out.println(AnnotationUtils.isNativeAnnotationType(null)); // false
1707+
* }</pre>
1708+
*
1709+
* @param annotationType the annotation type to check
1710+
* @return {@code true} if the annotation type is a native annotation type; otherwise, {@code false}
1711+
* @see #NATIVE_ANNOTATION_TYPES
1712+
*/
1713+
public static boolean isNativeAnnotationType(Class<? extends Annotation> annotationType) {
1714+
return NATIVE_ANNOTATION_TYPES.contains(annotationType);
1715+
}
1716+
1717+
/**
1718+
* Finds the meta-annotation of the specified type that is directly or indirectly
1719+
* present on the given {@link AnnotatedElement}.
1720+
*
1721+
* <p>This method searches for a meta-annotation of the specified type, considering both
1722+
* direct annotations and meta-annotations (annotations on annotations). It traverses the
1723+
* annotation hierarchy to find the first matching meta-annotation.</p>
1724+
*
1725+
* <h3>Example Usage</h3>
1726+
* <pre>{@code
1727+
* @Target(TYPE)
1728+
* @Retention(RUNTIME)
1729+
* @Inherited
1730+
* @Documented
1731+
* public @interface ServiceMode {
1732+
* }
1733+
*
1734+
* @Inherited
1735+
* @Target(TYPE)
1736+
* @Retention(RUNTIME)
1737+
* @ServiceMode
1738+
* @interface Monitored {
1739+
* }
1740+
*
1741+
* @Inherited
1742+
* @Target(TYPE)
1743+
* @Retention(RUNTIME)
1744+
* @Monitored
1745+
* @interface DataAccess {
1746+
* }
1747+
*
1748+
* @DataAccess
1749+
* class A {
1750+
* }
1751+
*
1752+
* // Find the ServiceMode meta-annotation on class A
1753+
* ServiceMode serviceMode = AnnotationUtils.findMetaAnnotation(A.class, ServiceMode.class);
1754+
* System.out.println(serviceMode); // Outputs: @ServiceMode
1755+
*
1756+
* // Find the Monitored meta-annotation on class A
1757+
* Monitored monitored = AnnotationUtils.findMetaAnnotation(A.class, Monitored.class);
1758+
* System.out.println(monitored); // Outputs: @Monitored
1759+
* }</pre>
1760+
*
1761+
* <p>If either the annotated element or the meta-annotation type is {@code null},
1762+
* this method will return {@code null}.</p>
1763+
*
1764+
* @param annotatedElement the element to search for meta-annotations on
1765+
* @param metaAnnotationType the type of meta-annotation to look for
1766+
* @param <A> the type of the meta-annotation to find
1767+
* @return the first matching meta-annotation of the specified type, or {@code null} if none is found
1768+
*/
1769+
@Nullable
1770+
public static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement, Class<A> metaAnnotationType) {
1771+
return first(findMetaAnnotations(annotatedElement, metaAnnotationType));
1772+
}
1773+
1774+
/**
1775+
* Finds all meta-annotations of the specified type that are directly or indirectly
1776+
* present on the given {@link AnnotatedElement}.
1777+
*
1778+
* <p>This method searches for meta-annotations of the specified type, considering both
1779+
* direct annotations and meta-annotations (annotations on annotations). It traverses the
1780+
* annotation hierarchy to find all matching meta-annotations.</p>
1781+
*
1782+
* <h3>Example Usage</h3>
1783+
* <pre>{@code
1784+
* @Target(TYPE)
1785+
* @Retention(RUNTIME)
1786+
* @Inherited
1787+
* @Documented
1788+
* public @interface ServiceMode {
1789+
* }
1790+
*
1791+
* @Inherited
1792+
* @Target(TYPE)
1793+
* @Retention(RUNTIME)
1794+
* @ServiceMode
1795+
* @interface Monitored {
1796+
* }
1797+
*
1798+
* @Inherited
1799+
* @Target(TYPE)
1800+
* @Retention(RUNTIME)
1801+
* @Monitored
1802+
* @interface DataAccess {
1803+
* }
1804+
*
1805+
* @DataAccess
1806+
* class A {
1807+
* }
1808+
*
1809+
* // Find all ServiceMode meta-annotations on class A
1810+
* List<ServiceMode> serviceModes = AnnotationUtils.findMetaAnnotations(A.class, ServiceMode.class);
1811+
* System.out.println(serviceModes); // Outputs: [@ServiceMode]
1812+
*
1813+
* // Find all Monitored meta-annotations on class A
1814+
* List<Monitored> monitoredList = AnnotationUtils.findMetaAnnotations(A.class, Monitored.class);
1815+
* System.out.println(monitoredList); // Outputs: [@Monitored]
1816+
*
1817+
* // When no meta-annotation is found
1818+
* List<ServiceMode> emptyList = AnnotationUtils.findMetaAnnotations(String.class, ServiceMode.class);
1819+
* System.out.println(emptyList); // Outputs: []
1820+
* }</pre>
1821+
*
1822+
* <p>If either the annotated element or the meta-annotation type is {@code null},
1823+
* this method will return an empty list.</p>
1824+
*
1825+
* @param annotatedElement the element to search for meta-annotations on
1826+
* @param metaAnnotationType the type of meta-annotation to look for
1827+
* @param <A> the type of the meta-annotation to find
1828+
* @return a read-only list of all matching meta-annotations of the specified type, never {@code null}
1829+
*/
1830+
@Nonnull
1831+
@Immutable
1832+
public static <A extends Annotation> List<A> findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> metaAnnotationType) {
1833+
return (List<A>) findDeclaredAnnotations(annotatedElement, annotation -> {
1834+
Class<? extends Annotation> annotationType = annotation.annotationType();
1835+
if (isNativeAnnotationType(annotationType)) {
1836+
return false;
1837+
}
1838+
if (annotationType.equals(metaAnnotationType)) {
1839+
return true;
1840+
}
1841+
return isNotEmpty(findMetaAnnotations(annotationType, metaAnnotationType));
1842+
});
1843+
}
1844+
16771845
private AnnotationUtils() {
16781846
}
16791847

1680-
}
1848+
}

microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
import static io.microsphere.util.AnnotationUtils.findAttributeValue;
6060
import static io.microsphere.util.AnnotationUtils.findAttributesMap;
6161
import static io.microsphere.util.AnnotationUtils.findDeclaredAnnotations;
62+
import static io.microsphere.util.AnnotationUtils.findMetaAnnotation;
63+
import static io.microsphere.util.AnnotationUtils.findMetaAnnotations;
6264
import static io.microsphere.util.AnnotationUtils.getAllDeclaredAnnotations;
6365
import static io.microsphere.util.AnnotationUtils.getAttributeValue;
6466
import static io.microsphere.util.AnnotationUtils.getAttributesMap;
@@ -627,6 +629,39 @@ void testIsCallerSensitivePresent() {
627629
assertEquals(CALLER_SENSITIVE_ANNOTATION_CLASS != null, isCallerSensitivePresent());
628630
}
629631

632+
@Test
633+
void testFindMetaAnnotation() {
634+
assertNotNull(findMetaAnnotation(A.class, Monitored.class));
635+
assertNotNull(findMetaAnnotation(B.class, Monitored.class));
636+
637+
assertNotNull(findMetaAnnotation(A.class, ServiceMode.class));
638+
assertNotNull(findMetaAnnotation(B.class, ServiceMode.class));
639+
640+
assertNotNull(findMetaAnnotation(A.class, Template.class));
641+
assertNotNull(findMetaAnnotation(B.class, Template.class));
642+
643+
assertNull(findMetaAnnotation(AnnotationUtils.class, Monitored.class));
644+
assertNull(findMetaAnnotation(AnnotationUtils.class, ServiceMode.class));
645+
assertNull(findMetaAnnotation(AnnotationUtils.class, Template.class));
646+
}
647+
648+
@Test
649+
void testFindMetaAnnotations() {
650+
assertFindMetaAnnotations(A.class, Monitored.class);
651+
assertFindMetaAnnotations(B.class, Monitored.class);
652+
653+
assertFindMetaAnnotations(A.class, ServiceMode.class);
654+
assertFindMetaAnnotations(B.class, ServiceMode.class);
655+
656+
assertFindMetaAnnotations(A.class, Template.class);
657+
assertFindMetaAnnotations(B.class, Template.class);
658+
}
659+
660+
private void assertFindMetaAnnotations(AnnotatedElement annotatedElement, Class<? extends Annotation> metaAnnotationType) {
661+
List<? extends Annotation> metaAnnotations = findMetaAnnotations(annotatedElement, metaAnnotationType);
662+
assertEquals(1, metaAnnotations.size());
663+
}
664+
630665
private void assertFilterAnnotations(Annotation[] annotations) {
631666
assertEquals(ofList(annotations), filterAnnotations(annotations, annotation -> true));
632667
assertEquals(ofList(annotations), filterAnnotations(ofList(annotations), annotation -> true));
@@ -639,6 +674,14 @@ private void assertFilterAnnotations(Annotation[] annotations) {
639674
@Retention(RUNTIME)
640675
@Inherited
641676
@Documented
677+
public @interface Template {
678+
}
679+
680+
@Target(TYPE)
681+
@Retention(RUNTIME)
682+
@Inherited
683+
@Documented
684+
@Template
642685
public @interface ServiceMode {
643686
}
644687

0 commit comments

Comments
 (0)