在Spring Boot中动态加载URI
通常,我们会在Spring Controller和处理方法中硬编码URI,但你是否考虑过从数据库中动态加载URI并在运行时更新它?我们可以从数据库中加载URI,并在任何Spring Controller中配置它。
为此,选择任何你想存储URI的数据库。
为了实现这一点,我们需要一个服务来从数据库加载所有URI并缓存它们,以便我们可以在处理方法中配置它们。
我创建了一个ApiCache服务来从数据库加载所有URI。
@Component
@Getter
@Setter
@DependsOn("dbInitialize")
public class ApiCache {
@Autowired
private DynamicUriRepository dynamicUriRepository;
private Map<String, String> cacheMap = new HashMap<>();
public String getApi(String apiName) {
return cacheMap.get(apiName);
}
@PostConstruct
public void init() {
List<DynamicUri> dynamicUris = dynamicUriRepository.findAll();
cacheMap.putAll(dynamicUris.stream()
.collect(Collectors.toMap(DynamicUri::getUriName, DynamicUri::getUriPath)));
}
}
**init() PostConstruct
**将从数据库加载所有URI并缓存它们。
**getApi()**方法将通过传入的URI名称从映射中返回URI路径。
为了演示,我创建了另一个名为DbInitialize的服务来在数据库中输入uri_name和uri_path,这里不详细说明,你可以自己管理。
以下是域模型:
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Entity
public class DynamicUri {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String uriName;
private String uriPath;
}
以下是我们的URI将被加载的控制器:
@RestController
public class DemoController {
@GetMapping("#{apiCache.getApi('DEMO-API')}")
public String demo() {
return "Hello from Spring Boot!";
}
}
“#{apiCache.getApi(‘DEMO-API’)}” 是一种spEL表达式,它将调用getApi()方法,并传入参数“DEMO-API”。有关更多spEL信息,请参考这里。
现在进入下一部分:我们如何在运行时更新URI?
为此,我在UriController中创建了一个端点来处理更新请求。
@RestController
public class UriController {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
@PutMapping(value = "/uri/update")
public Object updateUri(@RequestBody UriUpdateRequest uriUpdateRequest) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods =
requestMappingHandlerMapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
RequestMappingInfo requestMappingInfo = entry.getKey();
HandlerMethod handlerMethod = entry.getValue();
for (String patternValue : requestMappingInfo.getPatternValues()) {
if (patternValue.equals(uriUpdateRequest.getOldUri())) {
RequestMethod requestMethod =
requestMappingInfo.getMethodsCondition().getMethods().stream().findFirst().get();
if (requestMethod.name().equals(uriUpdateRequest.getHttpMethod())) {
RequestMappingInfo requestMappingInfoToRegister =
RequestMappingInfo.paths(uriUpdateRequest.getNewUri())
.methods(requestMethod).build();
Object handler = handlerMethod.getBean();
requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
requestMappingHandlerMapping.registerMapping(
requestMappingInfoToRegister, handler, handlerMethod.getMethod());
}
}
}
}
return "URI updated successfully";
}
}
为了演示,我在控制器中编写了所有逻辑。
RequestMappingHandlerMapping是一个处理应用程序中所有URI的类。
getHandlerMethods()方法返回一个映射,其中包含RequestMappingInfo和HandlerMethod。
RequestMappingInfo顾名思义,存储有关URI的所有信息。
HandlerMethod顾名思义,存储有关处理方法和bean的信息。
代码解释: 这里,我们使用RequestMappingHandlerMapping
检索应用程序中的所有可用处理方法。然后,我们定位与oldUri
关联的特定RequestMappingInfo
并根据请求的HTTP方法进行过滤以更新相应的URI。
注意:可能会有相同的URI与不同的HTTP方法关联。例如,POST /orders和GET /orders
当通过根据URI和HTTP方法进行过滤定位到所需的RequestMappingInfo
时,将使用新的URI和HTTP方法创建一个新的RequestMappingInfo
对象。然后将该方法的关联处理器bean存储在一个对象handler中。
Object handler = handlerMethod.getBean();
RequestMappingInfo requestMappingInfoToRegister =
RequestMappingInfo.paths(uriUpdateRequest.getNewUri())
.methods(requestMethod).build();
然后我们注销旧的RequestMappingInfo
并注册新的:
requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
requestMappingHandlerMapping.registerMapping(requestMappingInfoToRegister, handler,
handlerMethod.getMethod());