当存在一些可能不存在的类时,在XxxAutoConfiguration中应如何管理它们?

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

when there are some classes which may not exist, how should I manage them in an XxxAutoConfiguration?

问题

<dependency>
    <groupId>com.example</groupId>
    <artifactId>A</artifactId> // 它提供了 AFactory.class
    <version>1</version>
    <option>true</option>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.example</groupId>
    <artifactId>B</artifactId> // 它提供了 BFactory.class
    <version>1</version>
    <option>true</option>
    <scope>provided</scope>
</dependency>

在我的初始项目中,我将编写一个 XxxAutoConfiguration,该配置将使用 AFactory 或 BFactory。

我尝试过:

@Configuration
public class XxxAutoConfiguration {
    
    private AFactory aFactory;
    private BFactory bFactory;

    @Autowired(required = false)
    public void setAFactory(AFactory aFactory) {
        this.aFactory = aFactory;
    }

    @Autowired(required = false)
    public void setBFactory(BFactory bFactory) {
        this.bFactory = bFactory;
    }
    

    @Bean
    public Something something(){
        if(aFactory != null){
            return new Something(aFactory);
        }else if(bFactory != null){
            return new Something(bFactory);
        }else{
            throw new IllegalStateException();
        }
    }
}

但是它无法正常工作。

我知道我可以分别编写三个 AutoConfiguration,分别使用 @ConditionalOnBean(AFactory.class)@ConditionalOnBean(BFactory.class)@ConditionalOnMissingBean(....) 来解决问题,但这远非优雅... 你有没有什么好的解决方案?非常感谢。

英文:
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>A</artifactId> // it provide AFactory.class
            <version>1</version>
            <option>true</option>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>B</artifactId> // it provide BFactory.class
            <version>1</version>
            <option>true</option>
            <scope>provided</scope>
        </dependency>

In my starter project, I will write a XxxAutoConfiguration that uses AFactory or BFactory.

I've tried:

@Configuration
public class XxxAutoConfiguration {
    
    private AFactory aFactory;
    private BFactory bfactory;

    @Autowired(required = false)
    public void setaFactory(AFactory aFactory) {
        this.aFactory = aFactory;
    }

    @Autowired(required = false)
    public void setBfactory(BFactory bfactory) {
        this.bfactory = bfactory;
    }
    

    @Bean
    public Something something(){
        if(aFactory != null){
            return new Something(aFactory);
        }else if(bfactory != null){
            return new Something(bfactory);
        }else{
            throw new IllegalStateException();
        }
    }
}

but it doesn't work.

I know I can write three AutoConfiguration respectively with @ConditionalOnBean(AFactory.class), @ConditionalOnBean(BFactory.class) and @ConditionalOnMissingBean(....) to solve the problem, but it's
far from elegant... do you have any good solution? Thanks a lot.

答案1

得分: 0

在运行时使用可能不存在于类路径上的类来编写代码并不是一个好主意。编写代码以使其不引发 NoClassDefFoundError 是麻烦的。

标准的 Spring Boot 方法可以在 DataSourceConfiguration 的源代码中看到。

在你的情况下,你可以这样做:

abstract class XxxAutoConfiguration {

	@Configuration
	@ConditionalOnBean(AFactory.class)
	@ConditionalOnMissingBean(Something.class)
	@AutoConfigureOrder(1) // 首先尝试 AFactory
	static class ASomething {
		private AFactory aFactory;

		@Autowired
		void setaFactory(AFactory aFactory) {
			this.aFactory = aFactory;
		}

		@Bean
		Something something() {
			return new Something(this.aFactory);
		}
	}

	@Configuration
	@ConditionalOnBean(BFactory.class)
	@ConditionalOnMissingBean(Something.class)
	@AutoConfigureOrder(2) // 其次尝试 BFactory
	static class BSomething {
		@Bean
		Something something(BFactory bFactory) {
			return new Something(bFactory);
		}
	}

}

如你所见,你可以使用 @Autowired 或者使用参数来实现,两种方式都应该可以工作。

如果类路径上同时存在 AFactoryBFactory,你可以使用 @AutoConfigureOrder 来指定哪个胜出,就像这里展示的一样。还有其他更复杂的 @ConditionalXxx 注解。

XxxAutoConfiguration 类实际上只是一个伪包,用于将所有内容组合在一起。

英文:

Writing code that uses a class that may not exist on the classpath at runtime is not good idea. Writing the code so it doesn't cause NoClassDefFoundError is troublesome.

The standard Spring Boot way can e.g. be seen in the source code of DataSourceConfiguration.

In your case, you can do this:

abstract class XxxAutoConfiguration {

	@Configuration
	@ConditionalOnBean(AFactory.class)
	@ConditionalOnMissingBean(Something.class)
	@AutoConfigureOrder(1) // Try AFactory first
	static class ASomething {
		private AFactory aFactory;

		@Autowired
		void setaFactory(AFactory aFactory) {
			this.aFactory = aFactory;
		}

		@Bean
		Something something() {
			return new Something(this.aFactory);
		}
	}

	@Configuration
	@ConditionalOnBean(BFactory.class)
	@ConditionalOnMissingBean(Something.class)
	@AutoConfigureOrder(2) // Try BFactory second
	static class BSomething {
		@Bean
		Something something(BFactory bFactory) {
			return new Something(bFactory);
		}
	}

}

As you can see, you can do it using @Autowired or using a parameter, either way should work.

In case both AFactory and BFactory are present on the classpath, you can e.g. use @AutoConfigureOrder to specify which one wins, as shown here. There are other @ConditionalXxx annotations for more complex ways.

The XxxAutoConfiguration class is really just a pseudo-package for keeping it all together.

huangapple
  • 本文由 发表于 2020年8月24日 20:36:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63561207.html
匿名

发表评论

匿名网友

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

确定