EzyFox Bean: Bean Creation
The 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
,EzyPrototype
andEzyPropertiesBean
. 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 we need 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. Becase HttpClient 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<Book> 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 the circular dependency make your application broken, please organize your source in multi layers and don't add dependency from higer layer to lower layer.