Reputation: 13691
To utilize the full power of spring magic, annotate your active components with @Component
or any derived annotation, lean back and relax while Spring wires together your application.
Sofar so good.
Now I want to structure my app according to the Clean Architecture/Hexagonal Architecture pattern, and desire to keep Spring away from my inner circle, the domain. To achive that, I could simply drop the spring magic and create a couple of @Configuration
-classes with some @Bean
-providing methods.
That is, creating a lot of boilerplate code and the necessity of having enough domain knowledge to configure everything - so not, what I want.
What I want: I want to annotate my domain types with annotations like @UseCase
, @Port
, @Service
, @Entity
, @EntityId
, @ValueObject
and other meaningfull annotations, carrying the domain knowledge and intent regarding those classes - and use as much spring magic as possible in the outer layer, to automate the configuration, drawing necessary knowledge as declared in the domain layer.
I could do that, hiding the Spring Dependency by hiding an @Component
-Annotation within the @UseCase
-Annotation, but then I my domain part would still be spring dependent - just with some indirection.
What I would prefer is, telling Spring to not look for @Component
but for @UseCase
instead. My imagination tells me, Spring works somehow like this:
for (class in classpath-with-matching-package) {
if (class has annotation '@Component') {
createBeanDefinitionForClass(class)
}
}
createBeansForDefinitions()
And I hope, it is possible to tell the method which does the check, whether the class has the @Component
-Annotation to check wether it has some other, meaningful annotation, either by configuration or by extension.
However, I have no clue, what class and or method might be responsible for this task.
Questions in short:
Also feel free, to respectfully comment, why my idea is stupid. Thats always the most enlightening!
This is in Response to the answer by @Ghokun which seems reasonable but fails
My Example-Project has the following structure:
src/main/java/de/mycorp/group
|-> /exampleapp
|-> NoComponentDemoApp.java
|-> UseCaseScan.java
|-> /springless
|-> SomeUseCase.java
|-> UseCase.java
NoComponentDemoApp.java:
@SpringBootApplication
public class NoComponentDemoApp
{
public static void main(String[] args)
{
SpringApplication.run(NoComponentDemoApp.class, args);
}
}
UseCaseScane.java
@Configuration
@ComponentScan(
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(UseCase.class)}, // type default/annotation, value synonym of classes
basePackages = "de.mycorp.group.springless"
)
public class UseCaseScan
{
}
SomeUseCase.java
@UseCase
public class SomeUseCase
{
private static final Logger logger = LoggerFactory.getLogger(SomeUseCase.class);
public SomeUseCase()
{
logger.info("Some Actor created");
}
}
UseCase.java
public @interface UseCase
{
}
Ofc. when I enable default filters and mark SomeUseCase.java
with @Component
it works. But something fails. For brevity I removed import. Package info can be deduced from the structure. If deemed necessary, I will add it.
Upvotes: 0
Views: 310
Reputation: 3465
Spring already gives you that option with the following configuration:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
@ComponentScan(useDefaultFilters = false,
includeFilters = {@Filter(type = FilterType.ANNOTATION, class= UseCase.class)})
public class AppConfig {}
Edit:
It works with following configuration. Please check your classes:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
@SpringBootApplication
@ComponentScan(useDefaultFilters = false, includeFilters = { @Filter(UseCase.class) })
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
package demo;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@UseCase
public class AnnotatedClass {
private static final Logger logger = LoggerFactory.getLogger(AnnotatedClass.class);
@PostConstruct
public void init() {
logger.info("It Works!");
}
}
package demo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Indexed;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface UseCase {
}
...
2020-09-28 03:25:02.913 INFO 6829 --- [ main] demo.AnnotatedClass: It Works!
...
Upvotes: 1