`
worldmaomao
  • 浏览: 10577 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

懒加载的实现

阅读更多
这几天在学习公司内部搜索引擎客户端的使用,发现里面使用到了懒加载技术,趁机学习了cglib的东西。

什么是懒加载呢?其现象是这样的,例如:

// 调用memberDAO.find()方法时,不加载数据
MemberDO member = memberDAO.find("maomao");

// 调用member.getName()方法时,才去调用dao加载数据。这不同于传统的使用习惯。
String name = member.getName();

这个是怎么实现的呢?一开始受到搜索引擎客户端的影响走了弯路,它没有充分使用到cglib的特性。

先简单地介绍一下cglib里的一个类Enhancer。这个类的主要作用是动态生成一个类的子类,作动态代理。
Enhancer enhancer = new Enhancer();

// 设置需要代理的类
enhancer.setSuperclass(MemberDO.class);

// 设置一个Callback的过滤器。说它是过滤器,还不如说它是callback的选择器(比如执行哪个方法前,调用哪个callback),
// 它的一个抽象方法int accept(Method method) 返回的是callback数组的下标
enhancer.setCallbackFilter(new DefaultCallbackFilter());

// 这里就是设置上面所提到的callback数组
enhancer.setCallbacks(new Callback[] { new DefaultMethodInterceptor(),NoOp.INSTANCE });

// 返回一个动态生成的子类
enhancer.create();


要实现的需求是,执行member.getName()才去加载数据。原理就是在执行member.getName()前,把数据从dao或从外部接口读过来。 先看看我走弯路的实现方式:

public class MemberDAO {

    /**
     * callback过滤器
     * 
     * <pre>
     *  1.当执行的方法是toString,getName,getSex的时候,调用自定义的方法拦截器MethodInterceptor
     *  2.当执行的是其他方法时,不调用方法拦截器MethodInterceptor
     * </pre>
     */
    private class DefaultCallbackFilter implements CallbackFilter {

        String methods[] = { "toString", "getName", "getSex" };

        @Override
        public int accept(Method method) {
            if (ArrayUtils.contains(methods, method.getName())) {
                return 0;
            } else {
                return 1;
            }
        }

    }

    /**
     * 方法拦截器MethodInterceptor的实现
     * 
     * <pre>
     * 这里就是具体加载数据的实现
     * </pre>
     */
    private class DefaultMethodInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

            MemberDO member = (MemberDO) obj;
            if (member != null && member.isLoaded()) {
                System.out.println("already loaded.");
                return proxy.invokeSuper(obj, args);
            }

            // 从数据源取数据
            MemberDO tmpMember = generateMemberDO();
            member.setName(tmpMember.getName());
            member.setSex(tmpMember.getSex());

            // 设置已经加载的标志
            // (这里把是否加载的标记写在DO里,不太好。我做一下实验才这样做的,为了简单。写在ThreadLocal是一个方式,不会侵入到DO)
            member.setLoaded(true);
            return proxy.invokeSuper(member, args);
        }
    }

    /**
     * 查询数据
     * 
     * <pre>
     * 1.当lazy为true的时候,会为MemberDO创建一个动态代理类,并设置好CallbackFilter和MethodInterceptor
     * 2.当lazy为false的时候,直接从数据源取数据
     * </pre>
     * 
     * @param name 会员的名字
     * @param lazy 是否使用懒加载
     * @return
     */
    public MemberDO find(String name, boolean lazy) {
        if (lazy) {
            Enhancer enhancer = new Enhancer();
            enhancer.setUseCache(true);
            enhancer.setSuperclass(MemberDO.class);
            enhancer.setCallbackFilter(new DefaultCallbackFilter());
            enhancer.setCallbacks(new Callback[] { new DefaultMethodInterceptor(), NoOp.INSTANCE });
            return (MemberDO) enhancer.create();
        } else {
            return generateMemberDO();
        }
    }

    /**
     * 模拟从数据源取数据
     * 
     * @return
     */
    private MemberDO generateMemberDO() {
        System.out.println("query data from db.");
        MemberDO member = new MemberDO();
        member.setName("maomao");
        member.setSex("male");
        return member;
    }
}


看一下执行结果:
        // 控制台显示:query data from db.
        MemberDO member1 = memberDAO.find("test", false);

        // 控制台无显示
        MemberDO member2 = memberDAO.find("test", true);

        // 控制台显示:query data from db.
        // 控制台显示:name:maomao
        System.out.println(String.format("name:%s", member2.getName()));

        // 控制台显示:already loaded.
        // 控制台显示:sex:male
        System.out.println(String.format("sex:%s", member2.getSex()));

从执行结果来看,使用了懒加载,在调用member2.getName()才去加载数据,在调用member2.getSex()时候直接返回第一次加载的数据。

前面提到过上述的实现方式是我走的弯路,其实有更加简洁的实现方式,cglib有专门用来实现懒加载的callback接口,名叫LazyLoader。我们只要实现这个接口,写上具体的数据加载方式。看一个例子:
public class MemberDAO {

    public MemberDO find(String name, boolean lazy) {
        if (lazy) {

            // 设置懒加载的callback
            return (MemberDO) Enhancer.create(MemberDO.class, new MemberDOLazyLoader(name));
        } else {
            return generateMemberDO();
        }
    }

    /**
     * 模拟从数据源取数据
     * 
     * @return
     */
    private MemberDO generateMemberDO() {
        System.out.println("query data from db.");
        MemberDO member = new MemberDO();
        member.setName("maomao");
        member.setSex("male");
        return member;
    }

    private class MemberDOLazyLoader implements LazyLoader {

        /** 查询条件 */
        private String name;

        public MemberDOLazyLoader(String name){
            this.name = name;
        }

        @Override
        public Object loadObject() throws Exception {
            return generateMemberDO();
        }

    }
}


最后来看看执行结果:
        MemberDAO memberDAO = new MemberDAO();

        // 控制台显示:query data from db.
        MemberDO member1 = memberDAO.find("maomao", false);

        MemberDO member2 = memberDAO.find("maomao", true);

        // 控制台显示:query data from db.
        // 控制台显示:name:maomao
        System.out.println(String.format("name:%s", member2.getName()));

        // 控制台显示:sex:male
        System.out.println(String.format("sex:%s", member2.getSex()));


一样的执行结果,而且简单,没有侵入。是不是比较简单呢?
分享到:
评论

相关推荐

    Fragment实现懒加载

    5. **懒加载实现**: - 在Fragment的onStart()或onResume()方法中,可以检查数据是否已加载。如果没有,这时可以发起网络请求或读取本地数据,然后更新UI。确保在onPause()或onStop()中取消正在进行的异步任务,以...

    java swing tree树的懒加载

    6. **测试和优化**:在实际环境中测试你的懒加载实现,检查是否按预期工作,没有内存泄漏或其他性能问题。根据需要进行优化,例如通过缓存已加载的节点来减少重复的数据库查询。 在提供的压缩包文件"LazyLoadTree...

    js,jquery懒加载demo

    以下是一个简单的 JavaScript 懒加载实现示例: ```javascript let lazyImages = document.querySelectorAll('.lazy'); function lazyLoad() { lazyImages.forEach(img =&gt; { if (isElementInViewport(img)) { ...

    简单的图片懒加载实现

    在实现简单的图片懒加载时,我们通常会遵循以下步骤: 1. **数据存储**:首先,我们需要在HTML中为每个图片元素添加一些额外的数据属性,如`data-src`,用于存储原始图片URL,而不在`src`属性中直接设置。这样...

    懒加载案例lazyload

    在提供的压缩包中,`index.html`可能是包含懒加载实现的网页文件,`images`目录可能存储了待懒加载的图片,`js`目录下的JavaScript文件可能是实现懒加载功能的脚本,而`css`目录则可能包含了一些辅助的CSS样式。...

    Lazy Loading:JavaScript懒加载实现.docx

    Lazy Loading:JavaScript懒加载实现.docx

    Lazy Loading:视频懒加载实现方法.docx

    Lazy Loading:视频懒加载实现方法.docx

    Android界面数据懒加载实现代码

    在给定的标题和描述中,提到的是在Android界面,特别是Fragment中的数据懒加载实现。Fragment是Android应用程序中用于在Activity中展示可拆分的界面组件。当涉及到多页切换,如新闻客户端的新闻分类时,通常会在一个...

    前端实现图片懒加载(lazyload)的两种方式

    下面我们将详细介绍两种常见的图片懒加载实现方式。 ### 1. 数据属性+Intersection Observer API **数据属性**:在HTML中,为图片元素添加一个自定义的数据属性,如`data-src`,将实际的图片URL存储在这个属性中,...

    Android优化方案之Fragment的懒加载实现代码

    Android优化方案之Fragment的懒加载实现代码 本篇文章主要介绍了Android优化方案之Fragment的懒加载实现代码,解决了ViewPager中Fragment的网络加载问题。 Fragment的懒加载是非常有必要的,因为ViewPager默认加载...

    Android 多层嵌套后的 Fragment 懒加载实现示例

    总结来说,Android多层嵌套后的Fragment懒加载实现涉及对Fragment生命周期的深入理解,以及对不同UI结构和用户交互模式的适应。通过精心设计的生命周期监听和状态管理,我们可以确保在网络请求和用户体验之间找到...

    图片懒加载svg-loading图片

    3. 图片懒加载实现:阐述如何结合JavaScript(可能是jQuery或其他库)监听滚动事件,判断图片是否进入视口,当达到触发条件时替换占位符为真实的图片,并显示SVG加载动画。 4. 示例代码:提供完整的HTML、CSS和...

    JQ 图片懒加载

    **JQ 图片懒加载**是一种优化网页性能的技术,它主要应用于含有大量图片的页面,以提高页面加载速度和用户...提供的`demo`文件可能包含一个简单的懒加载实现示例,可以通过查看和运行来学习和理解这种技术的工作原理。

    sencha touch图片懒加载

    以下是一个基本的Sencha Touch图片懒加载实现步骤: 1. **创建图片模型(Model)**:首先,我们需要定义一个包含图片URL和其他必要信息的数据模型,例如: ```javascript Ext.define('ImageModel', { extend: 'Ext....

    Android实现WebView懒加载

    "Android实现WebView懒加载"这一技术就是为了优化这个问题,通过延迟加载部分资源,提升应用的启动速度和响应性。 首先,我们要理解什么是懒加载(Lazy Loading)。懒加载是一种优化策略,它推迟非关键资源的加载,...

    bootstrap-treeview树节点实现动态加载(懒加载)

    本项目在官方Bootstrap Treeview的基础上进行了扩展,实现了懒加载功能。这意味着当用户首次访问树形视图时,只会加载根节点,当用户展开某个节点时,该节点的子节点才会在后台请求数据并动态添加到视图中。这样,...

    jQuery图片懒加载代码

    **图片懒加载实现原理** 1. **数据属性**:首先,我们为需要懒加载的图片设置一个特殊的`data-`属性,如`data-src`,用于存储原始图片URL。 2. **监听滚动事件**:使用jQuery的`$(window).scroll()`函数监听窗口...

    微信小程序图片懒加载.zip

    **二、微信小程序中的图片懒加载实现** 1. **监听滚动事件**:首先,我们需要监听用户的滚动行为,通常使用`onPageScroll`生命周期函数来获取当前页面的滚动位置。 2. **计算元素位置**:利用`...

    图片懒加载

    京东的懒加载实现可能包括以下步骤: 1. 分析页面结构,确定图片的初始位置和视口高度。 2. 使用JavaScript或Intersection Observer监听滚动事件。 3. 当图片进入视口时,替换data-src为src,加载图片。 4. 可能还...

    使用element中el-cascader级联选择器:实现省市区街道筛选、动态懒加载以及回显

    全国省市区街道的json数据

Global site tag (gtag.js) - Google Analytics