San*_*eep 5 java spring spring-mvc spring-boot spring-webflux
我们在内部公司框架中想要的东西之一是 Spring Boot 应用程序中基于文件路径或包的路由。幸运的是,在 java 中,文件和包是相同的......所以两者都可以。
例如,我想强制org.company.api从其包名称中选取下面的所有包。org.company.api.user所以will be/user和org.company.api.user.loginwill be中的控制器/user/login。注意:记住org.company因项目而异。基本上我们希望 api 目录(相当于 api 子包)下的所有内容都用于此目的。
我想我可以使用 webflux 路由器功能来实现这一点,但我不确定是否有更好的方法。如何为目录下的所有文件生成路由器功能?
如果我正确理解您的问题,您希望自动向每个控制器方法的 URL 添加路径前缀,并且路径前缀应从控制器的包名称派生。
您可以通过以下方式做到这一点,但它需要使用作为输入参数和输出来RouterFunction实现控制器方法(即HandlerFunction) 。因此,如果你想将控制器方法实现为普通的java方法,可以使用任何类型作为输入和输出,你需要实现一些代码来在 / 之间转换为任何输入和输出类型,以适应ServerRequestServerResponseServerRequestServerResponseHandlerFunction可以调用任何普通的方法。然后,您最终会发现您正在重新发明 spring-mvc 带注释的控制器方法已经完成的事情。
实际上 spring-mvc 已经允许使用自定义控制器方法的路径前缀PathMatchConfigurer(请参阅此处的文档)。但使用PathMatchConfigurer是相当静态的,需要您手动维护所有路径前缀。因此,为了更加动态地根据控制器的包自动配置所有路径前缀,我们需要另一种方法来做到这一点。
在幕后PathMatchConfigurer所做的只是在实例化之前配置pathPrefixes内部。RequestMappingHandlerMapping因此,我们需要一种在实例化之前配置它的方法RequestMappingHandlerMapping,但在我们有足够的信息来了解所有路径前缀之后,我们需要配置在我们知道 spring 将创建哪些控制器 bean 之后发生的情况。
BeanFactoryPostProcessor正是提供了这样的钩子,它允许在实例化任何bean之前但在BeanDefinition收集所有bean元数据(即)之后执行代码。所以我们可以实现一个BeanFactoryPostProcessor它的工作是配置这个pathPrefixes地图RequestMappingHandlerMapping通过找出包含控制器的所有包来
以下内容BeanFactoryPostProcessor应该是您的一个很好的起点。
public class PathPrefixFactoryPostProcessor implements BeanFactoryPostProcessor {
private final String rootPackage;
public FooBeanFactoryPostProcessor(String rootPackage) {
this.rootPackage = rootPackage;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
List<String> controllerPackageNames = findPackageContainController(beanFactory);
Map<String, Predicate<Class<?>>> pathPrefixesMap = new HashMap<>();
for (String packageName : controllerPackageNames) {
if (packageName.startsWith(rootPackage)) {
String pathPrefix = packageName.substring(rootPackage.length());
pathPrefix = pathPrefix.replace(".", "/");
pathPrefixesMap.put(pathPrefix, HandlerTypePredicate.forBasePackage(packageName));
}
}
if(!pathPrefixesMap.isEmpty()){
BeanDefinition def = beanFactory.getBeanDefinition("requestMappingHandlerMapping");
def.getPropertyValues().add("pathPrefixes", pathPrefixesMap);
}
}
private List<String> findPackageContainController(ConfigurableListableBeanFactory beanFactory) {
List<String> result = new ArrayList<>();
for (String bdName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = beanFactory.getBeanDefinition(bdName);
Class<?> type = bd.getResolvableType().resolve();
if (type != null) {
if (AnnotatedElementUtils.hasAnnotation(type, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(type, RequestMapping.class)) {
result.add(type.getPackageName());
}
}
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
RequestMappingHandlerMappingbean具有名称requestMappingHandlerMapping(默认情况下应该如此)org.company.api,则不会添加路径前缀。然后将其定义为 bean :
@Configuraiton
public class Config {
@Bean
public PathPrefixFactoryPostProcessor pathPrefixFactoryPostProcessor(){
return new PathPrefixFactoryPostProcessor("org.company.api");
}
}
Run Code Online (Sandbox Code Playgroud)
然后给定两个包org.company.api.user并且org.company.api.user.login包包含以下控制器
@RestController
public class FooController {
@GetMapping("/foo")
public String foo(){
}
}
Run Code Online (Sandbox Code Playgroud)
被foo()映射到
/user/foo对于包裹org.company.api.user/user/login/foo对于包裹org.company.api.user.login| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |