简体   繁体   中英

How Spring knows bean to inject with @Bean and method call

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?

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.

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".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM