EzyFox Bean: Bean Creation
Updated at 1729068206000The most difficult point in the EzyFox Bean is how to create beans, because bean dependencies is complicated between multi layers and sometimes we have classes depend on each other.
1. Sequence of Bean Creation

- Firstly, EzyFox Bean will load configuration classes that be annotated by
EzyConfigurationBefore, you should create and register which singletons that other classes need to use. - Secondly, EzyFox Bean will load configuration classes that be annotated by
EzyConfiguration, if there is a configuration class depend on a singleton class, it will create that singleton. - Thirdly, EzyFox will create beans that be annotated by
EzySingleton,EzyPrototypeandEzyPropertiesBean. If there is any missing dependent bean, EzyFox Bean will print warning logs (by default) or throw exception by your configuration. - Finally, EzyFox Bean will load configuration classes that be annotated by
EzyConfigurationAfter, you should use created beans and should not create any beans here.
2. Example
Let's say we need to create a web service to get data from a book service via API Gateway and provide Rest API to client. Firstly, to call to the API Gateway we need create a HTTP client with [ezyhttp-client](https://github.com/youngmonkeys/ezyhttp/tree/master/ezyhttp-client). Becase [HttpClient](https://github.com/youngmonkeys/ezyhttp/blob/master/ezyhttp-client/src/main/java/com/tvd12/ezyhttp/client/HttpClient.java) is not annotated by `EzySingleton` annotation, so we need a singleton for it. Because it's not depending on any another beans, so we can use
EzyConfigurationBefore for it:@Setter @EzyConfigurationBefore public class HttpClientConfiguration { @Property("http.connect.timeout") private int connectTimeout; @EzySingleton public HttpClient httpClient() { return HttpClient .builder() .connectTimeout(connectTimeout) .build(); } }
The
HttpClientConfiguration class will be loaded first and the singleton of HttpClient will be created first. Now, let's say we don't want to use HttpClient directly, we want to use an BookGatewayProxy like this:@AllArgsConstructor public class BookGatewayProxy { private final String gatewayUrl; private final HttpClient httpClient; public Book getBookById(long bookId) throws Exception { return httpClient.call( new GetRequest() .setURL(gatewayUrl + "/api/v1/books" + bookId) .setResponseType(Book.class) ); } }
We can create singleton for the
BookGatewayProxy with EzyConfiguration like this:@Setter @EzyConfiguration public class BookGatewayConfiguration { @Property("book.gateway.url") private String gatewayUrl; @EzyAutoBind private HttpClient httpClient; @EzySingleton public BookGatewayProxy bookGatewayProxy() { return new BookGatewayProxy( gatewayUrl, httpClient ); } }
The class
BookGatewayConfiguration will be created after the HttpClientConfiguration class, and the instance of BookGatewayProxy will be created after HttpClient. Now, we will create a service class named BookDataService call to BookGatewayProxy to get book data like this:@EzySingleton @AllArgsConstructor public class BookDataService { private final BookGatewayProxy bookGatewayProxy; public Book getBookById(long bookId) { try { return bookGatewayProxy.getBookById(bookId); } catch (Exception e) { throw new NotFoundException("book not found", e); } } }
The singleton of
BookDataService will be created after the BookGatewayConfiguration configuration and BookGatewayProxy. Next step, we can create a controller named BookDataController to return Book data in json format like this:@EzySingleton @AllArgsConstructor public class BookDataController { private final BookDataService bookDataService; public String getBookData(long bookId) { return bookDataService .getBookById(bookId) .toJson(); } }
The singleton of this class will be created after
BookDataController. Now, let's say we want to cache some books to highlight them, we can create a configuration class named `HighlightBookConfiguration` and annotated it with `EzyConfigurationAfter` annotation like this:@EzyConfigurationAfter public class HighlightBookConfiguration implements EzyBeanConfig { @Setter @EzyAutoBind private BookDataService bookDataService; @Getter private final List highlightBooks = new ArrayList<>(); @Override public void config() { highlightBooks.add( bookDataService.getBookById(1) ); } }
This class will load after the all above class and EzyFox Bean will call
config method automatically.3. Circular dependency

Circular dependency happend when we have some classes depend on each other like above image. With circular dependency, EzyFox Bean will throw an exception. Example, if you update BookDataService like this:
@EzySingleton @AllArgsConstructor public class BookDataService { private final BookGatewayProxy bookGatewayProxy; private final BookDataController bookDataController; }
And then, you run the application, you will get an exception like this:
Exception in thread "main" java.lang.IllegalStateException: can not create singleton of class class com.tvd12.ezyfox.example.bean.service.BookDataService at com.tvd12.ezyfox.bean.impl.EzySimpleSingletonLoader.load(EzySimpleSingletonLoader.java:53) at com.tvd12.ezyfox.bean.impl.EzySimpleBeanContext$Builder.createAndLoadSingleton(EzySimpleBeanContext.java:959) at com.tvd12.ezyfox.bean.impl.EzySimpleBeanContext$Builder.createAndLoadSingleton(EzySimpleBeanContext.java:946) at com.tvd12.ezyfox.bean.impl.EzySimpleBeanContext$Builder.addScannedSingletonsToFactory(EzySimpleBeanContext.java:935) at com.tvd12.ezyfox.bean.impl.EzySimpleBeanContext$Builder.build(EzySimpleBeanContext.java:853) at com.tvd12.ezyfox.bean.impl.EzySimpleBeanContext$Builder.build(EzySimpleBeanContext.java:207) at com.tvd12.ezyfox.example.bean.EzyFoxBeanExample.main(EzyFoxBeanExample.java:13) Caused by: java.lang.IllegalStateException: circular dependency detected, class com.tvd12.ezyfox.example.bean.service.BookDataService => class com.tvd12.ezyfox.example.bean.http.BookGatewayProxy => class java.lang.String => class com.tvd12.ezyhttp.client.HttpClient => class com.tvd12.ezyhttp.client.HttpClient$Builder => class com.tvd12.ezyfox.example.bean.controller.BookDataController => class com.tvd12.ezyfox.example.bean.service.BookDataService at com.tvd12.ezyfox.bean.impl.EzySimpleSingletonLoader.detectCircularDependency(EzySimpleSingletonLoader.java:251)
So, to avoid circular dependencies that can break your application, please organize your source code into multiple layers and don't add dependencies from higher layers to lower layers.