Bean with same name in multiple dependency conflict issue - java

New to spring boot.
We have reusable beans in multiple base dependencies. Since, we need those reusable functionality, we are inheriting those jars in pom.xml.
Ex:
Jar1:
ClassName: UserInfo, RequestInterceptors, ClassName3, ClassName4, ClassNameN.
PackageName: com.somename1.base.comps
Jar2:
ClassName: UserInfo, RequestInterceptors, ClassName3, ClassName4, ClassNameN.
PackageName: com.somename2.base.comps
Since the class name is similar, creates conflict issue, when building.
Unfortunatly, we cannot able to change any class names, since someother teams are using these jar. No permission to create another version of these jars.
Is there any way to include both jar without excluding these conflicting components in #EnableAutoConfiguration or #ComponentScan
annotations?
Could someone share some advice here.

The solution here would be to name your beans. The reason for the conflict would be that the names of the classes are the same. For example:
#Configuration
public class Config{
public com.somename2.base.comps.UserInfo somename2UserInfo(){
return new com.somename2.base.comps.UserInfo();
}
public com.somename1.base.comps.UserInfo somename1UserInfo(){
return new com.somename1.base.comps.UserInfo();
}
}
The beans would then be named somename1UserInfo and somename2UserInfo and you can use these names instead when autowiring, e.g.:
public class SomeImpl{
#Autowired
#Qualifier("somename1UserInfo")
UserInfo userInfo;
/*
* Or you can do this
*/
#Resource(name = "somename1UserInfo")
UserInfo theSameUserInfo
}
This will allow you to autowire whichever bean should be used in whichever class. It may not be ideal to have to declare a qualifier for every place the classes are used, so if one implementation is primarily used, consider looking into the #Primary annotation.

Related

Does Spring (Boot) have a special treatment for package-names .model and .repo?

I have a #SpringBootTest that autowires a #Repository which returns an #Entity. I have no specific ComponentScanning declared.
This works as long as the Repo is in a package my.repo and the entity and test are in package my.model
If I move all three to package my.repo I get "Not a managed type" for the model.
If I move all three to package my.model I get "No qualifying bean of type" for the repo.
I know about the recommended package structure. But is it more than a recommendation? Are there strict rules for the packages hardcoded?
As long as your #Repository is in the same package with other classes/entities, it doesnt need any scanning. But yes if you choose to use a different package, you must tell spring to scan your specific packages for your #Repositoy(ies). So somewhere in your #Configuration class, use this:
#ComponentScan(basePackages = "someuniq.packagename.goeshere").
For an example - like this.

Spring Boot: How to include a configuration class that is not in my base package?

I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.

Spring boot: Exclude auto configuration based on environment variable

In spring boot we may exclude specific auto-configuration classes such that they will never be applied. Example with annotation based configuration:
#SpringBootApplication(exclude = OneConfiguration.class)
What I want is to provide an environment variable with a value that will determine some auto configurations that will be excluded. Any ideas how to achieve that?
You can exclude this configuration class by default:
#SpringBootApplication(exclude = OneConfiguration.class)
then extend it and make it conditonal:
#Configuration
#ConditionalOnProperty(name = "one.configuration.condtion", havingValue = "some-value")
public class OneConfigurationConditional extends OneConfiguration { }
Create class extending Condition checking value of enviroment variable:
public class AutoConfigurationCondition extends Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getenv("EXCLUDE").equals("yes");
}
}
Then you can use it in Configuration class:
#Configuration
#Conditional(AutoConfigurationCondition.class)
#EnableAutoConfiguration(exclude = {OneConfiguration.class})
class AutoConfigurationExcludingConf {}
I managed to do what I wanted with profiles :-( So I have few files now - e.g. application-artemis.yaml and application-activemq.yaml. In each I've defined corresponding spring.autoconfigure.exclude property. I'm not proud with this solution though it works fine so far and looks more or less neat compared to other things that I did :-) .
What I've tried besides that:
Managing the value with an environment variable, e.g.:
spring:
autoconfigure:
exclude: ${ABC:com.example.Myautoconfiguration}
This does not work and I've even reported it as an issue. But it seems that I can't rely on expression for this property. Strange enough it works for other properties...
I've played around with the suggestion of #Randal Flagg but somehow I couldn't get it up and running - I'm using #SpringBootApplication, the documentation says that I can use EnableAutoConfiguration only once, etc.
I've tried with my own TypeFilter but this is also not an option - autoconfigurations have special treatment during component scan and the design does not seem very extensible there. At least I could not find a nice way to plug in.

ClassPathScanningCandidateComponentProvider.findCandidateComponents have wrong class name

