Reputation: 63
I'm trying some stuff with Spring Framework and I would like to know how Spring can inject singleton dependency with method call when using java configuration?
Example :
@Configuration
public class AppConfiguration {
@Bean
public BlogRepository blogRepository() {
return new BlogRepositoryImpl();
}
@Bean
@Scope("prototype")
public BlogService blogService() {
return new BlogServiceImpl(blogRepository());
}
@Bean
public AuthorService authorService() {
return new AuthorServiceImpl(blogRepository());
}
}
I know that this class is also a bean and it is proxied by Spring but, how can Spring always get the existing BlogRepository
singleton since I call blogRepository()
from within the class and so proxy can't handle the call?
Upvotes: 0
Views: 1216
Reputation: 159114
What makes you think proxy cannot handle the call?
Spring could generate a proxy similar to this subclass:
class AppConfigurationProxy extends AppConfiguration {
private BlogRepository blogRepository;
@Override
public BlogRepository blogRepository() {
if (blogRepository == null)
blogRepository = super.blogRepository();
return blogRepository;
}
// same for the other two @Bean methods
}
Now, no matter how many times a method inside AppConfiguration
calls it's own blogRepository()
method, it will always get the same object.
UPDATE: Proof that above would work.
Simple Bean interfaces
public interface BlogRepository {
}
public interface BlogService {
}
public interface AuthorService {
}
Simple Bean classes
They don't have any actual logic, just a toString()
implementation that shows the "identity" of the object, similar to the default toString()
implementation in class Object
.
public class BlogRepositoryImpl implements BlogRepository {
@Override
public String toString() {
return "BlogRepositoryImpl@" + Integer.toHexString(hashCode());
}
}
public class BlogServiceImpl implements BlogService {
private BlogRepository blogRepository;
public BlogServiceImpl(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
@Override
public String toString() {
return "BlogServiceImpl@" + Integer.toHexString(hashCode()) + "[blogRepository=" + this.blogRepository + "]";
}
}
public class AuthorServiceImpl implements AuthorService {
private BlogRepository blogRepository;
public AuthorServiceImpl(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
@Override
public String toString() {
return "AuthorServiceImpl@" + Integer.toHexString(hashCode()) + "[blogRepository=" + this.blogRepository + "]";
}
}
Configuration class
As defined in the question.
public class AppConfiguration {
public BlogRepository blogRepository() {
return new BlogRepositoryImpl();
}
public BlogService blogService() {
return new BlogServiceImpl(blogRepository());
}
public AuthorService authorService() {
return new AuthorServiceImpl(blogRepository());
}
}
Proxy class as String could have implemented it
Same as at top of the answer, just completed with all the methods.
public class AppConfigurationProxy extends AppConfiguration {
private BlogRepository blogRepository;
private BlogService blogService;
private AuthorService authorService;
@Override
public BlogRepository blogRepository() {
if (this.blogRepository == null)
this.blogRepository = super.blogRepository();
return this.blogRepository;
}
@Override
public BlogService blogService() {
if (this.blogService == null)
this.blogService = super.blogService();
return this.blogService;
}
@Override
public AuthorService authorService() {
if (this.authorService == null)
this.authorService = super.authorService();
return this.authorService;
}
}
Test
public class Test {
public static void main(String[] args) {
// Show result without proxy
AppConfiguration config = new AppConfiguration();
System.out.println(config.blogRepository());
System.out.println(config.blogService());
System.out.println(config.authorService());
// Show how only one BlogRepository is craeted when proxy is used
config = new AppConfigurationProxy();
System.out.println(config.blogRepository());
System.out.println(config.blogService());
System.out.println(config.authorService());
}
}
Output
BlogRepositoryImpl@1e81f4dc
BlogServiceImpl@7960847b[blogRepository=BlogRepositoryImpl@6a6824be]
AuthorServiceImpl@2c13da15[blogRepository=BlogRepositoryImpl@77556fd]
BlogRepositoryImpl@9e89d68
BlogServiceImpl@3b192d32[blogRepository=BlogRepositoryImpl@9e89d68]
AuthorServiceImpl@16f65612[blogRepository=BlogRepositoryImpl@9e89d68]
As can be seen, the first part, which didn't use the proxy, ends up with 3 different instances of BlogRepositoryImpl
.
With the use of the Proxy, only one instance of BlogRepositoryImpl
is created, and shared even though blogService()
calls blogRepository()
"directly".
Upvotes: 0
Reputation: 1072
When you annotate class with @Configuration
, methods annotated with @Bean
are proxy wrapped by CGLIB
.
If it’s the first call of this method, then the original method’s body will be executed and the resulting object will be stored in the Spring
context. All subsequent calls just return the bean retrieved from the context.
Upvotes: 2