exclude #Component from #ComponentScan - java

I have a component that I want to exclude from a #ComponentScan in a particular #Configuration:
#Component("foo") class Foo {
...
}
Otherwise, it seems to clash with some other class in my project. I don't fully understand the collision, but if I comment out the #Component annotation, things work like I want them to. But other projects that rely on this library expect this class to be managed by Spring, so I want to skip it only in my project.
I tried using #ComponentScan.Filter:
#Configuration
#EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
but it doesn't appear to work. If I try using FilterType.ASSIGNABLE_TYPE, I get a strange error about being unable to load some seemingly random class:
Caused by: java.io.FileNotFoundException: class path resource [junit/framework/TestCase.class] cannot be opened because it does not exist
I also tried using type=FilterType.CUSTOM as following:
class ExcludeFooFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClass() == Foo.class;
}
}
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.CUSTOM, value=ExcludeFooFilter.class)})
public class MySpringConfiguration {}
But that doesn't seem to exclude the component from the scan like I want.
How do I exclude it?

The configuration seem alright, except that you should use excludeFilters instead of excludes:
#Configuration #EnableSpringConfigured
#ComponentScan(basePackages = {"com.example"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}

Using explicit types in scan filters is ugly for me. I believe more elegant approach is to create own marker annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface IgnoreDuringScan {
}
Mark component that should be excluded with it:
#Component("foo")
#IgnoreDuringScan
class Foo {
...
}
And exclude this annotation from your component scan:
#ComponentScan(excludeFilters = #Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}

Another approach is to use new conditional annotations.
Since plain Spring 4 you can use #Conditional annotation:
#Component("foo")
#Conditional(FooCondition.class)
class Foo {
...
}
and define conditional logic for registering Foo component:
public class FooCondition implements Condition{
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// return [your conditional logic]
}
}
Conditional logic can be based on context, because you have access to bean factory. For Example when "Bar" component is not registered as bean:
return !context.getBeanFactory().containsBean(Bar.class.getSimpleName());
With Spring Boot (should be used for EVERY new Spring project), you can use these conditional annotations:
#ConditionalOnBean
#ConditionalOnClass
#ConditionalOnExpression
#ConditionalOnJava
#ConditionalOnMissingBean
#ConditionalOnMissingClass
#ConditionalOnNotWebApplication
#ConditionalOnProperty
#ConditionalOnResource
#ConditionalOnWebApplication
You can avoid Condition class creation this way. Refer to Spring Boot docs for more detail.

In case you need to define two or more excludeFilters criteria, you have to use the array.
For instances in this section of code I want to exclude all the classes in the org.xxx.yyy package and another specific class, MyClassToExclude
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })

I had an issue when using #Configuration, #EnableAutoConfiguration and #ComponentScan while trying to exclude specific configuration classes, the thing is it didn't work!
Eventually I solved the problem by using #SpringBootApplication, which according to Spring documentation does the same functionality as the three above in one annotation.
Another Tip is to try first without refining your package scan (without the basePackages filter).
#SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}

In case of excluding test component or test configuration, Spring Boot 1.4 introduced new testing annotations #TestComponent and #TestConfiguration.

I needed to exclude an auditing #Aspect #Component from the app context but only for a few test classes. I ended up using #Profile("audit") on the aspect class; including the profile for normal operations but excluding it (don't put it in #ActiveProfiles) on the specific test classes.

Your custom filer has wrong logic.
Because metadataReader.getClass() is always not equal to Foo.class, metadataReader.getClass() return "org.springframework.core.type.classreading.MetadataReader". It'd use className for comparation.
public class CustomFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getClassMetadata().getClassName().equals(Foo.class.getName());
}
}
#ComponentScan.Filter is belong every #ComponentScan and only filter compononets from the specific scan path.
If you find it doesn't work, you could check whether there's another #ComponentScan or <context:component-scan base-package="xxx"/> in your code

Related

Spring Boot: #TestConfiguration Not Overriding Bean During Integration Test

