Please provide specialized BeanNameGenerator
implementations and provide a property to choose one. E.g spring.bean-name-gernerator=package-aware
(or default).
Problem
The default implementation fails, when two Component
s have the same class name. This issue raised at spring framework also suggests the use of a custom BeanNameGenerator
.
The code needed for the custom implantation is about 10 Lines in the main class. The registration needs to be done twice if a Servlet is used and can easily be missed. Also needs special treatment for @SpringBootTest
classes.
UPDATE: ... just a view Lines! But still a little anoying. Learned to use the @ComponentScan(nameGenerator = CustomGenerator.class )
(updated my example)
I think "good" structured code is likely to have classes with the same name, but in different packages. Especially when dealing with bounded contexts (DDD) where a Entity xyz can have different meanings in different contexts. These Entities are in no way beans, but they will raise some services like xyzService, xyzMapper, etc.
Comment From: philwebb
I'm not too keen to add an application.properties
entry since class name resolution wouldn't be type safe. I think we could offer the same nameGenerator
attribute on @SpringBootApplication
instead. Would that work for you?
Comment From: torsten-github
Yes, I agree. That seems to be a more appropriate place.
Comment From: snicoll
I've started to look at this one and I am now wondering if that's worth it. SpringApplicationBuilder
already offers the infrastructure to customize a BeanNameGenerator
with a one-liner, e.g.
new SpringApplicationBuilder(DemoApplication.class)
.beanNameGenerator(new CustomBeanNameGenerator())
.run(args);
It should be noted that specifying an attribute override on @SpringBootApplication
takes precedence to this (which was surprising).
@torsten-github what is wrong with the approach above? It also has the advantage of you providing the concrete instance (whereas the annotation approach forces you to have a default public constructor that we'd call to create it).
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: torsten-github
@torsten-github what is wrong with the approach above?
In my case, using a SpringBootServletInitializer
, this does not work, since SpringApplication Builder...
is executed on e.g. JBoss. Luckily the @ComponetnScan(nameGenerator = ...)
works in both worlds :)
Main thought
The need for a package aware BeanNameGenerator
is very likely. Please also see my issue at spring-framework
Would be nice to choose it out of the box. Maybe my own implementation is not spring-like, or misses some internal points I'm not aware of at the moment...? Makes me feel better, to know I'm use a spring-framework generator, which can handle same class names in different packages.
Comment From: snicoll
In my case, using a SpringBootServletInitializer, this does not work, since SpringApplication Builder... is executed on e.g. JBoss. Luckily the @ComponetnScan(nameGenerator = ...) works in both worlds :)
Why is that? There is a configure
method that takes the builder, which allows you to invoke the method I've mentioned.
Would be nice to choose it out of the box. Maybe my own implementation is not spring-like, or misses some internal points I'm not aware of at the moment...? Makes me feel better, to know I'm use a spring-framework generator, which can handle same class names in different packages.
I think you've lost me. I am merely suggesting a mechanism to set the BeanNameGenerator
already exists using the builder. This isn't different from whatever the extra attribute on @SpringBootApplication
would do.
Comment From: torsten-github
@snicoll What I'm trying to ask for, is: Please provide a package aware BeanNameGenerator class which I can setup. Instead of having me to write my own.
If you look at the spring-framework issue I mentioned above, you can see the code I need to write:
@SpringBootApplication
@ComponentScan(nameGenerator = CustomGenerator.class)
public class App extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application
.sources(App.class);
}
public static class CustomGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
}
If I get your point, you want me to configure the BeanNameGenerator in the static main
method. This will not work, on a Server, since it is run as a Servlet.
Comment From: torsten-github
By the way. If anyone sees a problem with above mentioned code please let me know. I'm not an expert in Spring-framework.
Comment From: snicoll
Please provide a package aware BeanNameGenerator class which I can setup. Instead of having me to write my own.
That's not the question I am asking, if you want to advocate for that more, please add a comment in the relevant issue.
If I get your point, you want me to configure the BeanNameGenerator in the static main method. This will not work, on a Server, since it is run as a Servlet.
No, I am not asking you to do that. You've exactly the setup I mentioned in my previous comment with a configure
method. This should work:
@SpringBootApplication
public class App extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application
.beanNameGenerator(new CustomGenerator.class)
.sources(App.class);
}
public static class CustomGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
}
I'll keep this one open until we get a chance to discuss it.
Comment From: wilkinsona
I think it makes sense to provide an alias for nameGenerator
on @SpringBootApplication
. Configuring the name generator in the main method (or configure method for war deployments) means that the configuration is lost when running an integration test. The alternative is to use @ComponentScan
which is cumbersome when using sliced tests as the exclude filters need to be re-declared.
Comment From: snicoll
Ah, that's a good point Andy, I missed that use case.
Comment From: torsten-github
That's not the question I am asking, if you want to advocate for that more, please add a comment in the relevant issue.
Oh, I thought this issue provide specialized BeanNameGenerator implementation... raised by me is the place for that. I hoped the boot project would make the configuration part a one-liner.
But, never mind, I think I made my point. And I'm not sure if I can still follow. It's OK for me. Thank you very much for your time.
This should work:
Ok, now I see. Missed that Builder in the parameter. I used to have the Builder in the main method.
Just for completeness: I changed my code from Annotation to your suggestion. But it will no longer start without a Server (using SpringBoot startup) not sure about the name. I think it's the build in tomcat?!
Comment From: torsten-github
I think we could offer the same nameGenerator attribute on @SpringBootApplication instead. Would that work for you?
Ah - now I get it. Sorry. But it still implied for me the "provide ... implementation" part. Isn't that a core frature of spring-boot? Would be nice to here a yes or no. Thanks
Comment From: snicoll
@torsten-github I already provided a link to the relevant issue. There is no reason for having that default in Spring Boot since Spring Framework can open the one it has privately at the moment. For further updates on the default implementation, please follow-up on this issue.
Comment From: sbrannen
FYI: https://github.com/spring-projects/spring-framework/commit/b4c91e7dac91c6176a5412ed930d2c048cc5c42f introduced a new FullyQualifiedAnnotationBeanNameGenerator
that will be available in Spring Framework 5.2.3.