I created a spring bean in a configuration class like this:
#Bean
MyClass getMyClass() {
MyClass mc = new MyClass()
return mc;
}
Whenever MyClass is autowired in another class that needs it injected, will it always create a new object by virtue of new in bean definition?
Is a bean created this way a real singleton ?
Spring guarantees that whatever you do in the method annotated with Bean annotation it will be done only once. Internal Spring factories will take care about that.
Of course it depends from scope but by default scope is singleton. See docs:
Scopes
Bean
Is spring default scope singleton or not?
Small example which should help you to understand how it works:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.util.Random;
#Configuration
public class SpringApp {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringApp.class);
System.out.println(ctx.getBean(MyClass.class));
System.out.println(ctx.getBean(MyClass.class));
System.out.println(ctx.getBean(MyClass.class));
System.out.println(ctx.getBean(MyClass.class));
}
#Bean
public MyClass getMyClass() {
System.out.println("Create instance of MyClass at " + LocalDateTime.now());
MyClass myClass = new MyClass();
return myClass;
}
}
class MyClass {
private int value = new Random().nextInt();
#Override
public String toString() {
return super.toString() + " with values = " + value;
}
}
prints:
Create instance of MyClass at 2019-01-09T22:54:37.025
com.celoxity.spring.MyClass#32a068d1 with values = -1518464221
com.celoxity.spring.MyClass#32a068d1 with values = -1518464221
com.celoxity.spring.MyClass#32a068d1 with values = -1518464221
com.celoxity.spring.MyClass#32a068d1 with values = -1518464221
When you define bean with scope protoype
#Scope("prototype")
#Bean
public MyClass getMyClass()
App prints:
Create instance of MyClass at 2019-01-09T22:57:12.585
com.celoxity.spring.MyClass#282003e1 with values = -677868705
Create instance of MyClass at 2019-01-09T22:57:12.587
com.celoxity.spring.MyClass#7fad8c79 with values = 18948996
Create instance of MyClass at 2019-01-09T22:57:12.587
com.celoxity.spring.MyClass#71a794e5 with values = 358780038
Create instance of MyClass at 2019-01-09T22:57:12.587
com.celoxity.spring.MyClass#76329302 with values = 868257220
In your example, yes.
What will happen practically is that when Spring starts up, it will call the getMyClass() method which will new up an instance of the object. Spring will then retain that single instance and inject it into all other beans that require an instance of MyClass.
It will work this way since you've not declared a scope on the bean -- the default is singleton as indicated by the other answer.
Spring's concept of a singleton bean is quite different from the
Singleton pattern.
The scope of the Spring singleton is best described as per container
and per bean
Section 4.4.1
This mean that if you create a spring bean, that bean provides its lifecycle from within the IoC Spring Container.
If you want to create with "NEW really a singleton bean". You have this through a new Spring Ioc Container.
Let me illustrate this through an example.
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringApp.class);
ApplicationContext ctxReallyNewOne = new AnnotationConfigApplicationContext(SpringApp.class);
ApplicationContext ctx2ReallySecondOne = new AnnotationConfigApplicationContext(SpringApp.class);
System.out.println(ThreadColors.Red + "Beans created via same Ioc container with same class");
System.out.println(ctx.getBean(MyClass.class));
System.out.println(ctx.getBean(MyClass.class));
System.out.println(ThreadColors.Cyan + "Beans created via different Ioc container with SAME CLASS");
System.out.println(ctxReallyNewOne.getBean(MyClass.class));
System.out.println(ctx2ReallySecondOne.getBean(MyClass.class));
}
After running this code, the following is written to the console.
Also if you want to get the creation date of a bean.Does not need to use LocalDataTime.now() . It is best practice to use "The Lifecycle of Spring Beans" for this.
The Lifecycle of Spring Beans
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 am writing a custom spring starter, in which I need to register a list of beans of same type based on the property defined in the application.properties
i have following properties in application.properties
mybean.names = mybean1, mybean2
mybean1.foo = foo1
mybean2.foo = foo2
mybean1.bar = bar1
mybean2.bar = bar2
Current implementation of autoconfiguration class is below
#Configuration
#ConditionalOnClass(MyBeanFactory.class)
public class MyBeanAutoConfiguration {
#Autowire
Environemnt evn;
#Bean(name = {"mybean1"})
public MyBean getBean() {
String[] names = StringUtils.commaDelimitedListToStringArray(evn.getProperty("mybean.names"));
MyBean mybean = new Mybean(evn.getProperty(names[0] + ".foo"),
evn.getProperty(names[0] + ".bar"));
return myBean;
}
}
And i want to simply autowire with qualifer my bean in spring boot application like below
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class MyApplication {
#Autowired
#Qualifer("mybean1")
MyBean mybean;
public static void main(String[] args) {
SpringApplication.run(VaultApplication.class, args);
}
How can i change my autoconfigure class to create multiple beans with different names defined in the properties file(in this case how can I create bean mybean2) so that i can use simply autowire with qualifier ?
I am not sure that i understood you question but if you want to have several bean with different name so best way is writing multiple #Bean method in your configuration class with different name, so you can autowire each one of them by #Qualifer
This question already has answers here:
Injecting beans into a class outside the Spring managed context
(8 answers)
Closed 3 years ago.
Introduction
I have some business logic properties in the application.yml file.
They are loaded into the application via a #ConfigurationProperties annotated class.
How could I use these properties in a class which is not a Spring Bean? It cannot be a singleton, because many objects of it must be created during run-time.
Example
application.yml
business.foo: 2
BusinessProperties.java
#ConfigurationProperties("business")
#Getter // lombok
#Setter // lombok
public class BusinessProperties {
private int foo;
}
TypicalBean.java
#Component
public class TypicalBean {
private final BusinessProperties properties;
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
#PostConstruct
public void printFoo() {
System.out.println("Foo: " + properties.getFoo()); // "Foo: 2"
}
}
NonBean.java
public class NonBean {
public void printFoo() {
System.out.println("Foo: ???"); // How to access the property?
}
}
Is there some way to create a non-singleton class, which can have access to configuration (or even other Spring beans) but otherwise works the same as a regular java class? Meaning that I can control its creation, it is collected by the garbage collector if not used anymore, etc.
You can still define the NonBean.class as a Component with Scope.Prototype
#Component
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class NonBean {
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
public void printFoo() {
System.out.println("Foo: " + properties.getFoo());
}
}
The trick is how you create an instance of NonBean.class. In the code where you'll be creating an instance of NonBean.class, use Spring's ObjectFactory<T>
private final ObjectFactory<NonBean> nonBeanFactory;
...
NonBean nonBean = nonBeanFactory.getObject();
The instantiated nonBean object will have been autowired.
All spring-beans creates by SpringApplicationContext. Bean - it's simple POJO-object, but created by Spring and saved in his container. If you want to get access to bean from outside of container - see this:
Getting Spring Application Context
Spring beans are really meant to be used within the application context but you might be able to achieve what you want by autowiring the properties to a static field in a Spring bean.
#Component
public class BusinessPropertiesUtils {
public static BusinessProperties INSTANCE;
#Autowired
public setBusinessProperties(BusinessProperties properties) {
this.INSTANCE = properties;
}
}
And then:
public class NonBean {
public void printFoo() {
System.out.println("Foo: " + BusinessPropertiesUtils.INSTANCE.getFoo());
}
}
PS: this is very hacky and definitely not the "Spring way".
You can configure beans with the prototype scope, which will give you a new instance of the bean every time it's requested.
From the Spring documentation:
In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance.
...
In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client.
Example of how you can convert the TypicalBean class to a prototype scoped bean:
#Component
#Scope("prototype")
public class TypicalBean {
...
}
Another alternative is to manually instantiate the bean class (or any POJO) and injecting the dependencies (configuration, spring beans, etc.) through the constructor or setter methods, if you have them available or can get them from the Spring Context.
new TypicalBean(properties);
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 test that fails:
#Test
public void testValidation() {
Validator validator = new LocalValidatorFactoryBean();
Map<String, String> map = new HashMap<String, String>();
MapBindingResult errors = new MapBindingResult(map, Foo.class.getName());
Foo foo = new Foo();
foo.setBar("ba");
validator.validate(foo, errors);
assertTrue(errors.hasFieldErrors());
}
Foo is as follows:
import javax.validation.constraints.Size;
public class Foo {
#Size(min=9, max=9)
private String bar;
// ... public setter and getter for bar
}
I have referenced Manually call Spring Annotation Validation and Using Spring Validator outside of the context of Spring MVC but I'm not sure why this test is failing.
You are trying to use a bean that is actually to be used inside a Spring ApplicationContext outside of it. To prepare it for use you also have to mimic the behavior of the ApplicationContext in regards to object initialization.
The LocalValidatorFactoryBean implements the InitializingBean interface. Which contains a single method which will be normally called by the ApplicationContext once the object is constructed and all dependencies have been injected (the same as a method annotated with #PostConstruct).
As you are using the bean outside of an ApplicationContext you will have to call the afterPropertiesSet method manually before the object is actually ready to be used.