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.
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
}
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
}
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.
You can use excludeFilter annotations provided by spring boot .
As mentioned by others you can always use #Profile annotation to
enable/disable profiles.
Another option is excludeFilter
Related
I'm attempting to override a bean definition from a library auto configuration and it isn't working. I'm overriding the definition to return a bean of the interface type - my own implementation, whereas the library auto configuration bean returns a concrete implementation.
Spring is finding my bean at startup, then overriding it with the library bean. I've tried using #AutoConfigureAfter(LibraryConfig.class) and making my bean #Lazy but nothing seems to work.
I've spent many a year staying clear of 'magical' code and this type of thing is a good case in point.
Your question is indeed not clear, probably an example could help here.
Anyway, when an infrastructure-level bean is defined, the easiest way in spring boot to override it is just to provide your own configuration that will return the same interface (the bean with the given name)
for example, let's say, in some spring boot source/thirdparty there is the following code:
interface SomeInterface {...}
public class ThirdpartyInternalImplementation implementsSomeInterface {...}
#Configuration
public class ThirdPartyConfiguration {
#Bean
public SomeInterface someInterface () {
return new ThirdpartyInternalImplementation();
}
}
So now, let's assume, that in your source code you've provided another implementation of SomeInterface and you want to return it instead. So you should do something like this:
class MyCustomImplementation implements SomeInterface {}
..........................
#Configuration
public class MyOwnConfiguration {
#Bean
public SomeInterface someInterface() {
return MyCustomImplementation();
}
}
This should be enough to register your custom implementation of SomeInterface.
I'm writing a library that uses spring, for others to use. In my library, I have class A that has an interface B as a field (with #Autowired).
I have a default implementation of that field, but the user can implement a custom implementation by himself. What I want to happen is the following:
If the user implemented B, I want that bean to be injected to A, otherwise I want my default implementation to be injected.
Something like the opposite of #Primary
I know that the user can add the #Primary annotation in order for that to happen, but I don't want him to add any other annotation besides #Component (because it is not clear for the user why he must add the #Primary annotation)
Is there a way to achieve this behavior? I've also tried #Order and #Priority, but no luck - the user must add another annotation.
Thanks!
You should create your own auto configuration. Auto configuration classes are always processed last so user's own configuration is processed first and when using #Conditional... annotations user's beans will take precedence.
Your auto configuration class should look like this:
#Configuration
public class MyAutoConfiguration {
#ConditionalOnMissingBean(B.class)
#Bean
public B defaultImplementation() { return A(); }
#Bean
public UsesB classThatUsesB(B b) { return UsesB(b); }
}
In this case if the user of your library defines a bean of type B it will always be used first and if they don't the bean created by the defaultImplementation method will be used.
I don't believe so. Spring's #Autowired is rather specific. Making it perform differently without any configuration changes (either to XML or the Spring configuration class) is pretty much impossible.
Some times interfaces are annotated with #Component annotation. Then my obvious reasoning was that classes that implement such interface will be treated as components as well. But if I am right that is not the case.
So what is the purpose of #Component annotation on interfaces.
Annotating an interface with #Component is common for Spring classes, particularly for some Spring stereotype annotations :
package org.springframework.stereotype;
...
#Component
public #interface Service {...}
or :
package org.springframework.boot.test.context;
...
#Component
public #interface TestComponent {...}
#Component is not declared as an inherited annotation :
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Component {...}
But whatever, during loading of the context, Spring discovers beans by considering the hierarchy of the annotation declared in the candidate class.
In the org.springframework.boot.BeanDefinitionLoader class (included in the Spring Boot dependency) that loads bean definitions from underlying sources, you can see an example of
org.springframework.core.annotation.AnnotationUtils.findAnnotation() that Spring uses to retrieve annotations in the whole hierarchy of the annotation:
class BeanDefinitionLoader {
...
private boolean isComponent(Class<?> type) {
// This has to be a bit of a guess. The only way to be sure that this type is
// eligible is to make a bean definition out of it and try to instantiate it.
if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
return true;
}
// Nested anonymous classes are not eligible for registration, nor are groovy
// closures
if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
|| type.getConstructors() == null || type.getConstructors().length == 0) {
return false;
}
return true;
}
...
}
Concretely, it means as the #Service annotation is itself annotated with #Component, Spring will consider a candidate class annotated with #Service as a bean to instantiate.
So, your guesswork is right :
Classes that implement such interface will be treated as components as
well.
But this works only for interfaces (such as #Service) that are Java annotations and not for plain interfaces.
For Spring classes, this way of doing makes sense (enriching actual stereotype for example) but for your own beans, using #Component for the interface rather than the implementation will not work and would bring more drawbacks than advantages :
it defeats in a same way the purpose of an interface that is above all a contract. It couples it to Spring and it supposes that you will always have a single implementation of the class. In this case, why using an interface ?
it scatters the reading of the class at two places while the interface doesn't need to have any Spring stereotype.
That is not the case there is no need to adding #component on an interface because it is not a bean as we can't create reference for it.
The main part is actually #autowired where you injection the dependecy.
For example
public interface SortAlog();
public class BubbleSortAlgo();
No we are following the dynamic binding and creating the object of interface but implementation is on the run time.
So #autowired is the one that will create the object internally and we have #component for bubbleSortAlgo and the only candidate for the injection, so it will get reference from there.
I hope I was able to make a point here.
We use #Configuration classes with lots of #Bean annotated methods that essentially read like this:
#Bean
public TeamContactIndexer teamContactIndexer(GroupService groupService, ContactCrudService contactCrudService, ContactRetrieveService contactRetrieveService) {
return new TeamContactIndexer(groupService, contactCrudService, contactRetrieveService);
}
So this returns a new bean and injects other spring declared things via method arguments into the constructor by name.
The only way I know to reduce verbosity is to annotate the beans with #Component and constructor with #Autowired.
For many this is perfectly acceptable but I prefer to not litter my code with Spring annotations just to facilitate plumbing and keep a clean separation between completely spring free business logic and plumbing code in #Configuration annotated classes. I treat them as a more type safe, less verbose replacement for what we used to do in xml.
However, wouldn't it be nice if I could just go ...
#Bean
public TeamContactIndexer teamContactIndexer;
... and have spring figure out that it needs to autowire the constructor of that class (100% spring free) to produce the bean. This is not currently supported in Spring as far as I can see even though it should be quite easy to do as far as I can see. Am I missing something or is there really no way around me micromanaging constructor calls in my #Configuration classes (other than littering my code with annotations)? The vast majority of my #Bean methods should be easily replaced like this.
UPDATE
#bezmax has provided a workable approach here which is to use a component scan annotation.
#Configuration
#ComponentScan(
basePackages={"com.github.jsonj.tools"},
includeFilters = { #Filter(type = FilterType.ASSIGNABLE_TYPE, value = {JsonParser.class})})
public class JsonParserConfig {
}
I've used the above to provide a bean definition for a bean without annotations in a library that I use. This replaces the #Bean annotated factory method I had earlier. It's still somewhat verbose but at least you can put in a comma separated list of classes. The default for type is wrong for this usecase so you must specify it; likewise the package defintion is required even though it could be deduced from the class on the filter.
IMHO there is room for an obvious improvement in Spring, which is to provide an annotation that simply takes a comma separated list of classes. So, instead of scanning a package, simply list the bean classes you want initialized. There are probably still a few hairy issues with autowiring via the constructor.
This feature is implemented in Spring 4.3 (not yet released).
You can read more about that in the changelog (see 6.1)
Added:
As about registering your unannotated classes automatically, there seems to be a flexible way to achieve this using #ComponentScan annotation. This annotation allows you to specify a set of include filters, which, when matched on classes, are automatically registered as beans. I had not actually tried using more complex rules with this filter, and it seems that you have several options there (check out the documentation on #ComponentScan) but the easiest one would be something like this:
#Configuration
#ComponentScan(
value = "some.package.path",
includeFilters = {
#Filter(type = ASSIGNABLE_TYPE, value = {
MyClass1.class,
MyClass2.class,
MyClass3.class
})
})
public class WebConfig extends WebMvcConfigurerAdapter {
...
I have a Spring AOP aspect used for logging, where a method can be included for logging by adding an annotation to it, like this:
#AspectLogging("do something")
public void doSomething() {
...
}
I've been using this on Spring beans and it's been working just fine. Now, I wanted to use it on a REST-service, but I ran into some problems. So, I have:
#Path("/path")
#Service
public class MyRestService {
#Inject
private Something something;
#GET
#AspectLogging("get some stuff")
public Response getSomeStuff() {
...
}
}
and this setup works just fine. The Rest-service that I'm trying to add the logging to now has an interface, and somehow that messes stuff up. As soon as I add the #AspectLogging annotation to one of the methods, no dependencies are injected in the bean, and also, the aspect is newer called!
I've tried adding an interface to the REST-service that works, and it gets the same error.
How can having an interface lead to this type of problems? The aspect-logger works on classes with interfaces elsewhere, seems it's only a problem when it's a REST-service..
Ref the below Spring documentation (para 2) -
To enable AspectJ annotation support in the Spring IoC container, you
only have to define an empty XML element aop:aspectj-autoproxy in your
bean configuration file. Then, Spring will automatically create
proxies for any of your beans that are matched by your AspectJ
aspects.
For cases in which interfaces are not available or not used in an
application’s design, it’s possible to create proxies by relying on
CGLIB. To enable CGLIB, you need to set the attribute
proxy-targetclass= true in aop:aspectj-autoproxy.
In case your class implements an interface, a JDK dynamic proxy will be used. However if your class does not implement any interfaces then a CGLIB proxy will be created. You can achieve this #EnableAspectJAutoProxy. Here is the sample
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public LoggingAspect logingAspect(){
return new LoggingAspect();
}
}
#Component
#Aspect
public class LoggingAspect {
...
...
}
In my opinion what you are actually trying to do is to add spring annotations to a class maintained by jersey. In the result you are receiving a proxy of proxy of proxy of somethng. I do not think so this is a good idea and this will work without any problems. I had a similar issue when I tried to implement bean based validation. For some reasons when there were #PahtParam and #Valid annotations in the same place validation annotations were not visible. My advice is to move your logging to a #Service layer instead of #Controller.