In my project,I user a bean which implements FactoryBean and InitializingBean interface. I user spring.main.lazy-initialization=true. However, the afterPropertiesSet method was still executed.
public class PigeonGenericServiceProxy implements FactoryBean, InitializingBean {
private String url;
private int timeout = 1000;
private String callType = "sync";
private String interfaceName;
private Class<?> clazz;
private ClassLoader classLoader;
private Object obj;
@Override
public void afterPropertiesSet() throws Exception {
clazz = Class.forName(interfaceName);
classLoader = Thread.currentThread().getContextClassLoader();
InvokerConfig<GenericService> invokerConfig = new InvokerConfig(url, GenericService.class);
invokerConfig.setTimeout(timeout);
invokerConfig.setGeneric(GenericType.JSON.getName());
invokerConfig.setCallType(callType);
GenericService genericService = ServiceFactory.getService(invokerConfig);
obj = Proxy.newProxyInstance(classLoader, new Class[]{clazz}, (proxy, method, args) -> {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return interfaceName.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return proxy == args[0];
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return interfaceName;
}
Type returnType = method.getGenericReturnType();
JavaType javaType = JacksonUtils.typeFactory().constructType(returnType);
List<String> paramTypes = Arrays.stream(method.getParameterTypes()).map(Class::getName).collect(Collectors.toList());
List<String> paramValues = Arrays.stream(args).map(item -> ClassNameUtils.replace(JacksonUtils.serialize(item))).collect(Collectors.toList());
CallMethod callMethod = CallMethod.getCallMethod(callType);
switch (callMethod) {
case SYNC:
String result = genericService.$invoke(method.getName(), paramTypes, paramValues);
return JacksonUtils.deserialize(ClassNameUtils.replace(result), javaType);
case FUTURE:
genericService.$invoke(method.getName(), paramTypes, paramValues);
Future<String> stringFuture = FutureFactory.getFuture(String.class);
Future future = Futures.lazyTransform(stringFuture, input -> JacksonUtils.deserialize(ClassNameUtils.replace(input), javaType));
FutureFactory.setFuture(future);
return null;
case CALLBACK:
CompletableFuture completableFuture = new CompletableFuture();
InvokerHelper.setCallback(new CompletableFutureCallback(classLoader, javaType, completableFuture));
genericService.$invoke(method.getName(), paramTypes, paramValues);
FutureFactory.setFuture(completableFuture);
return null;
case ONEWAY:
genericService.$invoke(method.getName(), paramTypes, paramValues);
return null;
default:
return null;
}
});
}
@Nullable
@Override
public Object getObject() throws Exception {
return obj;
}
@Nullable
@Override
public Class<?> getObjectType() {
return clazz;
}
public void setUrl(String url) {
this.url = url;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setCallType(String callType) {
this.callType = callType;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
}
However, when I use PersonFactoryBean which also implements FactoryBean and InitializingBean interface, the lazy-initialization can work.
public class PersonFactoryBean implements FactoryBean<Person>, InitializingBean {
private int age;
private String name;
@Override
public void afterPropertiesSet() throws Exception {
// test lazy
System.out.println("PersonFactoryBean afterPropertiesSet");
System.out.println("PersonFactoryBean age: " + age);
System.out.println("PersonFactoryBean name: " + name);
TimeUnit.SECONDS.sleep(2);
}
@Override
public Person getObject() throws Exception {
Person person = new Person();
person.setAge(this.age);
person.setName(this.name);
// test lazy
TimeUnit.SECONDS.sleep(2);
return person;
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
The reason is that PigeonGenericServiceProxy can not determine Class until afterPropertiesSet has bean executed?
Comment From: wilkinsona
The reason is that PigeonGenericServiceProxy can not determine Class until afterPropertiesSet has bean executed?
Yes, it's likely that PigeonGenericServiceProxy
has had to be initialised so that Framework can determine the type of bean that it will produce. This is to allow Framework to perform type matching during dependency injection when the application context has been refreshed.
If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.
Comment From: ChenKangQiang
Thank you
The reason is that PigeonGenericServiceProxy can not determine Class until afterPropertiesSet has bean executed?
Yes, it's likely that
PigeonGenericServiceProxy
has had to be initialised so that Framework can determine the type of bean that it will produce. This is allow Framework to perform type matching during dependency injection when the application context has been refreshed.If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.
Thank you