I'm trying to create my own lazy load implementation using CGLib, but i've faced some strange behavior that I cannot explain.
Here is what i'm doing.
Proxy instance is being created like follows:
public static <T> T newInstance(Long sourceId, SourceMapper<T> sourceMapper) {
Class<?> proxyTargetType = sourceMapper.getType();
//mapper will use provided sourceId in order to load real object from the DB
Function<Long, T> mapper = sourceMapper.getMapper();
return (T) Enhancer.create(proxyTargetType,
new DynamicProxy<>(sourceId, mapper));
}
Here is the usage of the code above:
Order order = new Order();
try {
//heavy object is being proxied
long customerTariffId = rs.getLong("customer_tariff_id");
order.setCustomerTariff(DynamicProxy
.newInstance(customerTariffId, CUSTOMER_TARIFF_MAPPER));
}
Heavy object should be loaded only if any of its methods gets invoked:
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
T source = this.getSource(); // loads real object using sourceId and mapper
if(source == null) return null;
return method.invoke(source, args);
}
It works perfectly if this.getSource()
loads some object.
But here what i'm getting if we assume, that order.getCustomerTariff()
should return null
(this.getSource()
will return null
)
LOG.debug("{}", order.getCustomerTariff()); //null (1)
LOG.debug("{}", order.getCustomerTariff() != null); //true (2)
I assume, that for some reason toString()
gets invoked at line (2), so i'm getting String
null
instead of literal null
. That's why it is not equal to a literal null
in the comparison clause.
How do you think, is there any way to return a regular null
at line (2) and receive a correct value of false
during that check?
EDIT
Class being proxied looks similar to this:
public class CustomerTariff extends DomainEntity {
private Customer customer;
//some other similar fields
private Tariff tariff;
public CustomerTariff() {
}
public CustomerTariff(Customer customer
Tariff tariff) {
this.customer = customer;
this.tariff = tariff;
}
public CustomerTariff(Long id, Customer customer,
Tariff tariff) {
super(id);
this.customer = customer;
this.tariff = tariff;
}
//getters and setters
@Override
public String toString() {
return "CustomerTariff{" +
"customer=" + customer +
", tariff=" + tariff +
"} " + super.toString();
}
}
public abstract class DomainEntity {
private Long id;
public DomainEntity() {}
public DomainEntity(Long id) {
this.id = id;
}
@Override
public String toString() {
return "DomainEntity{" +
"id=" + id +
'}';
}
}