如何在Spring中禁用使用 @Component 注解创建的Bean?

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

How can I disable creating bean with @Component annotation in Spring?

问题

以下是翻译好的内容:

我在项目中有一些通用的界面,用于重构逻辑。大致看起来是这样的:

public interface RefactorAwareEntryPoint {

    default boolean doRefactor() {
        if (EventLogService.wasEvent(getEventType())) {
            return true;
        }
        boolean result = doRefactorInternal();
        if (result) {
            EventLogService.registerEvent(eventType);
        }
        return result;
    }

    String getEventType();
    
    boolean doRefactorInternal();
}

然后,当我需要编写一些重构代码时,我使用方法实现这个接口,将类标记为 @Component,然后 Spring 会循环评估每个接口实现并将其注册到数据库中。但我们有很多重构(每年大约有 200-300 个新的)。手动禁用旧的实现很困难,而且我们的 Spring 上下文中有很多 bean。我们能否做一些事情,例如使用一些注解 - 该注解将根据某些条件禁用组件的创建?

例如:

@Component
@Enabled(YEAR.2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
    // 代码实现
}

这个注解将按照如下方式工作(伪代码):

if (YEAR.2020) {
    创建 bean -> new CustomRefactor()
}

当到了 YEAR.2021 时,我们在 Spring 上下文中将不再有来自 YEAR.2020 的 bean。

英文:

I have some common interface for refactoring logic in my project. It looks about like this:

public interface RefactorAwareEntryPoint {

	default boolean doRefactor() {
    	if (EventLogService.wasEvent(getEventType())) {
			return true;
		}
		boolean result = doRefactorInternal();
		if (result) {
			EventLogService.registerEvent(eventType);
		}
		return result;
	}

	String getEventType();
	
	boolean doRefactorInternal();
}

And than, when I need to write some refactoring - I implement this interface with methods, mark class like @Component, and Spring in loop evaluate each interface implementation and register it in database.
But we have a lot of refactors (every year - 200-300 new). It's hard to disable old implementations manualy, and we have a lot of beans in our spring-context.
Can we do something, for example, use some annotation - which will disable component creation by some condition?

For example:

@Component
@Enabled(YEAR.2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
 // Code implementation
}

And this annotation will work like this (a pseudocode):

if (YEAR.2020) {
  create bean -> new CustomRefactor()
}

And when it will be YEAR.2021 - we will have no beans from YEAR.2020 in spring-context.

答案1

得分: 8

使用注解@Profile,可以在特定环境中提供应用程序配置和Beans。

你可以在Spring Boot 2.4.0参考文档的3. Profiles部分找到更多信息。

Spring配置文件提供了一种在应用程序配置中隔离部分内容并使其仅在特定环境中可用的方式。任何@Component@Configuration@ConfigurationProperties都可以使用@Profile进行标记,以限制其加载时间。

将每一年视为一个单独的环境。

@Component
@Profile("2020")
public class CustomRefactor2020 implements RefactorAwareEntryPoint {
 // 代码实现
}
@Component
@Profile("2021")
public class CustomRefactor2021 implements RefactorAwareEntryPoint {
 // 代码实现
}
英文:

Use the annotation @Profile that makes application configuration and beans available in certain environments.

You can find more at Spring Boot 2.4.0 reference documentation: 3. Profiles

> Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any @Component, @Configuration or @ConfigurationProperties can be marked with @Profile to limit when it is loaded

Consider each year as a separate environment.

@Component
@Profile("2020")
public class CustomRefactor2020 implements RefactorAwareEntryPoint {
 // Code implementation
}
@Component
@Profile("2021")
public class CustomRefactor2021 implements RefactorAwareEntryPoint {
 // Code implementation
}

答案2

得分: 6

除了我们的同事提供的答案之外,还要考虑到 Spring 的一个功能,称为“Stereotype 注解”。这就是像 @Service 这样的知名注解在 Spring 中是如何定义的。