I have a Bean defined in a class decorated with #Configuration:
#Configuration
public class MyBeanConfig {
#Bean
public String configPath() {
return "../production/environment/path";
}
}
I have a class decorated with #TestConfiguration that should override this Bean:
#TestConfiguration
public class MyTestConfiguration {
#Bean
#Primary
public String configPath() {
return "/test/environment/path";
}
}
The configPath bean is used to set the path to an external file containing a registration code that must be read during startup. It is used in an #Component class:
#Component
public class MyParsingComponent {
private String CONFIG_PATH;
#Autowired
public void setCONFIG_PATH(String configPath) {
this.CONFIG_PATH = configPath;
}
}
While trying to debug this I set a breakpoint inside each method as well as the constructor of the test config class. The #TestConfiguration's constructor breakpoint is hit, so i know that my test configuration class instantiates, however the configPath method of that class is never hit. Instead, the configPath method of the normal #Configuration class is hit and the #Autowired String in MyParsingComponent is always ../production/environment/path rather than the expected /test/environment/path.
Not sure why this is happening. Any thoughts would be greatly appreciated.
As documented in the Detecting Test Configuration section of the Spring Boot reference manual, any beans configured in a top-level class annotated with #TestConfiguration will not be picked up via component scanning. So you have to explicitly register your #TestConfiguration class.
You can do that either via #Import(MyTestConfiguration.class) or #ContextConfiguration(classes = MyTestConfiguration.class) on your test class.
On the other hand, if your class annotated with #TestConfiguration were a static nested class within your test class, it would be registered automatically.
Make sure that the method name of your #Bean factory method does not match any existing bean name. I had issues with method names like config() or (in my case)
prometheusConfig() which collided with existing bean names. Spring skips those factory methods silently and simply does not call them / does not instantiate the beans.
If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your #Bean("beanName") annotation.
Test configuration has to be explicitly imported in the test via #Import({MyTestConfiguration.class}).
The name of the #Bean methods in #Configuration and #TestConfiguration have to be different. At least it makes difference in Spring Boot v2.2.
Also make sure spring.main.allow-bean-definition-overriding=true otherwise the bean could not be overriden.
For me worked this code:
#TestConfiguration // 1. necessary
public class TestMessagesConfig {
#Bean
#Primary // 2. necessary
public MessageSource testMessageSource() { // 3. different method name than in production code e.g. add test prefix
}
}
I struggled with a related problem, whereby even though I was using an inner static class, my test bean was not being registered.
It turns out, You still need to add your inner static class to the #ContextConfiguration class array, otherwise the beans inside the #TestConfiguration doesn't get picked up.
public interface Foo {
String execute();
}
public class FooService {
private final Foo foo;
FooService(Foo foo) {
this.foo = foo;
}
public String execute() {
return foo.execute();
}
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {FooService.class, FooTest.FooTestConfig.class})
public class FooTest {
#Autowired
FooService fooService;
#Test
void test() {
Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
}
#TestConfiguration
static class FooTestConfig {
#Bean
public Foo getFooBean() {
return () -> "MY_TEST_BEAN";
}
}
}
I came across a similar issue recently and got it sorted out by annotating my testing bean with #Primary as well as #Bean. Not sure why it's required, which seems not documented in the Spring doc. The version of my SpringBoot is 2.0.3.
In my case it was an issue with #RunWith(SpringRunner.class), I'm not exactly sure why it wasn't working, I was following this - Testing in Spring Boot
But after replacing that with #ExtendWith(SpringExtension.class) the inner static #TestConfiguration class created the beans as expected.
Maybe a version mismatch - I'm using Spring Boot 2.7.2.
In my case replacing #Import(TestConfig.class) with #ContextConfiguration(classes=TestConfig.class) did the trick. For some reason, some of the beans from TestConfig but 1 wasn't until I replaced #Import with #ContextConfiguration.
This was also mentioned in some comments that were hidden because they had no upvotes.
I found it odd how several answers stated that the names of the #Beans have to be different from each other. How would that make one override the other?
There wasn't one specific answer that worked for me, but I've solved the issue by combining some of their advices.
Here's what worked for me.
Main configuration class:
#Configuration
public class SpringConfiguration {
#Bean
BeanInterface myBean() {
return new BeanImplementation();
}
#Bean
OtherClass otherBean() {
return new OtherClass();
}
}
Test configuration class:
#TestConfiguration
public class TestSpringConfiguration {
#Bean
#Primary
BeanInterface myBean() {
return new TestBeanImplementation();
}
}
Test class:
#SpringBootTest(classes = TestSpringConfiguration.class,
properties = "spring.main.allow-bean-definition-overriding=true")
public class Tests {
#Test
public void test() {
// do stuff
}
}
In this way, the "myBean" bean instance is the one defined in the TestSpringConfiguration class, while "otherBean" is the one defined in the SpringConfiguration class, since it's not overridden.
If I gave two different names to the "myBean" beans, the "real" one would still be initialized and, in my case, would give an error during tests, since it needs something that's only available at runtime in its proper environment.
Once I gave both the same name, Spring would throw an error saying that they were conflicting. Hence why I had to specify the property spring.main.allow-bean-definition-overriding=true in the #SpringBootTest annotation of the test class.
By the way, if you're NOT using Spring Boot, I guess these alternative annotations could work for you:
#ExtendWith(value = SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, // <- not sure about this one
classes = { SpringConfiguration.class, TestSpringConfiguration.class })
public class Tests {
#Test
public void test() {
// do stuff
}
}
Then, you would still have to set the property spring.main.allow-bean-definition-overriding=true in the test application.yml or application.properties file, or in some other way via code on startup.
Note: I'm not 100% sure that you would need the loader = AnnotationConfigContextLoader.class thing. Try without it, first. I needed it in a project of mine which had Spring without Boot, but I can't remember whether it's a standard thing to set or I needed it for some specific reason.

