스프링 - 프로그래밍 방식으로 콩 세트 생성
구성 목록의 각 구성에 대해 12개 정도의 콩을 생성해야 하는 Dropwizard 애플리케이션이 있습니다.건강검진이나 석영 스케쥴러 같은 것들.
이와 같은 것:
@Component
class MyModule {
@Inject
private MyConfiguration configuration;
@Bean
@Lazy
public QuartzModule quartzModule() {
return new QuartzModule(quartzConfiguration());
}
@Bean
@Lazy
public QuartzConfiguration quartzConfiguration() {
return this.configuration.getQuartzConfiguration();
}
@Bean
@Lazy
public HealthCheck healthCheck() throws SchedulerException {
return this.quartzModule().quartzHealthCheck();
}
}
나는 이런 콩이 필요한 MyConfiguration의 인스턴스를 여러 개 가지고 있습니다.지금은 이러한 정의를 복사하여 붙여넣고 새 구성마다 이름을 변경해야 합니다.
어떻게든 구성 클래스를 반복하고 각 클래스에 대한 일련의 빈 정의를 생성할 수 있습니까?
새로운 서비스를 추가해야 할 때마다 동일한 코드를 복사하여 붙여넣고 메소드 이름을 바꾸게 하지 않아도 서브클래싱 솔루션이나 안전한 타입이면 됩니다.
편집: 이 콩에 의존하는 다른 성분이 있다는 것을 추가해야 합니다(주사를 맞음).Collection<HealthCheck>
예를 들어.)
따라서 새로운 콩을 즉시 선언하고 Spring의 애플리케이션 컨텍스트에 단순히 일반 콩인 것처럼 주입해야 합니다. 즉, 프록싱, 후처리 등의 절차를 거쳐야 합니다. 즉, Spring beans 라이프사이클을 거쳐야 합니다.
method javadocs 를 참조해주세요.이것이 바로 필요한 것입니다. 이는 일반적인 빈 정의가 로드된 후 단 하나의 빈이 인스턴스화되기 전에 Spring의 응용 프로그램 컨텍스트를 수정할 수 있기 때문입니다.
@Configuration
public class ConfigLoader implements BeanDefinitionRegistryPostProcessor {
private final List<String> configurations;
public ConfigLoader() {
this.configurations = new LinkedList<>();
// TODO Get names of different configurations, just the names!
// i.e. You could manually read from some config file
// or scan classpath by yourself to find classes
// that implement MyConfiguration interface.
// (You can even hardcode config names to start seeing how this works)
// Important: you can't autowire anything yet,
// because Spring has not instantiated any bean so far!
for (String readConfigurationName : readConfigurationNames) {
this.configurations.add(readConfigurationName);
}
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// iterate over your configurations and create the beans definitions it needs
for (String configName : this.configurations) {
this.quartzConfiguration(configName, registry);
this.quartzModule(configName, registry);
this.healthCheck(configName, registry);
// etc.
}
}
private void quartzConfiguration(String configName, BeanDefinitionRegistry registry) throws BeansException {
String beanName = configName + "_QuartzConfiguration";
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzConfiguration.class).setLazyInit(true);
// TODO Add what the bean needs to be properly initialized
// i.e. constructor arguments, properties, shutdown methods, etc
// BeanDefinitionBuilder let's you add whatever you need
// Now add the bean definition with given bean name
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
private void quartzModule(String configName, BeanDefinitionRegistry registry) throws BeansException {
String beanName = configName + "_QuartzModule";
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzModule.class).setLazyInit(true);
builder.addConstructorArgReference(configName + "_QuartzConfiguration"); // quartz configuration bean as constructor argument
// Now add the bean definition with given bean name
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
private void healthCheck(String configName, BeanDefinitionRegistry registry) throws BeansException {
String beanName = configName + "_HealthCheck";
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HealthCheck.class).setLazyInit(true);
// TODO Add what the bean needs to be properly initialized
// i.e. constructor arguments, properties, shutdown methods, etc
// BeanDefinitionBuilder let's you add whatever you need
// Now add the bean definition with given bean name
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
// And so on for other beans...
}
이를 통해 필요한 콩을 효과적으로 선언하고 각 구성별로 한 세트의 콩 세트인 Spring의 애플리케이션 컨텍스트에 콩을 주입할 수 있습니다.어떤 작명 패턴에 의존한 다음 필요한 곳에 이름을 붙여 콩을 자동으로 재배해야 합니다.
@Service
public class MyService {
@Resource(name="config1_QuartzConfiguration")
private QuartzConfiguration config1_QuartzConfiguration;
@Resource(name="config1_QuartzModule")
private QuartzModule config1_QuartzModule;
@Resource(name="config1_HealthCheck")
private HealthCheck config1_HealthCheck;
...
}
주의:
파일에서 수동으로 구성 이름을 읽는 방법으로 이동하는 경우 스프링을 사용합니다.
클래스 경로를 직접 스캔하는 방법으로 진행한다면, 놀라운 리플렉션 라이브러리를 사용하는 것을 강력히 추천합니다.
모든 속성과 종속성을 각 bean 정의에 수동으로 설정해야 합니다.각 콩 정의는 다른 콩 정의와 독립적입니다. 즉, 재사용할 수 없으며, 다른 콩 정의 안에 설정할 수 없습니다.그들을 옛날 XML 방식으로 선언하는 것처럼 생각해 보세요.
자세한 내용은 BeanDefinition Builder javadocs 및 Generic BeanDefinition javadocs를 확인하십시오.
다음과 같은 작업을 수행할 수 있어야 합니다.
@Configuration
public class MyConfiguration implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@PostConstruct
public void onPostConstruct() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
for (..) {
// setup beans programmatically
String beanName= ..
Object bean = ..
configurableBeanFactory.registerSingleton(beanName, bean);
}
}
}
그냥 여기서 돈을 내겠습니다.다른 사람들은 구성이 주입되는 빈을 만들어야 한다고 언급했습니다.그러면 이 콩은 구성을 사용하여 다른 콩을 만들고 컨텍스트에 삽입합니다(또한 한 가지 형태로 주입해야 함).
다른 사람들은 이해하지 못했던 것은, 다른 콩들이 이 역동적으로 만들어진 콩들에 의존할 것이라고 말씀하셨기 때문입니다.이것은 당신의 역동적인 콩 공장이 의존적인 콩보다 먼저 인스턴스화되어야 한다는 것을 의미합니다.다음을 사용하여 주석 세계에서 이 작업을 수행할 수 있습니다.
@DependsOn("myCleverBeanFactory")
당신의 영리한 콩 공장이 어떤 종류의 물건인지에 관해, 다른 사람들은 이것을 하는 더 좋은 방법들을 추천합니다.하지만 내 기억이 맞다면, 당신은 봄 2세계에서 실제로 이와 같이 할 수 있습니다.
public class MyCleverFactoryBean implements ApplicationContextAware, InitializingBean {
@Override
public void afterPropertiesSet() {
//get bean factory from getApplicationContext()
//cast bean factory as necessary
//examine your config
//create beans
//insert beans into context
}
..
Michaas의 답변을 확대하는 것입니다. 제가 이렇게 설정하면 Michaas의 해결책이 효과가 있습니다.
public class ToBeInjected {
}
public class PropertyInjected {
private ToBeInjected toBeInjected;
public ToBeInjected getToBeInjected() {
return toBeInjected;
}
@Autowired
public void setToBeInjected(ToBeInjected toBeInjected) {
this.toBeInjected = toBeInjected;
}
}
public class ConstructorInjected {
private final ToBeInjected toBeInjected;
public ConstructorInjected(ToBeInjected toBeInjected) {
this.toBeInjected = toBeInjected;
}
public ToBeInjected getToBeInjected() {
return toBeInjected;
}
}
@Configuration
public class BaseConfig implements BeanFactoryAware{
private ConfigurableBeanFactory beanFactory;
protected ToBeInjected toBeInjected;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
@PostConstruct
public void addCustomBeans() {
toBeInjected = new ToBeInjected();
beanFactory.registerSingleton(this.getClass().getSimpleName() + "_quartzConfiguration", toBeInjected);
}
@Bean
public ConstructorInjected test() {
return new ConstructorInjected(toBeInjected);
}
@Bean
public PropertyInjected test2() {
return new PropertyInjected();
}
}
한 가지 주의할 점은 구성 클래스의 속성으로 사용자 지정 원두를 생성하고 @PostConstruct 메서드에서 초기화하고 있습니다.이렇게 하면 객체를 콩으로 등록하게 되고(그래서 @Autowire 및 @Inject는 예상대로 작동합니다), 나중에 필요한 콩에 대한 컨스트럭터 인젝션에 동일한 인스턴스를 사용할 수 있습니다.속성 가시성은 하위 클래스에서 생성된 개체를 사용할 수 있도록 protected로 설정됩니다.
우리가 들고 있는 인스턴스가 실제로는 Spring proxy가 아니기 때문에 몇 가지 문제가 발생할 수 있습니다(발화되지 않는 부분 등).다음과 같이 등록한 후 콩을 회수하는 것이 실제로 좋은 생각일 수 있습니다.
toBeInjected = new ToBeInjected();
String beanName = this.getClass().getSimpleName() + "_quartzConfiguration";
beanFactory.registerSingleton(beanName, toBeInjected);
toBeInjected = beanFactory.getBean(beanName, ToBeInjected.class);
된 를 해야 로 확장된 기본 를 만들어야 .Configuration
수업.그런 다음 모든 구성 클래스에서 다음과 같이 반복할 수 있습니다.
// Key - name of the configuration class
// value - the configuration object
Map<String, Object> configurations = applicationContext.getBeansWithAnnotation(Configuration.class);
Set<String> keys = configurations.keySet();
for(String key: keys) {
MyConfiguration conf = (MyConfiguration) configurations.get(key);
// Implement the logic to use this configuration to create other beans.
}
제가 생각해낼 수 있는 "최상의" 접근법은 제 Quartz 구성과 스케줄러를 모두 하나의 우버빈에 포장한 후에 그것을 수동으로 배선한 다음 코드를 리팩터링하여 우버빈 인터페이스와 함께 작동하는 것입니다.
우버빈은 PostConstruct에 필요한 모든 객체를 생성하고 ApplicationContextAware를 구현하여 자동으로 연결할 수 있습니다.이상적이지는 않지만, 제가 생각해낼 수 있는 최선이었습니다.
봄은 단순히 콩을 안전한 방법으로 동적으로 첨가하는 좋은 방법이 없습니다.
언급URL : https://stackoverflow.com/questions/28374000/spring-programmatically-generate-a-set-of-beans
'sourcetip' 카테고리의 다른 글
파워셸에서 regex backreference/match 값을 추출하는 방법은 무엇입니까? (0) | 2023.09.15 |
---|---|
MySQL - 0이 아닌 MIN 찾기 (0) | 2023.09.15 |
MySQL보다 SQLite가 더 빠릅니까? (0) | 2023.09.15 |
몇 번 실행 후 쿼리가 매우 느립니다. (0) | 2023.09.15 |
mysqdump에 없는 경우 테이블 만들기 (0) | 2023.09.15 |