将Spring Boot服务注入非托管类中

huangapple go评论86阅读模式
英文:

Inject Spring Boot Service Into Non-Managed Class

问题

我有一组类,它们都扩展自我的ReportConfig抽象类。一旦创建,它们目前是不可变的,不受Spring管理。

然而,我发现有时需要执行一个需要使用特定服务的操作,但我很难找到将这些服务注入到我的非受管ReportConfig实例中的最佳方法。请看下面的示例:

@Service
public class ReportService() {

    @Autowired
    private SchemaService schemaService;

    @Autowired
    private ExecutionService executionService;

    @Autowired
    private ReportConfigFactory reportConfigFactory;

    public Result executeReport(ReportRequest request) {

        ReportConfig reportConfig = reportConfigFactory.getReportConfig(request);

        reportConfig.validateAgainstSchema(schemaService.getSchemaForDataset(reportConfig.getDataset()));

        executionService.execute(reportConfig.getQuery());

    }
}

现在,我对dataService.execute()这一行没有问题,但对reportConfig.validateAgainstSchema()这一行有问题,我觉得一个替代方案可能是:

reportConfig.validateAgainstSchema(schemaService);

但我觉得这可能代表了紧耦合。

我想象中也有一种方式可以直接将SchemaService注入到ReportConfig中,但不确定这是否违背了初衷...

期待听到您的想法。

谢谢

英文:

I have a set of classes that all extend from my ReportConfig abstract class. Once created, they are currently immutable and not managed by Spring.

However, I'm finding that sometimes I need to perform an action that requires the use of a particular service, but I'm struggling to find the best way to inject these services into my non-managed ReportConfig instances. See below:

@Service
public class ReportService() {

    @Autowired
    private SchemaService schemaService;

    @Autowired
    private ExecutionService executionService;

    @Autowired
    private ReportConfigFactory reportConfigFactory;

    public Result executeReport(ReportRequest request) {

        ReportConfig reportConfig = reportConfigFactory.getReportConfig(request);

        reportConfig.validateAgainstSchema(schemaService.getSchemaForDataset(reportConfig.getDataset()));

        executionService.execute(reportConfig.getQuery());

}

Now I have no issue with the dataService.execute() line, but I do have an issue with the reportConfig.validateAgainstSchema() line and I feel an alternative would be something like:

reportConfig.validateAgainstSchema(schemaService);

But I feel that maybe represents tight coupling.

I'm imagining there's also a way to inject SchemaService straight into ReportConfigs, but not sure if this defeats the object...

Keen to hear your thoughts.

Thanks

答案1

得分: 3

根据您在问题中提供的当前应用程序设计,我所理解的是,我猜测功能并思考了一种替代设计,

reportConfig.validateAgainstSchema(schemaService.getSchemaForDataset(reportConfig.getDataset()));

您可以考虑的是,如果reportConfig 是执行 validateAgainstSchema 的正确位置,如果不是,请考虑进行更改。
schemaService 是否是执行 validateAgainstSchema 的正确位置(我认为不是),如果也不是正确的位置,那么可以创建一个 SchemaValidator 或类似的内容。
这是我根据您提供给我的信息所能想到的。

如果 validateAgainstSchema 没有被 ReportConfig 的任何子类重写,并且可能性非常罕见,那么您应该考虑将其移动到另一个类中。我之所以这么说,是因为如果模式发生更改,您不应该必须修改 reportConfig 类。

如果它被子类覆盖并且存在多个实现,则一种方法是使用验证器工厂来获取验证器并进行验证。我甚至不知道这是否适合您的用例,但根据问题中的信息,这是我能想到的一切。
希望它在某种程度上对您有所帮助。

英文:

From what I understood from the current design of your application given in question and I am guessing the functionality and thinking about an alternative design,

reportConfig.validateAgainstSchema(schemaService.getSchemaForDataset(reportConfig.getDataset()));

What you can think of is, if reportconfig is the right place to do validateAgainstSchema if not think about changing it.
Is schemaService the right place to do validateAgainstSchema(I think not) if that is also not the right place to do it, then create a SchemaValidator or something similar.
This is something that I could think of with the information you have given me.

If validateAgainstSchema is not overridden by any child classes of ReportConfig and possibility is very rare then you should definitely think of moving it to a different class. The reason I say this is that if the schema changes then you should not have to change the reportConfig class

If its overridden and there are multiple implementations then one way would be to use a validator factory to get the validator and do validation. I don't even know if this suits your use case but this is all I can come up with the information in the question.
Hope it helps you in someway.

答案2

得分: 2

为什么需要将服务注入配置类?直接传递配置所需内容不就可以了吗?

或者,如果必要的话,您可以使用基于setter的注入将服务自动装配到ReportConfig抽象类中,或者可以使用方法注入。

类似于

基于setter的注入

public abstract class ReportConfig {