What difference does #EnableConfigurationProperties make if a bean is already annotated with #ConfigurationProperties?

The Spring Boot documentation says that to use the #ConfigurationProperties annotation
You also need to list the properties classes to register in the
#EnableConfigurationProperties annotation, as shown in the following
example:
and gives this code:
#Configuration
#EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
But in the very next paragraph says:
Even if the preceding configuration creates a regular bean for
AcmeProperties, we recommend that #ConfigurationProperties only deal
with the environment and, in particular, does not inject other beans
from the context. Having said that, the #EnableConfigurationProperties
annotation is also automatically applied to your project so that any
existing bean annotated with #ConfigurationProperties is configured
from the Environment.
Suggesting that listing a #ConfigurationProperties bean under an #EnableConfigurationProperties annotation is not necessary.
So which is it? Experimentally, I've seen that if I annotate a bean with #ConfigurationProperties it gets properties injected to it as expected without needing to list it in #EnableConfigurationProperties, but if this is the case then why list anything that has a #ConfigurationProperties annotation under #EnableConfigurationProperties, as is shown in the documentation? Does it make any kind of difference?
As M. Deinum referred #EnableConfigurationProperties Is for enabling support of #ConfigurationProperties. If you take a look to the annotation Java Doc you can see:
Enable support for ConfigurationProperties annotated beans. ConfigurationProperties beans can be registered in the standard way (for example using Bean #Bean methods) or, for convenience, can be specified directly on this annotation. [...]
For example, let's say you have a class whose responsibility is to read and store information from your application.yml / application.properties that is required to make a connection to different databases. You annotate it with #ConfigurationProperties.
Then, you typically have a #Configuration annotated class that provides a DataSource #Bean to your application. You can use the #EnableConfigurationProperties to link it to the #ConfigurationProperties class and init your data sources accordingly.
Here is a small example:
application.yml
data-sources:
db1:
url: "jdbc:postgresql://localhost:5432}/db1"
username: test
password: test
db2:
url: "jdbc:postgresql://localhost:5432}/db2"
username: test
password: test
DataSourcesConfiguration
#ConfigurationProperties
public class DataSourcesConfiguration {
private Map<String, BasicDataSource> dataSources;
public void setDataSources(Map<String, BasicDataSource> dataSources) {
this.dataSources = dataSources;
}
Map<String, BasicDataSource > getDataSources() {
return dataSources;
}
}
DataSourceConnectionConfiguration
#Configuration
#EnableConfigurationProperties(DataSourcesConfiguration.class)
public class DatabaseConnectionConfiguration implements Provider<Connection> {
private DataSourcesConfiguration dataSourcesConfiguration;
public DatabaseConnectionConfiguration(DataSourcesConfiguration dataSourcesConfiguration) {
this.dataSourcesConfiguration = dataSourcesConfiguration;
}
#Bean
public DataSource dataSource() {
// Use dataSourcesConfiguration to create application data source. E.g., a AbstractRoutingDataSource..
}
}
It took me a while to reach to this post but would like to add here so that others may get benefited.
#ConfigurationProperties - Used to bind a class with an externalized property file. Very powerful and must be used to separate out bean classes with configuration entity class.
#Configuration - Creates a Spring bean of configuration stereotype.
#EnableConfigurationProperties - Creates a binding between a configuration entity class and Spring configuration stereotype so that after injection within a service properties can be retrieved easily.
If we look at the code below:
#Configuration #EnableConfigurationProperties #ConfigurationProperties(prefix="ar1")
public class ar1Settings { }
#Configuration tells Spring to treat this as a configuration class and register it as a Bean
#EnableConfigurationProperties tells Spring to treat this class as a consumer of application.yml/properties values
#ConfigurationProperties tells Spring what section this class represents.
My understanding is that if you don't need to specify the section of the property file, then #ConfigurationProperties can be omitted.
#EnableConfigurationProperties imports EnableConfigurationPropertiesRegistrar which enables support for #ConfigurationProperties annotated beans.
#ConfigurationProperties is an annotation for externalized configuration, it is to be applied to a bean configuration class or method annotated with #Bean eg
#ConfigurationProperties(prefix = "some-prefix")
public SomePrefix prefixBean() {
return new SomePrefix();
}
To load the properties and bind them to properties within the method or the class that match the prefix.
ps: some-prefix binds to SomePrefix because of spring's support for Relaxed binding.
Before springboot 2.2, You could do either of the following:
#Configuration
#ConfigurationProperties(prefix = "some-prefix")
public class ConfigProperties {
//...some code
}
or
#Configuration
#EnableConfigurationProperties(SomeClassToBeBounded.class)
public class ConfigProperties {
along with
#ConfigurationProperties(prefix = "some-prefix")
public class SomeClassToBeBounded{
//...some code
}
From springboot 2.2
You can do it in a much easier way:
#ConfigurationProperties(prefix = "some-prefix")
#ConfigurationPropertiesScan
public class ConfigProperties {
//...some code
}
With this, the classpath scanner enabled by #SpringBootApplication finds the ConfigProperties class, even though we didn't annotate this class with #Component.

Is there a way to register a repository base class with a spring boot auto configuration?

We have a base JPA repository class with some additional utility methods that we use in our projects. Following the Spring Data JPA documentation we created the class and use the #EnableJpaRepositories annotation in a configuration class as in the following example:
#Configuration
#EnableJpaRepositories(basePackageClasses = MyApplication.class,
repositoryBaseClass = MyJpaRepositoryImpl.class)
public class SpringDataJpaMyRepositoryConfiguration {
}
We also set the basePackageClasses attribute so our repositories are found, as the configuration class is not in the application root package. Everything works as expected, so no problems so far.
Now we would like to create a spring boot starter to add the repository base class to our projects without further configuration, but we don't know how to do it. If we create an AutoConfiguration class with the EnableJpaRepositories annotation setting the repositoryBaseClass attribute, the automatic repository lookup strategy which looks for repositories under the class annotated with #SpringBootApplication doesn't work anymore.
And we can't use the basePackageClasses attribute as we don't know the main class or package of the project using the autoconfiguration.
Is there any way to do this? Maybe by redefining some bean in our autoconfiguration?
The ideal way would be something that allows to set the repository base class without having to define all the Spring Data JPA autoconfiguration again.
This question has driven me crazy at the time, so I thought I could help you on this.
Basically, the idea is to:
Create a configuration class for your Jpa config
Add #EntityScan and #EnableJpaRepositories referencing the same configuration class as the basePackageClass
Import this configuration class in your autoconfiguration
Create an annotation that you can then reuse where you need your Jpa config
In your example, you're using your Spring application class as your base for scannning.
I've put up a sample project to POC the main ideas at https://github.com/rdlopes/custom-jpa-demo
In the example, there's a project for the JPA entities/repositories exposing a JPA configuration:
#Configuration
#EntityScan(basePackageClasses = JpaConfiguration.class)
#EnableJpaRepositories(basePackageClasses = JpaConfiguration.class,
repositoryBaseClass = BaseRepositoryImpl.class)
public class JpaConfiguration {
}
Be careful with the common implementation for your repositories, you need to show a special signature:
#NoRepositoryBean
public class BaseRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements BaseRepository<T, ID> {
public BaseRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
#Override
public String someCustomMethod(ID id) {
return "Class for entity of id " + id + " is: " + getDomainClass().getSimpleName();
}
}
You can then create your auto configuration as such:
#Configuration
#ConditionalOnClass(CustomJpaRepositories.class)
#Import(JpaConfiguration.class)
public class JpaCustomAutoConfiguration {
}
Providing an annotation to keep things tidy and use it where you need JPA:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
public #interface CustomJpaRepositories {
}
Using your JPA classes will be as simple as having this annotation where you call your JPA repositories:
#SpringBootApplication
#CustomJpaRepositories
public class CustomJpaSampleApplication {
public static void main(String[] args) {
SpringApplication.run(CustomJpaSampleApplication.class, args);
}
#Bean
public CommandLineRunner dataInitializer(UserRepository userRepository) {
return args -> {
User user1 = new User();
user1.setName("user 1");
userRepository.save(user1);
User user2 = new User();
user2.setName("user 2");
userRepository.save(user2);
userRepository.findAll()
.forEach(user -> System.out.println(
userRepository.someCustomMethod(user.getId())));
};
}
}
Hope this helps you getting passed the head scratching moments :-)
EDIT: I've pretty much rewritten my answer - I misunderstood the original question
It's not the nicest solution but the only way I can see this working is by using SpEL inside #EnableJpaRepositories.
This can then go in your auto-configuration and use #ConditionalOnProperty to only auto-configure if the base package property is set
#Configuration
#ConditionalOnProperty("repositories-base-packages")
public class BaseRepositoryAutoConfiguration {
#Configuration
#EnableJpaRepositories(
repositoryBaseClass = MyJpaRepositoryImpl.class,
basePackages = "${repositories-base-packages}"
)
public static class JpaRepositoriesConfig { }
}
Then make sure you have a application.properties or application.yml which defines repositories-base-packages inside your application.
Not sure how you'd declare multiple base packages, my SpEL knowledge is primitive so not sure if it would even be possible.

