普通懒加载
使用懒加载容器包裹对象
public class LazyReference<T> {
private final SupplierEx<? extends T> supplier;
private volatile T value;
public LazyReference(SupplierEx<? extends T> supplier) {
Assert.notNull(supplier, "supplier can not be null");
this.supplier = supplier;
}
public T get() {
if (value == null) synchronized (this) {
if (value == null) {
value = supplier.get();
}
}
return value;
}
}
ps: SupplierEx
是一个自定义的Supplier
的扩展,可以很方便的在lambda表达式中抛异常而不是强制catch
import lombok.SneakyThrows;
import java.util.function.Supplier;
/**
* 用于lambda摆脱java的异常捕捉限制(并非包装为runtime)
*/
@FunctionalInterface
public interface SupplierEx<T> extends Supplier<T> {
T getEx() throws Exception;
@Override
@SneakyThrows
default T get() {
return getEx();
}
}
使用字节码懒加载
对于很多情况下,不方便使用容器的时候,就必须使用动态代理来完成懒加载了,这里使用Cglib来实现
import lombok.experimental.UtilityClass;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.LazyLoader;
/**
* 懒加载工具类,可以使用{@link LazyReference} 或者 Enhancer 动态代理创建懒加载对象
*/
@UtilityClass
public class LazyLoaderUtil {
public static <T> LazyReference<T> lazyReference(SupplierEx<? extends T> lazyLoader) {
return new LazyReference<>(lazyLoader);
}
@SuppressWarnings("unchecked")
public static <T> T lazyLoad(Class<T> clazz, LazyLoader lazyLoader) {
return (T) Enhancer.create(clazz, lazyLoader);
}
}
LazyLoader
是Cglib 的一种Callback
,因为只有一个方法,可以视为函数式接口以lambda表达式构建
public interface LazyLoader extends Callback {
Object loadObject() throws Exception;
}
LazyLoaderUtil
在使用时,就是这样的:
@Test
void test_2022_08_04_09_39_34() throws InterruptedException {
MyBean myBean = LazyLoaderUtil.lazyLoad(MyBean.class, MyBeanImpl::new);
System.out.println(myBean.helloProxy());
AtomicLong start = new AtomicLong();
myBean = LazyLoaderUtil.lazyLoad(MyBeanImpl.class, () -> {
MyBeanImpl myBean22 = new MyBeanImpl();
myBean22.name = "myBean22";
start.set(System.nanoTime());
return myBean22;
});
long end = System.nanoTime();
TimeUnit.NANOSECONDS.sleep(5000000);
System.out.println(myBean.helloProxy());
Assertions.assertTrue(start.get() - end > 5000000);//创建时间晚于方法结束时间,代表延迟创建
}
interface MyBean {
String helloProxy();
}
static class MyBeanImpl implements MyBean {
private String name = getClass().getSimpleName();
@Override
public String helloProxy() {
return "Hello Proxy, I'm " + name;
}
}
但是有一个问题是,如果要延迟加载的是没有默认构造函数的类,就必须在Enhancer
创建动态代理的时候使用有参构造
Enhancer e = new Enhancer();
e.setSuperclass(type);
e.setCallback(callback);
//需要参数类型和参数
e.create(constructorArgTypes, constructorArgs);
这太不方便了!我们要避开构造参数! 从手牌中发动 Objenesis
使用SpringObjenesis
跳过构造方法
objenesis是一个小型java类库用来实例化一个特定class的对象,可以跳过构造方法
我们参考org.springframework.aop.framework.ObjenesisCglibAopProxy
中的写法来写一个CglibUtil
,这也是Spring中默认创建动态代理的方式
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.objenesis.SpringObjenesis;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@UtilityClass
public class CglibUtil {
private static final SpringObjenesis objenesis = new SpringObjenesis();
@SuppressWarnings("unchecked")
public static <T> T create(@NotNull Class<T> type, @Nullable Callback... callbacks) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(type);
return (T) createProxyClassAndInstance(enhancer, callbacks, null, null);
}
public static Object createProxyClassAndInstance(@NotNull Enhancer enhancer, @Nullable Callback[] callbacks, @Nullable Class<?>[] constructorArgTypes, @Nullable Object[] constructorArgs) {
if (callbacks == null || callbacks.length == 0) {
callbacks = new Callback[]{NoOp.INSTANCE};
}
List<Class<?>> classes = new ArrayList<>(callbacks.length);
for (Callback callback : callbacks) {
if (callback != null) {
classes.add(callback.getClass());
}
}
enhancer.setCallbackTypes(classes.toArray(new Class[0]));
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
if (objenesis.isWorthTrying()) {
try {
//使用Objenesis跳过构造方法
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
} catch (Throwable ex) {
log.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction", ex);
}
}
if (proxyInstance == null) {
// Regular instantiation via default constructor...
try {
Constructor<?> ctor = (constructorArgs != null ? proxyClass.getDeclaredConstructor(constructorArgTypes) : proxyClass.getDeclaredConstructor());
ReflectionUtils.makeAccessible(ctor);
proxyInstance = (constructorArgs != null ? ctor.newInstance(constructorArgs) : ctor.newInstance());
} catch (Throwable ex) {
log.debug("Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well", ex);
enhancer.setCallbacks(callbacks);
return constructorArgs != null ? enhancer.create(constructorArgTypes, constructorArgs) : enhancer.create();
}
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
}
然后将之前的LazyLoaderUtil
使用我们新写的CglibUtil
:
import lombok.experimental.UtilityClass;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.LazyLoader;
/**
* 懒加载工具类,可以使用{@link LazyReference} 或者 Enhancer 动态代理创建懒加载对象
*/
@UtilityClass
public class LazyLoaderUtil {
public static <T> LazyReference<T> lazyReference(SupplierEx<? extends T> lazyLoader) {
return new LazyReference<>(lazyLoader);
}
public static <T> T lazyLoad(Class<T> clazz, LazyLoader lazyLoader) {
return CglibUtil.create(clazz, lazyLoader);
}
}
我们来测试一下能否避开构造函数:
@Test
void test_2022_08_04_9_49_49() {
String uuid = UUID.randomUUID().toString();
TestBean hhh = LazyLoaderUtil.lazyLoad(TestBean.class, () -> new TestBean(uuid));
//构造函数中赋值的final对象是null,说明创建的代理对象的父构造方法没有被调用
//(这里IDEA都提示永不为null了,但是依然为null 嘿嘿嘿嘿)
Assertions.assertNull(hhh.name);
Assertions.assertEquals(uuid, hhh.getName());
}
static class TestBean {
private final String name;
public TestBean(String name) {
this.name = (name == null ? "不可以是null哦" : name);
System.out.println(name);
}
public String getName() {
return name;
}
}
完美避开!
最终测试
那我们再最后来测试一下,看看LazyLoaderUtil
能否安全的兼容大部分SpringBean:
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cglib.core.TypeUtils;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
public class LazyLoaderUtilTest {
@Autowired
ListableBeanFactory listableBeanFactory;
@Test
void test_2022_08_04_10_16_01() {
List<Object> beans = new ArrayList<>();
for (String beanDefinitionName : listableBeanFactory.getBeanDefinitionNames()) {
Object bean = listableBeanFactory.getBean(beanDefinitionName);
//找到bean的真实类,不能代理代理类
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (TypeUtils.isFinal(targetClass.getModifiers())) {
//final 的类不能代理,我们跳过
continue;
}
beans.add(LazyLoaderUtil.lazyLoad(targetClass, () -> bean));
}
System.out.println(beans);
}
}
测试通过,非常完美!
PS: 如果结合静态获取SpringBean的SpringUtil
,LazyLoaderUtil
会变得更香
结合SpringUtil
之后的最终形态:
/**
* 懒加载工具类,可以使用{@link LazyReference} 或者 Enhancer 动态代理创建懒加载对象
*/
@UtilityClass
public class LazyLoaderUtil {
public static <T> LazyReference<T> lazyReference(SupplierEx<? extends T> lazyLoader) {
return new LazyReference<>(lazyLoader);
}
public static <T> LazyReference<T> lazyReferenceBean(Class<? extends T> beanClass) {
return new LazyReference<>(() -> SpringUtil.getBean(beanClass));
}
public static <T> LazyReference<T> lazyReferenceBean(Class<? extends T> beanClass, T defaultValue) {
return new LazyReference<>(() -> SpringUtil.getBeanOrDefault(beanClass, defaultValue));
}
public static <T> LazyReference<T> lazyReferenceBean(Class<? extends T> beanClass, SupplierEx<? extends T> supplierEx) {
return new LazyReference<>(() -> SpringUtil.getBeanOrDefault(beanClass, supplierEx));
}
public static <T> LazyReference<T> lazyReferenceBean(String beanName, Class<? extends T> beanClass) {
return new LazyReference<>(() -> SpringUtil.getBean(beanName, beanClass));
}
public static <T> T lazyLoad(Class<T> clazz, LazyLoader lazyLoader) {
return CglibUtil.create(clazz, lazyLoader);
}
public static <T> T lazyLoad(Class<? extends T> clazz, SupplierEx<? extends T> lazyLoader) {
return CglibUtil.create(clazz, (LazyLoader) lazyLoader::getEx);
}
public static <T> T lazyLoadBean(Class<? extends T> beanClass) {
return CglibUtil.create(beanClass, (LazyLoader) () -> SpringUtil.getBean(beanClass));
}
public static <T> T lazyLoadBean(Class<? extends T> beanClass, T defaultValue) {
return CglibUtil.create(beanClass, (LazyLoader) () -> SpringUtil.getBeanOrDefault(beanClass, defaultValue));
}
public static <T> T lazyLoadBean(Class<? extends T> beanClass, SupplierEx<? extends T> supplierEx) {
return CglibUtil.create(beanClass, (LazyLoader) () -> SpringUtil.getBeanOrDefault(beanClass, supplierEx));
}
public static <T> T lazyLoadBean(String beanName, Class<? extends T> beanClass) {
return CglibUtil.create(beanClass, (LazyLoader) () -> SpringUtil.getBean(beanName, beanClass));
}
}