运行时修改SolrDocument注解后查询仍用旧集合名问题求助
我正在使用Spring Data Solr,定义了如下Solr文档模型类TestDocument和对应的SolrCrudRepository:
@SolrDocument(collection = "oldCollectionName") public class TestDocument { @Id @Indexed(name = "id", type = "string") private String id; @Field(value = "name") private String name; @Field(value = "externalid") private Integer externalId; }
我想在运行时修改该类上的@SolrDocument(collection = "oldCollectionName")注解值,于是写了Service方法,通过反射修改注解后日志显示注解已设为新集合名,但调用testDocumentRepository的查询方法时,仍使用旧集合名。
Service方法代码:
public List<TestDocument> getDocumentsByName(String name){ String newSolrDocument = getModifiedSolrCollectionName(); alterAnnotationValue(TestDocument.class, SolrDocument.class, newSolrDocument); SolrDocument solrDocument = TestDocument.class.getAnnotation(SolrDocument.class); LOGGER.info("Dynamically set SolrDocument Annotaation: "+solrDocument.collection()); return testDocumentRepository.findByName(name); }
修改注解的工具方法:
public void alterAnnotationValue(Class<?> targetClass, Class<? extends Annotation> targetAnnotation, String newCollectionName) { try { Method method = Class.class.getDeclaredMethod("getAnnotationData", null); method.setAccessible(true); Object annotationData = method.invoke(targetClass); Field annotations = annotationData.getClass().getDeclaredField("annotations"); annotations.setAccessible(true); Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData); // 重新创建SolrDocument注解实例 SolrDocument newAnnotation = new SolrDocument() { @Override public Class<? extends Annotation> annotationType() { return SolrDocument.class; } @Override public String collection() { return newCollectionName; } @Override public String solrCore() { return ""; // 保留原注解默认值或按需设置 } }; map.put(targetAnnotation, newAnnotation); } catch (Exception e) { e.printStackTrace(); } }
(注:修正了原工具方法中参数类型不匹配的问题,需创建有效的注解实例而非直接传入字符串)
现在的问题是:反射修改类注解后,Repository查询仍使用旧集合名,请问还需做什么才能让修改生效?
嘿,这个坑我之前也踩过——你遇到的是Spring Data框架的初始化缓存机制在搞鬼!
为什么反射修改注解不生效?
Spring Data Solr在应用启动阶段,就已经扫描解析了@SolrDocument的注解信息,把集合名绑定到了SolrCrudRepository的实现类中,并且这个配置是被缓存起来的。你后续通过反射修改Class对象上的注解元数据,已经初始化好的Repository实例根本不会再去读取这个更新后的注解,所以查询还是会用旧的集合名。
可行的解决思路
你需要绕过Spring Data的静态缓存,动态指定集合名执行查询,这里给你三个实用方案:
方案1:直接用SolrTemplate动态指定集合名
SolrTemplate支持手动指定查询的集合,完全不依赖模型类上的注解,是最直接的解决方式:
@Autowired private SolrTemplate solrTemplate; public List<TestDocument> getDocumentsByName(String name){ String newCollectionName = getModifiedSolrCollectionName(); // 构造查询条件 SolrQuery query = new SolrQuery("name:" + name); // 手动指定集合名执行查询 return solrTemplate.queryForList(newCollectionName, query, TestDocument.class); }
方案2:动态创建Repository实例
如果你还是想用Repository的自定义查询方法(比如findByName),可以通过SolrRepositoryFactory动态创建新的Repository实例,每次都指定最新的集合名:
@Autowired private SolrClient solrClient; public List<TestDocument> getDocumentsByName(String name){ String newCollectionName = getModifiedSolrCollectionName(); // 基于新集合名创建SolrTemplate SolrTemplate dynamicSolrTemplate = new SolrTemplate(solrClient, newCollectionName); // 动态生成Repository实例 SolrRepositoryFactory factory = new SolrRepositoryFactory(dynamicSolrTemplate); TestDocumentRepository dynamicRepo = factory.getRepository(TestDocumentRepository.class); return dynamicRepo.findByName(name); }
这种方式每次都会生成全新的Repository实例,完全避开了原有实例的缓存问题。
方案3:刷新Spring Bean上下文(不推荐)
如果非要让原有Repository实例生效,你需要触发Spring上下文刷新,重新初始化所有相关Bean,但这种方式会影响应用性能,还可能引发其他依赖问题,不建议在生产环境使用。
额外提醒
你原有的注解修改工具方法存在参数类型错误,注解是接口,必须通过匿名内部类实现所有方法才能创建有效的注解实例,我已经在代码中帮你修正了这一点。
内容的提问来源于stack exchange,提问作者user3388770

