Reputation: 15205
I'm working on an experimental method that will take a bean name, property name, and value expression, and use the Spring SPeL to assign that property of that bean with that value. The class with this method is a ManagedResource, so I can reach it from JMX.
I then defined another simple class, gave it a property and a Component annotation. I also Autowire this class into the jmx bean class, just to verify the bean of that type exists.
I then fired up the SpringBoot service.
I then called the method from VisualVM.
It failed, saying it couldn't find the bean with that name.
So, now with more detail.
Here's the first class:
@Component
@ManagedResource
public class JMXDemonstration {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private SomeRandomThing thing;
@Value("${jmxDemonstration.name}")
private String name;
@ManagedAttribute
public String getName() { return name; }
@ManagedAttribute
public void setName(String name) { this.name = name; }
@ManagedOperation
public String buildHelloWorldMessage() {
return "Hello, " + name + ": " + thing.getId();
}
@ManagedOperation
public void assignValueToBeanProperty(String beanName, String propertyName, String expression) {
Object bean = applicationContext.getBean(beanName);
ExpressionParser parser = new SpelExpressionParser();
SimpleEvaluationContext evalContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression(propertyName).setValue(evalContext, bean, expression);
}
}
And here's the other class:
@Component
public class SomeRandomThing {
private String id;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
}
When I call the method from VisualVM, I pass "SomeRandomThing", "id", and "xxx".
This fails with:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'SomeRandomThing' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685)
I also set a breakpoint in the method and looked at "this", and confirmed that there is a valid "thing" property (if there wasn't, the service wouldn't have started).
So do I have the default bean name algorithm wrong? It seems hard to believe, because I also ran a test of "JMXDemonstration", "name", and "George", and that worked perfectly fine.
Update:
Also note that calling "applicationContext.getBean(SomeRandomThing.class)" returns the bean instance of that class, and also calling "applicationContext.getBeanDefinitionNames()" returns an array that does not contain "SomeRandomThing", but it does contain "JMXDemonstration".
Why is SomeRandomThing available as a bean through autowiring and through the type, but not through it's bean name?
Update:
Oh, because the bean name is "someRandomThing", not "SomeRandomThing". I guess I would have expected the former originally, but when I saw that the bean name for "JMXDemonstration" was "JMXDemonstration", I assumed it would then be "SomeRandomThing", not "someRandomThing".
Upvotes: 0
Views: 8763
Reputation: 39988
Bean Naming
@Component is a class level annotation. During the component scan, Spring Framework automatically detects classes annotated with @Component.
@Component
class CarUtility {
// ...
}
By default, the bean instances of this class have the same name as the class name with a lowercase initial. On top of that, we can specify a different name using the optional value argument of this annotation.
Annotation based Configuration
For stereotype annotation based bean, if the name is not explicitly specified with the value field of stereotype annotations, then the name is again generated by
AnnotationBeanNameGenerator
which is an implementation of the BeanNameGenerator strategy interface here
If the annotation's value doesn't indicate a bean name, an appropriate name will be built based on the short name of the class (with the first letter lower-cased). For example:
com.xyz.FooServiceImpl -> fooServiceImpl
With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by doc java.beans.Introspector.decapitalize (which Spring uses here).
Upvotes: 1