Spring использовать разные файлы свойств в зависимости от параметров запроса - PullRequest
1 голос
/ 25 мая 2020

Фон:

Я работаю над микросервисом java Spring REST, который должен работать с несколькими идентичными серверными системами и несколькими идентичными базами данных в зависимости от параметров запроса.

В основном у меня 3 "марки". Для каждого бренда есть набор последующих услуг и база данных. Я не могу их контролировать.

Моя весенняя служба получит бренд как часть запроса, и ей нужно будет вызвать нужные нижестоящие службы и использовать правильную базу данных.

Раньше я имел дело с это за счет наличия отдельного экземпляра весеннего сервиса для каждой из марок. Для каждой марки будет один файл свойств, и Spring будет использовать его для подключения фасоли. У меня были бы отдельные URL-адреса для каждой марки, и проблем не было.

Некоторым из моих bean-компонентов нужно знать о «бренде» во время создания, поскольку они являются оболочкой для подключений нижестоящих сервисов. Т.е., как только компонент будет создан, не будет возможности переключить его на «другой бренд».

Проблема:

Я хотел бы изменить это так, чтобы один экземпляр моей службы мог обрабатывать запросы для любого бренда.

Требования: Я думал о следующем решении:

Иметь общий файл свойств для небрендированных вещей. Spring будет связывать любые небрендированные бобы и хранить их как одиночные бобы.

Имейте файл свойств со спецификациями бренда c URL-адреса и c для каждого бренда Spring создаст набор одноэлементных beans для каждого бренда, используя соответствующий файл свойств.

Затем, когда запрос придет весной, будет читать параметры запроса и использовать bean-атрибуты c для этого бренда.

Производительность важна для меня, поэтому я хотел бы повторно использовать bean-компоненты насколько возможно.

Я хотел бы сделать это как можно более прозрачным, чтобы людям, создающим новые bean-компоненты, не нужно было беспокоиться о том, чтобы делать что-либо за пределами стандартной конфигурации / класса контекста.

Кто-нибудь знает, что бы быть лучшим решением для достижения этой цели?

Ответы [ 2 ]

1 голос
/ 02 июня 2020

Я думаю, вы можете решить проблему, внедряя службу в каждый запрос с правильным набором конфигураций и bean-компонентов; возможно, уже существует в вашем контексте приложения.

Дано:

$ curl http://localhost:8080/greetings/rodo && echo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand1" http://localhost:8080/greetings/rodo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand2" http://localhost:8080/greetings/rodo && echo
Hi from brand2, rodo

Следующий код будет работать:

- application.yml -

brand1:
  greetingPrefix: Hi from brand1,

brand2:
  greetingPrefix: Hi from brand2,

- DemoApplication. java -

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Configuration
    class ServiceConfig {
        @Bean
        public GreetingService greetingServiceBrand1(Brand1Config config) {
            return new GreetingService(config);
        }

        @Bean
        public GreetingService greetingServiceBrand2(Brand2Config config) {
            return new GreetingService(config);
        }
    }

    @Configuration
    class WebConfig implements WebMvcConfigurer {
        @Autowired
        private ApplicationContext applicationContext;

        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(greetingServiceResolver());
        }

        private GreetingServiceResolver greetingServiceResolver() {
            GreetingService greetingServiceBrand1 = applicationContext.getBean("greetingServiceBrand1", GreetingService.class);
            GreetingService greetingServiceBrand2 = applicationContext.getBean("greetingServiceBrand2", GreetingService.class);
            return new GreetingServiceResolver(greetingServiceBrand1, greetingServiceBrand2);
        }
    }
}

@RestController
@RequestMapping("/greetings")
class GreetingController {
    @GetMapping("/{name}")
    public String get(GreetingService greetingService, @PathVariable String name) {
        return greetingService.sayHi(name);
    }
}

class GreetingServiceResolver implements HandlerMethodArgumentResolver {
    private final GreetingService greetingServiceBrand1;
    private final GreetingService greetingServiceBrand2;

    public GreetingServiceResolver(GreetingService greetingServiceBrand1, GreetingService greetingServiceBrand2) {
        this.greetingServiceBrand1 = greetingServiceBrand1;
        this.greetingServiceBrand2 = greetingServiceBrand2;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(GreetingService.class);
    }

    @Override
    public Object resolveArgument(
            MethodParameter methodParameter,
            ModelAndViewContainer modelAndViewContainer,
            NativeWebRequest nativeWebRequest,
            WebDataBinderFactory webDataBinderFactory
    ) throws Exception {
        String brand = nativeWebRequest.getHeader("x-brand-name");
        return resolveGreetingService(brand);
    }

    private GreetingService resolveGreetingService(String brand) {
        if ("brand2".equals(brand)) {
            return greetingServiceBrand2;
        }
        return greetingServiceBrand1; // default
    }
}

class GreetingService {
    private BaseConfig config;

    public GreetingService(BaseConfig config) {
        this.config = config;
    }

    public String sayHi(String name) {
        return config.getGreetingPrefix() + " " + name;
    }
}

abstract class BaseConfig {
    private String greetingPrefix;

    public String getGreetingPrefix() {
        return greetingPrefix;
    }

    public void setGreetingPrefix(String greetingPrefix) {
        this.greetingPrefix = greetingPrefix;
    }
}

@Configuration
@ConfigurationProperties("brand1")
class Brand1Config extends BaseConfig {
}

@Configuration
@ConfigurationProperties("brand2")
class Brand2Config extends BaseConfig {
}

Как видите, очень важно передать службу каждому методу контроллера, напишите распознаватель и вводит правильный набор зависимостей в зависимости от параметра, переданного в запрос, в данном случае через заголовок.

0 голосов
/ 28 мая 2020

Поскольку ваши файлы свойств в любом случае должны быть объявлены статически, вы можете просто записать все свои различные бренды в одном файле свойств, например, в формате «ключ-значение», который Spring может подобрать как список конфигураций.

brandConfigs:
 - brand: foo
   property: foos
 - brand2: bar
   porperty: bars

Загрузите все ваши компоненты подключения к вашим нисходящим сервисам при запуске и просто направьте их в соответствии с вашим параметром запроса. Мне кажется, это самый простой и эффективный способ. Если некоторые из этих нисходящих маршрутов используются очень редко, вы можете лениво загружать bean-компоненты по запросу, но, вероятно, это не будет иметь смысла, если у вас нет тысяч различных нисходящих маршрутов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...