noa*_*oah 25 java spring spring-mvc dropwizard
我有一个Dropwizard应用程序,需要为配置列表中的每个配置生成十几个bean.健康检查,石英调度等等.
像这样的东西:
@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();
}
}
Run Code Online (Sandbox Code Playgroud)
我有多个MyConfiguration实例都需要像这样的bean.现在我必须复制并粘贴这些定义,并为每个新配置重命名它们.
我可以以某种方式迭代我的配置类并为每个类生成一组bean定义吗?
我可以使用子类化解决方案或类型安全的任何东西,而不会让我复制并粘贴相同的代码,并在我必须添加新服务时重命名方法.
编辑:我应该补充一点,我有其他依赖这些bean的组件(Collection<HealthCheck>
例如,它们注入.)
Fed*_*ner 38
所以你需要动态地声明新的bean并将它们注入到Spring的应用程序上下文中,好像它们只是普通的bean,这意味着它们必须经过代理,后处理等,即它们必须服从Spring bean生命周期.
请参阅BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
方法 javadocs.这正是您所需要的,因为它允许您在加载普通bean定义之后 但 在实例化任何单个bean之前修改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);
}
}
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...
}
Run Code Online (Sandbox Code Playgroud)
这有效地声明了您需要的bean并将它们注入到Spring的应用程序上下文中,每个配置都有一组bean.你必须依赖一些命名模式,然后在需要的地方通过名称自动装配你的bean:
@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;
...
}
Run Code Online (Sandbox Code Playgroud)
笔记:
如果您通过手动从文件中读取配置名称,请使用Spring ClassPathResource.getInputStream()
.
如果你自己扫描类路径,我强烈建议你使用惊人的Reflections库.
您必须手动设置每个bean定义的所有属性和依赖项.每个bean定义都与其他bean定义无关,即你不能重用它们,将它们放在另一个bean中,等等.把它们想象成你用旧的XML方式声明bean.
检查BeanDefinitionBuilder javadocs和GenericBeanDefinition 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
只是扩展 Michas 的答案 - 如果我这样设置,他的解决方案就有效:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
需要注意的一件事是,我正在创建自定义 bean 作为配置类的属性,并在 @PostConstruct 方法中初始化它们。通过这种方式,我将对象注册为 bean(因此 @Autowire 和 @Inject 按预期工作),稍后我可以在需要它的 bean 的构造函数注入中使用相同的实例。属性可见性设置为受保护,以便子类可以使用创建的对象。
由于我们持有的实例实际上不是 Spring 代理,因此可能会出现一些问题(方面未触发等)。在注册后检索 bean 实际上可能是一个好主意,如下所示:
toBeInjected = new ToBeInjected();
String beanName = this.getClass().getSimpleName() + "_quartzConfiguration";
beanFactory.registerSingleton(beanName, toBeInjected);
toBeInjected = beanFactory.getBean(beanName, ToBeInjected.class);
Run Code Online (Sandbox Code Playgroud)
我就在这凑钱。其他人提到您需要创建一个 bean,将您的配置注入其中。然后该 bean 将使用您的配置创建其他 bean 并将它们插入到上下文中(您还需要以一种或另一种形式注入)。
我认为其他人没有注意到的是,您已经说过其他 bean 将依赖于这些动态创建的 bean。这意味着您的动态 bean 工厂必须在依赖 bean 之前实例化。您可以使用(在注释世界中)执行此操作
@DependsOn("myCleverBeanFactory")
Run Code Online (Sandbox Code Playgroud)
至于你的聪明豆工厂是什么类型的对象,其他人已经推荐了更好的方法来做到这一点。但是,如果我没记错的话,您实际上可以在旧的 Spring 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
}
Run Code Online (Sandbox Code Playgroud)
..
noa*_*oah -5
我能想到的“最佳”方法是将所有 Quartz 配置和调度程序包装在 1 个 uber bean 中,并手动将其全部连接起来,然后重构代码以使用 uber bean 接口。
uber bean 在其 PostConstruct 中创建了我需要的所有对象,并实现了 ApplicationContextAware,以便它可以自动连接它们。这并不理想,但这是我能想到的最好的。
Spring 根本没有一种以类型安全的方式动态添加 bean 的好方法。
归档时间: |
|
查看次数: |
22549 次 |
最近记录: |