What is the use case of #Import annotation?

According to official doc:
Annotation Type Configuration
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions...
#Configuration classes may be composed using the #Import annotation,
not unlike the way that works in Spring XML. Because
#Configuration objects are managed as Spring beans within the
container..
But i can also use #Configuration annotation without #Import. I have tested the code listed below and it works as expected. So what is the purpose to use #Import?
DispatcherServletInitializer
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebMvcConfigurerAdapter
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "package.name" })
// #Import(OptionalConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
// ...
}
OptionalConfig
#Configuration
public class OptionalConfig {
#Bean(name = "myClass")
public MyClass myClass() {
return new MyClass();
}
}
Service
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyClass myClass; // yes, it works
// ...
}
If component scanning is enabled, you can split bean definitions in multi #Configuration classes without using #Import. And you don't need to provide all of them to the application context constructor.
I think the main purpose for #Import is to provide you a way to simplify multiple configurations registration if you'd like to avoid component scanning (as of Spring Framework 4.2, per reference manual).
There's a note in Spring Reference Documentation about #Import usage:
As of Spring Framework 4.2, #Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you’d like to avoid component scanning, using a few configuration classes as entry points for explicitly defining all your components.
Thus far, we've seen how to break up bean definitions into multiple #Configuration classes and how to reference those beans across #Configuration boundaries. These scenarios have required providing all #Configuration classes to the constructor of a JavaConfigApplicationContext, and this is not always ideal. Often it is preferable to use an aggregation approach, where one #Configuration class logically imports the bean definitions defined by another.
The #Import annotation provides just this kind of support, and it is the direct equivalent of the <import/> element found in Spring beans XML files.
http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
I found a use of using the #Import annotation. I don't think it's the use case.
If you are developing a set of libraries using Spring, probably you don't have a SpringBootApplication. So, you have not enabled any auto-scan to resolve beans.
If a bean declared in a configuration file in the library library-a is referred through dependency injection in library-b you need to use #Import to tell Spring how to resolve the bean.
As we said, in library-a you have this configuration file.
#Configuration
public class ConfigurationA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
In library-b you must have this configuration file if you want to use BeanA
#Configuration
#Import(ConfigurationA.class)
public class ConfigurationB {
#Bean
public BeanB beanB(BeanA beanA) {
return new BeanB(beanA);
}
}
Hope it helps.
With component scanning enabled it's difficult to immediately see where #Import adds value if your view of the world is limited to your own application and its packages. Where it can help is if you are importing bean libraries with their own package structure that you don't want to component scan.
You can place such libraries on your classpath and use #Import to cherry-pick #Configuration classes from within them. That's why it's often referred to as composition because you are composing your #Configuration class from multiple sources.
A typical use case of #Import is when teams develop REST services and realize they need some common configuration.
Every service will have its own namespace i.e. org.mybusiness.specific and will have its #SpringBootApplication (and therefore component scan root) start at this package.
On the other hand, the common library will have its namspace set to org.mybusiness.common and will therefore be out of reach for component scanning.
Thus the need to reference the common configuration class using #Import(CommonConfig.class)
#Import works great when for some reasons you need to register not all but some of the components from a package. In such case #ComponentScan would be too complicated.
See details in the Spring boot component scan include a single class question.

