要在Spring Boot中動態加入注解,可以使用Java的動態代理技術和反射機制。以下是一個示例代碼,演示如何在運行時動態加入注解:
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Set;
public class DynamicAnnotationExample {
public static void main(String[] args) {
// 獲取所有帶有@Controller注解的類
Set<Class<?>> controllerClasses = getClassesWithAnnotation(Controller.class);
// 動態加入@GetMapping注解到所有方法上
for (Class<?> controllerClass : controllerClasses) {
addGetMappingAnnotation(controllerClass);
}
}
private static Set<Class<?>> getClassesWithAnnotation(Class<? extends Annotation> annotationClass) {
Set<Class<?>> classes = new HashSet<>();
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
for (BeanDefinition beanDefinition : provider.findCandidateComponents("com.example")) {
try {
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
classes.add(clazz);
} catch (Exception e) {
e.printStackTrace();
}
}
return classes;
}
private static void addGetMappingAnnotation(Class<?> controllerClass) {
Object controllerProxy = Proxy.newProxyInstance(
controllerClass.getClassLoader(),
new Class[]{controllerClass},
(proxy, method, args) -> {
// 動態加入@GetMapping注解到方法上
AnnotationMetadata annotationMetadata = new AnnotationMetadata() {
@Override
public Set<String> getAnnotationTypes() {
return Set.of(GetMapping.class.getName());
}
@Override
public Set<String> getMetaAnnotationTypes(String annotationName) {
return null;
}
@Override
public boolean hasAnnotation(String annotationName) {
return GetMapping.class.getName().equals(annotationName);
}
@Override
public boolean hasMetaAnnotation(String metaAnnotationName) {
return false;
}
@Override
public boolean isAnnotated(String annotationName) {
return GetMapping.class.getName().equals(annotationName);
}
@Override
public Map<String, Object> getAnnotationAttributes(String annotationName) {
return Map.of("value", new String[]{"/dynamic"});
}
@Override
public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
return null;
}
};
Method proxyMethod = Proxy.class.getDeclaredMethod("proxyMethod", Method.class);
proxyMethod.setAccessible(true);
Method realMethod = (Method) proxyMethod.invoke(proxy, method);
Method dynamicMethod = new DynamicMethod(realMethod, annotationMetadata);
return dynamicMethod.invoke(proxy, args);
});
// 替換原有的Controller Bean
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
String beanName = generator.generateBeanName(controllerClass, null);
SpringContextHolder.replaceBean(controllerClass, beanName, controllerProxy);
}
// 動態代理中用于替換原有方法的動態方法
private static class DynamicMethod extends Method {
private final AnnotationMetadata annotationMetadata;
public DynamicMethod(Method method, AnnotationMetadata annotationMetadata) {
super(
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers(),
method.getDeclaringClass(),
method.getDefaultValue()
);
this.annotationMetadata = annotationMetadata;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return annotationMetadata.isAnnotated(annotationClass.getName()) ? annotationMetadata.getAnnotationAttributes(annotationClass.getName()) : super.getAnnotation(annotationClass);
}
@Override
public Annotation[] getAnnotations() {
Annotation[] annotations = super.getAnnotations();
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(Controller.class.getName());
List<Annotation> result = new ArrayList<>(Arrays.asList(annotations));
result.add(createGetMappingAnnotation(attributes));
return result.toArray(new Annotation[0]);
}
private Annotation createGetMappingAnnotation(Map<String, Object> attributes) {
return new GetMapping() {
@Override
public Class<? extends Annotation> annotationType() {
return GetMapping.class;
}
@Override
public String[] value() {