I have a java project containing a spring boot application called processor. This project depends on a project called rules and a project called service. Every project has the same package pattern - my.com.package.
The processor and rules projects both contain classes annotated with a custom annotation #Condition. The annotation interface is annotated with #Retention(RetentionPolicy.RUNTIME). When I scan for classes annotated with #Condition from service or processor like this
private ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Condition.class));
for (BeanDefinition bd : scanner.findCandidateComponents("my.com")) {
try {
Class<?> c = Class.forName(bd.getBeanClassName());
Condition condition = c.getAnnotation(Condition.class);
register(condition);
} catch (ClassNotFoundException | IOException e) {
logger.error(e.getLocalizedMessage());
}
}
The classes annotated with #Condition in the processor project have the correct class name(my.com.package.x.Class), but the classes annotated with #Condition in the rules project have an incorrect fully qualified class name(my.com.Class) and it only finds 2 out of 5 class names in the project that have the annotation.
If I change the argument to scanner.findCandidateComponents to the full package path in the rules project (my.com.package.rules) while scanning in either processor or service the scanner finds no candidates. If I use my.com.* as the argument it only finds the candidates in the processor project.
I saw a similar question here Using ClassPathScanningCandidateComponentProvider with multiple jar files? and the solution was to pass the class loader to the component provider. I tried getting the class loader of the class doing the scanning and passing it to the provider like this
scanner.setResourceLoader(new PathMatchingResourcePatternResolver(classLoader));
and it didn't change any results for me.
Silly mistake, the problem was I had the wrong version of the rules project defined in the pom for my processor project so it was using an older version of the code.
However this
Condition condition = c.getAnnotation(Condition.class);
returned null for the classes taken from the jar, so this concerns me a little if this code isn't being run from source in my workspace.

How to scan multiple paths using the #ComponentScan annotation?

I'm using Spring 3.1 and bootstrapping an application using the #Configuration and #ComponentScan attributes.
The actual start is done with
new AnnotationConfigApplicationContext(MyRootConfigurationClass.class);
This Configuration class is annotated with
#Configuration
#ComponentScan("com.my.package")
public class MyRootConfigurationClass
and this works fine. However I'd like to be more specific about the packages I scan so I tried.
#Configuration
#ComponentScan("com.my.package.first,com.my.package.second")
public class MyRootConfigurationClass
However this fails with errors telling me it can't find components specified using the #Component annotation.
What is the correct way to do what I'm after?
Thanks
#ComponentScan uses string array, like this:
#ComponentScan({"com.my.package.first","com.my.package.second"})
When you provide multiple package names in only one string, Spring interprets this as one package name, and thus can't find it.
There is another type-safe alternative to specifying a base-package location as a String. See the API here, but I've also illustrated below:
#ComponentScan(basePackageClasses = {ExampleController.class, ExampleModel.class, ExmapleView.class})
Using the basePackageClasses specifier with your class references will tell Spring to scan those packages (just like the mentioned alternatives), but this method is both type-safe and adds IDE support for future refactoring -- a huge plus in my book.
Reading from the API, Spring suggests creating a no-op marker class or interface in each package you wish to scan that serves no other purpose than to be used as a reference for/by this attribute.
IMO, I don't like the marker-classes (but then again, they are pretty much just like the package-info classes) but the type safety, IDE support, and drastically reducing the number of base packages needed to include for this scan is, with out a doubt, a far better option.
Provide your package name separately, it requires a String[] for package names.
Instead of this:
#ComponentScan("com.my.package.first,com.my.package.second")
Use this:
#ComponentScan({"com.my.package.first","com.my.package.second"})
Another way of doing this is using the basePackages field; which is a field inside ComponentScan annotation.
#ComponentScan(basePackages={"com.firstpackage","com.secondpackage"})
If you look into the ComponentScan annotation .class from the jar file you will see a basePackages field that takes in an array of Strings
public #interface ComponentScan {
String[] basePackages() default {};
}
Or you can mention the classes explicitly. Which takes in array of classes
Class<?>[] basePackageClasses
You use ComponentScan to scan multiple packages using
#ComponentScan({"com.my.package.first","com.my.package.second"})
You can also use #ComponentScans annotation:
#ComponentScans(value = { #ComponentScan("com.my.package.first"),
#ComponentScan("com.my.package.second") })
I use:
#ComponentScan(basePackages = {"com.package1","com.package2","com.package3", "com.packagen"})
make sure you have added this dependency in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Use
#ComponentScan(basePackages = {"package1", "package2"})
define it at top before class.
Edit: the brackets must be around all the base packages, not a pair of brackets per package

Categories

Resources