How to avoid the use of Spring annotations in my java beans and use them only in the configuration class?

I'm new in Spring and this is my first post here, I'm starting to use spring annotations, I was able to cofigure my project using XML configuration and now I'm able to configure it using only annotations and avoiding XML.
But my current need is to avoid the use of annotations in my java classes (beans) and use it only in my AppConfig.java class which I use to configure Spring.
This is my current working configuration:
AppConfig.java
#Configuration
#ComponentScan(basePackageClasses= {BlocBuilder.class,... all my classes go here})
public class AppConfig {
#Bean
#Scope("prototype")
public BlocBuilder blocBuilder(){
return new BlocBuilder();
}
}
And this is one of my java classes.
BlocBuilder.java
public class BlocBuilder {
#Autowired
#Qualifier("acServices")
private SomeInterface someInterface;
public SomeInterface getSomeInterface() {
return someInterface;
}
public void setSomeInterface(SomeInterface someInterface) {
this.someInterface = someInterface;
}
What I want to achieve is to avoid the use of annotations in my classes for example in my BlocBuilder.java class I don't want to have annotations and move them to my config class.
How can I manage it?
Any help whould be really appreciated.
Constructor injection is what you're looking for. Create a 1-arg constructor in BlocBuilder which takes SomeInterface type argument. And then in config class, pass it as argument:
#Configuration
#ComponentScan(basePackageClasses= {BlocBuilder.class,... all my classes go here})
public class AppConfig {
#Bean
public SomeInterface someInterface() {
return new SomeInterfaceImpl();
}
#Bean
#Scope("prototype")
public BlocBuilder blocBuilder(){
return new BlocBuilder(someInterface());
}
}
I wouldn't recommend a config class. It'll couple all your beans together.
Spring configuration doesn't need to be an all or nothing thing: annotations or XML. You can mix and match as you choose. Put annotations in the classes you want and use XML for the rest.

Categories

Resources