I was thinking about the lazy-initialization of beans in Spring. For me, it wasn't crystal clear that the "lazy" here means that a bean will be created when it's referenced.
I expected that the lazy initialization support in Spring is different. I thought this is a "method-call" based lazy creation. What I mean by this is, whenever any method is being called on the method, it will be created.
I think this could be easily solved by creating a proxy instance of the specific bean and do the initialization on any method call.
Am I missing something why this is not implemented? Is there any problem with this concept?
Any feedback/idea/answer will be appreciated.
You can achieve the behavior you want by scoping your bean with a proxyMode of ScopedProxyMode.TARGET_CLASS (CGLIB) or ScopedProxyMode.INTERFACES (JDK).
For example
public class StackOverflow {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Conf.class);
Bar bar = ctx.getBean(Bar.class);
System.out.println(bar);
System.out.println(bar.foo.toString());
}
}
#Configuration
class Conf {
#Bean
#Lazy
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public Foo foo() {
System.out.println("heyy");
return new Foo();
}
#Bean
public Bar bar() {
return new Bar();
}
}
class Bar {
#Autowired
public Foo foo;
}
class Foo {
}
will print
com.example.Bar#3a52dba3
heyy
com.example.Foo#7bedc48a
demonstrating that the Foo bean was initialized through its #Bean factory method only after a method was invoked on the Foo instance injected by the Spring context in the Bar bean.
The #Scope annotation can be used in a class declaration as well.
bellow is my views:
Bean types in Spring Container:
There are two Scope bean type in Spring Container.One is Prototype, this type bean won't exist lazy-init concept, because these beans will be instantiated when clients invoke the getBean() method every time. Another is Singleton, these bean will be instantiated once, these beans can be lazily instantiated(just be instantiated when they're used, such as #Autowired, refrenced) if you define the bean use #Lazy or lazy-init=true.
How to implement lazy-init:
Yes, common implementation is Proxy Mode. Spring use JDK Dynamic Proxy and Cglib to implement Proxy, you can go further understanding about these techs.
Hope to help you.
Related
I have a bean declared in by config file as:
#Bean
#Lazy
#Scope("prototype")
public SomeClass someClass()
{
return new SomeClass();
}
Then inside my test class I have the following code:
#Autowired
private SomeClass someClass;
#Before
public void setUp()
{
xyzObject.someMethod(someClass);
}
My question here is inside my setUp() method I am just referencing the autowired SomeClass bean but not getting it from the spring context using getBean(), but still it is creating a new bean everytime the setUp() is running before a test. Why is this happening? Does prototype scope means a new bean gets created whenever the bean's reference is used anywhere in the code? I haven't seen anything specified like that in the documentation.
That is the essence of Inversion of Control. Because you injected it into another bean, every time you instantiate another bean your injected bean would be created and provided by spring as part of instantiation of your containing bean. You don't need to call getBean() here. However if in some place you just need an instance of your SomeClass bean you may call getBean() to tell your spring environment to give you that bean to you. However, It is better to avoid such practice and rely on injection. Or create a Factory that can provide you such Bean (Still relaying on spring though) That would make your code not aware of Spring which is IMHO is more ellegant
One of the new features in Spring 4.3 is implicit constructor injection in which we don't have to specify #Autowired anymore on top of the constructor.
My question is, how does the implicit constructor injection behave on lazy bean?
#Component
#Lazy
public class SomeClass {}
#Component
public class ClientClass {
// does SomeClass still lazily initialized???
public ClientClass(SomeClass someClass) { ... }
}
You've applied the annotation to the bean, which means that Spring will create a lazy proxy and inject it where SomeClass is required. The fact that the bean requirement comes from an implicit (singleton) constructor doesn't matter.
In the code below, is calling bar() inside foo.setBar(bar()) and blah.setBar(bar()) using two difference instances of Bar? Or is it using a bean instance of Bar instead? If it's a bean instance, how does Spring do it automagically? Is it achieved by proxy?
#Configuration
public class AppConfig {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setBar(bar());
return foo;
}
#Bean
public Bar bar() {
return new Bar();
}
#Bean
public Blah blah() {
Blah blah = new Blah();
blah.setBar(bar());
return blah;
}
}
Spring creates a proxy of your #Configuration annotated classes. This proxy intercepts #Bean method calls and caches the bean instances so that further calls to the same #Bean method refers to the same bean instance.
Hence in your case both calls to bar() method refers to the same Bar instance.The Bar instance is actually a singleton per application context.This is why the #Bean methods visibility is restricted to either protected , package or public because Spring needs to override your #Bean methods in the proxy.
Single bean instance will be used and it is achieved using proxies. Spring uses the concept of Inheritance based proxies to achieve this. Please take a look at - How to exactly work the Spring Inheritance-based Proxies configuration?
the same bean instance will be used in your case because a singleton scope is used by default for '#Bean' annotation.
yes, it's achived by spring proxying internals.
I have a hobby-project which I would like to migrate to Spring.
As an example I have the following classes:
public class OtherBean {
public void printMessage() {
System.out.println("Message from OtherBean");
}
}
public class InjectInMe {
#Inject OtherBean otherBean;
public void callMethodInOtherBean() {
otherBean.printMessage();
}
}
However as I read the documentation, I must annotate all classes to be managed by Spring with an annotation like #Component (or others that are similar).
Running it with the following code:
public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
InjectInMe bean = context.getBean(InjectInMe.class);
bean.callMethodInOtherBean();
}
}
Gives me the error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [somepackage.InjectInMe] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:371)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:968)
at somepackage.SpringTest.main(SpringTest.java:10)
My question is: is there a way to make Spring manage any class I ask the ApplicationContext to instantiate without me having to register them in an Annotated Config (or XML config)?
In Guice I can just inject into the class
public class GuiceTest {
static public class GuiceConfig extends AbstractModule {
#Override
protected void configure() {}
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new GuiceConfig());
InjectInMe bean = injector.getInstance(InjectInMe.class);
bean.callMethodInOtherBean();
}
}
Gives me the output:
Message from OtherBean
Is there anyway I can make Spring work like Guice? As in make Spring inject my beans without me having to register or scan packages for classes annotated with #Component-like annotations?
Any Spring gurus have a way to solve this problem?
My question is: is there a way to make Spring manage any class I ask
the ApplicationContext to instantiate without me having to register
them in an Annotated Config (or XML config)?
No, it's not possible. This is simply not how Spring is designed. You must either implicitly (e.g. scan + annotation) or explicitly (e.g. XML bean definition) register your beans.
The least you can do is something like:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(OtherBean.class);
context.register(InjectInMe.class);
context.refresh();
There are a few ways to provide bean definitions for Spring's ApplicationContext:
Use component scanning and annotate your types with #Component or one of its meta annotations (#Service, #Controller, etc.)
Define a <bean> in XML configuration or a #Bean in Java configuration (or whatever other type of explicit configuration).
Use a BeanDefinitionRegistryPostProcessor to register beans directly on a BeanDefinitionRegistry.
Some ApplicationContext subtypes implement BeanDefinitionRegistry, so you can register bean definitions with them directly.
The beans generated from these bean definitions will be available when injection is required. You wouldn't want (at least IMO) your container to instantiate a type without you telling it somehow that it is OK.
You must either declare the class as a #Component, or whatever it may be, or you must provide an xml definition. If you don't define the bean, how is Spring supposed to know what to do? For the properties of a class, e.g. MyController has a private service MyService. If you put the #Autowired tag above the service instantiation, Spring will automatically inject this server into your controller, which is what your comment about #Inject seems to hint at. However, in order to autowire, you must define a bean for that class, which means you must use either the #Component/#Controller, etc annotation configuration or an xml bean configuration.
I currently have numerous classes with a field like
private MyStuff myStuff = new MyStuff();
It would be preferable if there was a MyStuff singleton which all the classes used. Our project has a MyConfiguration class with some Beans in it, but they don't seem to get used, at least not directly, so I can't use them for examples. I have been tasked to create a MyStuff bean in the MyConfiguration class, and then inject it into my other classes.
What I have so far:
#Configuration
public class MyConfiguration
{
#Bean
public MyStuff myStuff()
{
return new MyStuff();
}
}
public SomeClass
{
public void dealWithStuff()
{
someStuff.myMethod();
}
#Autowired
private MyStuff someStuff;
}
This compiles but does not run. someStuff is null when it tries to call myMethod(). Apparently it does not see the bean or make the connection. What am I missing?
I'm going to make an assumption, so correct me if I'm wrong: you are creating instances of SomeClass yourself. Something like
SomeClass someInstance = new SomeClass();
outside of any Spring component.
In this case, how do you expect Spring to inject anything since it's not even process it.
You need to let Spring create objects (beans) that need to have other beans injected.
The problem is naming. Refer to this for a complete description but briefly it says
If you intend to express annotation-driven injection by name, do not primarily use #Autowired, even if is technically capable of referring to a bean name through #Qualifier values. Instead, use the JSR-250 #Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process.
It means that you should be doing something like this:
#Resource(name="myStuff")
public void setSomeStuff(MyStuff someStuff){
this.someStuff = someStuff;
}
The reason is that you have defined your bean as myStuff but referring it as someStuff.
Also to have a singleton instance, all you need is to define its scope as singleton in your Spring Configuration as following:
#Configuration
public class MyConfiguration
{
#Bean #Scope(BeanDefinition.SCOPE_SINGLETON)
public MyStuff myStuff()
{
return new MyStuff();
}
}
As I see in your code, your object reference name is "someStuff" but you are referring to myStuff you should use someStuff.myMethod();