一般来说,通过使用 @Component 注解标记你的类,你可以将该类作为 Spring Bean 加载,因为被注解的类将成为一个叫做“组件扫描”的过程的对象,这个过程会在你启动应用程序上下文时发生。

自从 Spring 4 开始,有一个条件接口,基本上可以实现类似于你所说的 @Enabled(YEAR.2020) 的逻辑。

你可以使用内置的 @ConditionalOnProperty 将 2020 年映射到属性,甚至实现一个自定义条件逻辑。我假设你已经实现了一个名为 @ConditionalOnYear 的自定义条件。

现在,有趣的是(这是我在帖子开头提到的一个“stereotype”特性),你可以创建自己的“组件”注解,并带有自定义的“条件”逻辑,并且可以像使用常规 Bean 一样使用它:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnYear(2020)
@Component
public @interface Year2020OnlyComponent {

	@AliasFor(annotation = Component.class)
	String value() default "";

}
@Year2020OnlyComponent
public class CustomRefactor implements RefactorAwareEntryPoint {
 // 代码实现
}

你还可以通过巧妙使用 @AliasFor 注解来改进它,变成类似这样:

@SinceYearComponent(2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
 // 代码实现
}

但这有点超出了本问题的范围,所以我只是在这里提及一个方向。

当然,即使没有这个“Stereotype”注解特性,也可以仅仅使用两个注解,就像你之前建议的那样:

@Component
@SinceYear(2020) // 一个自定义条件
public class CustomRefactor implements RefactorAwareEntryPoint {
 // 代码实现
}
英文:

In addition to the answers provided by our colleagues, consider the feature of spring called "Stereotype annotations". This is how well-known annotations like @Service are defined in spring.

In general, the fact that you mark your class with @Component annotation allows you to load the class as a spring bean because the annotated class becomes a subject to a process called "component scanning" - a process happens when you start the application context.

Since spring 4 there is a conditional interface that basically makes possible implementing a logic similar to what you refer to as @Enabled(YEAR.2020).

You might use a built-in "@ConditionalOnProperty" to map the 2020 year to property or even implement a custom conditional logic. I'll assume that you've implemented a custom conditional as @ConditionalOnYear

Now, what's interesting (and this is a "stereotype" feature that I've mentioned at the beginning of the post) is that you may create your own "component" annotation with a custom "conditional" logic and use it "as if" its a regular bean:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnYear(2020)
@Component
public @interface Year2020OnlyComponent {

	@AliasFor(annotation = Component.class)
	String value() default "";

}
@Year2020OnlyComponent
public class CustomRefactor implements RefactorAwareEntryPoint {
 // Code implementation
}

You can also improve that by clever usage of @AliasFor annotation to be something like:

@SinceYearComponent(2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
 // Code implementation
}

But this is kind of out of scope for this question - so I just mention a direction here.

Of course, it's possible to merely use two annotations as you've suggested even without this "Stereotype" annotation feature:

@Component
@SinceYear(2020) // a custom conditional
public class CustomRefactor implements RefactorAwareEntryPoint {
 // Code implementation
}

答案3

得分: 0

检查一下BeanFactoryPostprocessor接口。可能在bean被创建之前,你可以移除一个bean。

否则,你可以实现自己的BeanFactory,并且使用你的实现来创建ApplicationContext。

英文:

Check out the BeanFactoryPostprocessor interface. Probably you can remove a bean before it‘s creation.

Else you might implement your own BeanFactory and create the ApplicationContext with your implementation.

答案4

得分: 0

你可以使用Spring Boot提供的excludeFilter注解。

英文:

You can use excludeFilter annotations provided by spring boot .

答案5

得分: 0

  1. 如他人所提,您可以始终使用@Profile注解来启用/禁用配置文件。
  2. 另一个选项是excludeFilter
英文:
  1. As mentioned by others you can always use @Profile annotation to
    enable/disable profiles.
  2. Another option is excludeFilter

huangapple
  • 本文由 发表于 2020年8月26日 03:48:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/63586118.html
匿名

发表评论

匿名网友

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

确定