6

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.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
Anton
  • 110
  • 1
  • 1
  • 7

5 Answers5

8

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
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
6

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
}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
0

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.

Martin Frey
  • 10,025
  • 4
  • 25
  • 30
0

You can use excludeFilter annotations provided by spring boot .

Chintamani
  • 1,076
  • 7
  • 23
  • 47
0
  1. As mentioned by others you can always use @Profile annotation to enable/disable profiles.
  2. Another option is excludeFilter
Mahesh Bhuva
  • 744
  • 5
  • 17