I'm trying to inject an optional bean into a service. The bean is successfully created but consistently after the dependent service. Any thoughts why this goes wrong?
Spring Boot 2.5.0 is used. Without using the Optional, everything works fine.
Application.java
#SpringBootApplication
#Configuration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public Optional<SomeClass> someClass() {
System.out.println("creating bean");
return Optional.of(new SomeClass("someName"));
}
}
SomeClass.java
#Value
public class SomeClass {
String name;
}
SomeService.java
#Service
public class SomeService {
public SomeService(Optional<SomeClass> some) {
System.out.println(
some.map(SomeClass::getName).orElse("empty")
);
}
}
Output:
empty
creating bean
When autowiring dependencies the Optional should be used at the injection point, to indicate that a bean might or might not be available. The bean cannot be available for different reasons (conditional configuration, or just a setup error leading to null for a bean).
Creating an Optional bean is something different then using Optional for the injection point.
Ideally, you would just create the bean, which would be created eagerly, if processing can fail, handle it and return null. Now when using Optional at the injection point it will see null and Optional.empty() will automatically be injected.
Related
As far as I understood, Spring manages autowiring mechanism with AutowiredAnnotationBeanPostProcessor on postProcessBeforeInitialization stage. But how does it inject proxies that ought to be created on postProcessAfterInitialization stage?
EDIT 1
Suppose I have this Spring configuration
#Service
class RegularBean {
// injected on postProcessBeforeInitialization stage
#Autowired
private TransactionBean tBean;
// invoked in between of postProcessBeforeInitialization and postProcessAfterInitialization
#PostConstruct
void init() {
tBean.transactionMethod();
}
}
#Service
class TransactionBean {
// transactional proxy is created on postProcessAfterInitialization stage
#Transactional
public void transactionMethod() { ... }
}
Transactional proxy is created on postProcessAfterInitialization stage. But #PostConstruct is called right before it. Is injected tBean wrapped with transactional proxy? If it so, then why? Because it should not be. If it is not wrapped, then how transactions are going to be handled in the future?
Suppose that I replace field-injection with constructor-injection. Will it change the behavior somehow?
When you use autowiring on method or field ,Spring Container not always create and inject the required field/attribute instance. Spring internally create smart proxies and inject the proxies to your bean. This smart proxy will resolve the bean at later point and delegate call to actual bean during method invocation. This is a common strategy we use to resolve request and session scoped beans to singleton instance (Say service layer beans) using Scope annotation.
Adding small snippet to show Spring internally create proxies for Object. Consider a classic circular dependency case when we use Constructor injection.
#Component
public class CircularDependencyA {
private CircularDependencyB circB;
public CircularDependencyA(#Lazy CircularDependencyB circB) {
System.out.println("CircularDependencyA Ctr ->"+circB.getClass().getName());
this.circB = circB;
}
}
#Component
public class CircularDependencyB {
private CircularDependencyA circA;
public CircularDependencyB(CircularDependencyA circA) {
System.out.println("CircularDependencyB Ctr ->"+circA.getClass().getName());
this.circA = circA;
}
}
#Configuration
#ComponentScan(basePackages = { "com.example.springdemo.cd" })
public class TestConfig {
}
public class TestCircularDependency {
public static void main(String[] args) {
try(AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(TestConfig.class);){
}
}
}
Based on our hints Spring is creating Proxy(using CGLIB) for CircularDependencyB Object and see the op from the CircularDependencyA Constructor
CircularDependencyA Ctr ->com.example.springdemo.cd.CircularDependencyB$$EnhancerBySpringCGLIB$$e6be3b79
Thanks
I've a Spring Boot application (1.5.9) where I implement two converters and an component making use of one of these
#Component
public class Converter1 implements Converter<A,B> {
public B convert(A a) {
...
}
}
#Component
public class Converter2 implements Converter<B,C> {
public C convert(B b) {
...
}
}
#Component
public class Converter1Consumer {
private Converter<A,B> converter
public Converter1Consumer(Converter<A,B> converter) {
this.converter = converter;
}
...
}
Everything works fine and is injected correctly until AOP is in use. Then I receive following exception:
NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.core.convert.converter.Converter<?, ?>' available: expected single matching bean but found 2: converter1,converter2
Everything works fine again, when I change the name from converter to converter1. I can understand latter as Spring matches parameter name to the bean name. Can somebody explain me the reason, AOP converting the parameter definition to Converter<?,?> in the Converter1Consumer?
I have a instance of a class that is created outside of Spring that I'd like to have access to Spring beans so that it may fire an event and be observed by Spring beans. I'm not using Spring web, my application is running from the command-line via spring boot.
The only option you have is to expose the Spring context of your application using a static method so that the object that is not managed by Spring can use it to get references to managed beans it needs.
Start with a wrapper for the context. Create a regular managed bean which required reference to the context in its constructor. The reference is assigned to a static class field, which also has a static getter:
#Service
class ContextWrapper {
private static ApplicationContext context;
#Autowired
public ContextWrapper(ApplicationContext ac) {
context = ac;
}
public static ApplicationContext getContext() {
return context;
}
}
Use the static getter to get access to context in the object which is not managed by Spring and get reference to beans using methods available in the context:
SomeBean bean = ContextWrapper.getContext().getBean("someBean", SomeBean.class);
// do something with the bean
Last thing you need is communication channel from Spring beans to non-managed object. For instance, the SomeBean can expose a setter which will accept the non-managed object as a parameter and store the reference in a field for future use. The object mast get references to managed beans using the static context accessor mentioned above and use the setter to make the bean aware of its existence.
#Service
class SomeBean {
// ... your bean stuff
private SomeClass someclass;
public void setSomeClass(Someclass someclass) {
this.someclass = someclass;
}
private void sendEventToSomeClass() {
// communicate with the object not managed by Spring
if (someClass == null) return;
someClass.sendEvent();
}
}
You can inject by constructor that spring beans, something like:
#Service
class Bean {
...
}
class NotBean {
private Bean bean;
public NotBean(Bean bean) {
this.bean = bean;
}
// your stuff (handle events, etc...)
}
I have the following components, in two different files:
#Component
public class Chauffeur {
Car car;
public Chauffeur(Car car){
this.car = car;
}
public void go(){
System.out.println("Chauffeur");
car.drive();
}
}
#Component
public class Car{
public void drive() {
System.out.println("Drive car");
}
}
the following configuration file:
#Configuration
#ComponentScan
public class DriveableConfiguration {
}
and the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=DriveableConfiguration.class)
public class DriveableTest {
#Autowired
Chauffeur chauffeur;
#Test
public void chauffeurTest(){
chauffeur.go();
}
}
All the classes above are in the same package and the test is passing.
In the test I annotated chauffer with #Autowired, which should mean that the Spring container looks after the creation of the instance of Chauffeur without the developer needing to explicitly instantiate it.
Now, the constructor for Chauffer needs an instance of Car, so there is no default constructor for that class. Nonetheless the container creates it, injecting the required instance in the constructor.
Is the #Autowired saying to the container to instantiate the element with whatever (Components, Beans) it can provide, included parameters in the constructor? If so, in what case is it needed to use #Autowired to annotate a constructor?
Only if you use Spring 4.3+. In such a case #Autowired on constructor is optional if you have one non default constructor.
You can check the example here.
So as of 4.3, you no longer need to specify an explicit injection annotation in such a single-constructor scenario. This is particularly elegant for classes which otherwise do not carry any container annotations at all, for example when programmatically registered
For versions lower than 4.3 you will an exception will be thrown:
the container will throw an exception looking for a default
constructor, unless you explicitly indicate autowire mode
‘constructor’ in your bean definition setup (e.g. in an XML )
I'm developing a Spring Boot application and am trying out using Java annotation-based bean creation (using #Configuration and #Bean) rather than the familiar old XML-based bean creation. I'm puzzled though. If I attempt to create a bean in XML but fail to set an #Required property I get a BeanInitializationException when the application context is created. In my trials so far with annotation-based bean creation though this does not seem to be the case.
For example:
public class MyClass {
...
#Required
public void setSomeProp(String val){
}
}
Then in Spring XML:
<bean class="MyClass"/>
This will blow up during application startup (and IntelliJ flags it) because the required property is not set. But the same does not seem to be true of this:
#Configuration
public class MyConfig {
#Bean
public MyClass myClass() {
return new MyClass();
}
}
This application starts up just fine even though the required property is not ever set. I must be missing something here, because this seems like a pretty key feature in Spring.
UPDATE
I did some digging & debugging and it turns out that the bean definition is somehow being flagged to skip checking that #Required fields are set. In the Spring class 'RequiredAnnotationBeanPostProcessor' the boolean method 'shouldSkip()' is returning true for beans created this way. When I used the debugger to force that method to return false bean creation did indeed blow up with the expected exception.
Seeing as I'm making a pretty basic Spring Boot application I'm inclined (as Zergleb suggests) to submit this as a bug.
UPDATE 2
Some further debugging has revealed that even if the field is getting set forcing the check still throws the same exception, as if it hadn't been set. So perhaps dunni is correct and there is no way for this to work with #Bean notation.
As you said I also could not get #Required to run as expected this may be a bug and needs to be reported. I have a few other suggestions that did work for me.
Class annotated with #Configuration
//With the bean set up as usual These all worked
#Bean
public MyClass myClass() {
return new MyClass();
}
When you annotate the class #Component and load using component scanning works as expected.(The component scanning part is important you either need your #Configuration class to either have #ComponentScan or perhaps remove #Configuration and replace with #SpringBootApplication and this will enable scanning for components without needing to wire them up using #Bean configs)
#Component // Added this
public class MyClass {
...
#Required //Failed as expected
public void setSomeProp(String val){
}
}
Use #Autowired(required=true) //Fails with BeanCreationException //No qualifying bean of type [java.lang.String] found for dependency
//No more #Component
public class MyClass {
...
#Autowired(required=true) //Fails
public void setSomeProp(String val){
}
}
#Autowired required=false //Does not crash
public class MyClass {
...
#Autowired(required=false) //Simply never gets called if missing
public void setSomeProp(String val){
}
}
#Value //Does not work if test.property is missing // Could not resolve placeholder 'test.property' in string value "${test.property}
public class MyClass {
#Value("${test.property}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp;
}
}
#Value with default value//Does not crash // When getSomeProp is called it returns "My Default Value"(Unless you have test.property=Anything in your application.properties file then it returns "Anything"
public class MyClass {
#Value("${test.property:My Default Value}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp; //Returns "My Default Value"
}
}
Inside your #Configuration file also fails if it cannot find anything to populate String someProp in the myClass method
#Bean
public MyClass myClass(String someProp) { //Fails being unable to populate this arg
MyClass myObj = new MyClass();
myObj.setSomeProp(someProp);
return ;
}
If course this won't work, since you create the object of MyClass yourself (new MyClass()), thus the annotations are not evaluated. If you create a bean with a #Bean method, the container will only make sure, that all dependencies are there (method parameters) and that the bean scope is adhered to, meaning if it's a singleton bean, only one bean is created per application context. The creation of the bean/object itself is solely the responsibility of the developer.
The equivalent of the xml <bean> tag is annotating the class with #Component, where the bean is created completely by the container, thus the annotations are evaluated.
As it is being said that when you are having your own #Configuration class where you are creating the bean by itself, #Required doesn't apply there.
When you already have a #Component, let Spring Boot do the component scan and at the required setter property you can add #Autowired and it will work fine.
Found this link on web- https://www.boraji.com/spring-required-annotation-example
For example:
I have a Component called Employee having Id and Name.
#Component
public class Employee {
int id;
String name;
public int getId() {
return id;
}
#Autowired
#Required
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have a Configuration class called AppConfig.java
#Configuration
public class AppConfig {
#Bean
public int getId() {
return 1;
}
}
So now we see, that component Employee needs an Id property for binding during startup, so I wrote bean method of type Integer, which will get autowired during runtime. If you do not write a bean of type Integer, it will result a BeanCreationException.
And here is my main class file.
#SpringBootApplication
public class SingletonApplication {
public static void main(String[] args) {
ApplicationContext ctx =
SpringApplication.run(SingletonApplication.class, args);
Employee emp = (Employee)ctx.getBean(Employee.class);
System.out.println(emp.getId());
}
}