   protected SchemaService schemaService;

   protected final setSchemaService(SchemaService schemaService) {
    this.schemaService = schemaService;
   }  

}

方法注入

public class BeanUtility implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    protected SchemaService getSchemaService() {
        return this.applicationContext.getBean("schemaService", SchemaService.class);
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
英文:

Why do you have to inject services into config classes ? Could you just not pass what config need directly ?

Alternatively if you must you can use setter based injection to autowire the service into ReportConfig abstract class or you can use method injection.

Something like

Setter based Injection

public abstract class ReportConfig {

   protected SchemaService schemaService;
   
   protected final setSchemaService(SchemaService schemaService) {
    this.schemaService = schemaService;
   }  
    
}

Method Injection

public class BeanUtility implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    protected SchemaService getSchemaService() {
        return this.applicationContext.getBean("schemaService", SchemaService.class);
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

答案3

得分: 0

你的逻辑有点奇怪。
正确的方式是另一个服务接收 ReportConfig。你现在做的是相反的。

你可以在另一个对象内部使用 bean,但它不受 Spring 管理,所以你需要重新创建它。

YourService container = (YourService) context.getBean("yourService");
container.load();

但这是错误的。正确的方法是改变你的逻辑。创建一个通用的服务,带有针对特定 reportConfig 的规则。自动装配你的服务,传入 report config,并在服务内部处理该特定数据。(抱歉我的英语)

英文:

Your logical is a bit strange.
The correct is another service receive ReportConfig. You doing the opposite.

You can work with bean inside another object not spring managed but you need to recreate it.

YourService container = (YourService) context.getBean("yourService");
container.load();

But it's wrong. The right way is change your logical. Create a generic service with rules to specific reportConfig. Autowire your service and pass your report config and work with that specific data inside the service. (sry my english)

答案4

得分: 0

你可以考虑将你的服务封装在静态持有者类中

public class SchemaServiceHolder {

   private static final SchemaServiceHolder INSTANCE = new SchemaServiceHolder();

   @Autowired
   private SchemaService schemaService;

   private SchemaServiceHolder () {
   }

   public static SchemaServiceHolder instance() {
      return INSTANCE;
   }

   public SchemaService getSchemaService() {
      return schemaService;
   }
}

然后,你需要创建这种类型的bean,以便Spring可以将该服务注入到单例的SchemaServiceHolder.INSTANCE

@Configuration
public class AppConfig {
   @Bean
   public SchemaServiceHolder schemaServiceHolder () {
      return SchemaServiceHolder.instance();
   }
}

最后,你的对象可以静态地引用schemaService

SchemaService schemaService = SchemaServiceHolder.instance().getSchemaService();
英文:

You could think of wrapping your services in static holder classes

public class SchemaServiceHolder {

   private static final SchemaServiceHolder INSTANCE = new SchemaServiceHolder();

   @Autowired
   private SchemaService schemaService;

   private SchemaServiceHolder () {
   }

   public static SchemaServiceHolder instance() {
      return INSTANCE;
   }

   public SchemaService getSchemaService() {
      return schemaService;
   }
}

You then need to create a bean of this type so Spring can inject the service into the singleton SchemaServiceHolder.INSTANCE

@Configuration
public class AppConfig {
   @Bean
   public SchemaServiceHolder schemaServiceHolder () {
      return SchemaServiceHolder.instance();
   }
}

And finally, your objects can reference the schemaService statically

SchemaService schemaService = SchemaServiceHolder.instance().getSchemaService();

答案5

得分: 0

在您的情况下,最简单的解决方案是将对象传递给工厂。因此,基本上这行代码 reportConfigFactory.getReportConfig(request); 将被更改为以下内容:

reportConfigFactory.getReportConfig(request, schemaService);

在非受管的 Spring 类的构造函数中传递 Spring 管理的 bean 是完全可以的。在您的情况下,不是直接在构造函数中传递,而是传递给一个工厂方法。

英文:

The simplest solution in your case to " also a way to inject SchemaService straight into ReportConfigs, but not sure if this defeats the object..." is to pass the object to the factory.

So basically this line reportConfigFactory.getReportConfig(request);
would be changed to this

reportConfigFactory.getReportConfig(request, schemaService);

It is totally fine to pass the spring managed bean in a constructor of a non managed spring class. In your case is not the constructor directly but to a factory method.

huangapple
  • 本文由 发表于 2020年4月3日 21:29:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/61013015.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定