资讯专栏INFORMATION COLUMN

Java三种代理模式:静态代理、动态代理和cglib代理

Kaede / 1724人阅读

摘要:动态代理又被称为代理或接口代理。静态代理在编译时产生字节码文件,可以直接使用,效率高。代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但会继承目标对象,需要重写方法,所以目标对象不能为类。

一、代理模式介绍

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。

代理模式UML类图

举个例子,我们生活中经常到火车站去买车票,但是人一多的话,就会非常拥挤,于是就有了代售点,我们能从代售点买车票了。这其中就是代理模式的体现,代售点代理了火车站对象,提供购买车票的方法。

二、静态代理

这种代理方式需要代理对象和目标对象实现一样的接口。

优点:可以在不修改目标对象的前提下扩展目标对象的功能。

缺点:

冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。

不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。

举例:保存用户功能的静态代理实现

接口类: IUserDao

package com.proxy;

public interface IUserDao {
    public void save();
}

目标对象:UserDao

package com.proxy;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("保存数据");
    }
}

静态代理对象:UserDapProxy 需要实现IUserDao接口!

package com.proxy;

public class UserDaoProxy implements IUserDao{

    private IUserDao target;
    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }
    
    @Override
    public void save() {
        System.out.println("开启事务");//扩展了额外功能
        target.save();
        System.out.println("提交事务");
    }
}

测试类:TestProxy

package com.proxy;

import org.junit.Test;

public class StaticUserProxy {
    @Test
    public void testStaticProxy(){
        //目标对象
        IUserDao target = new UserDao();
        //代理对象
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();
    }
}

输出结果

开启事务
保存数据
提交事务
三、动态代理

动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。

静态代理与动态代理的区别主要在:

静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件

动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

特点:
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。

JDK中生成代理对象主要涉及的类有

java.lang.reflect Proxy,主要方法为

static Object    newProxyInstance(ClassLoader loader,  //指定当前目标对象使用类加载器

 Class[] interfaces,    //目标对象实现的接口的类型
 InvocationHandler h      //事件处理器
) 
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

java.lang.reflect InvocationHandler,主要方法为

 Object    invoke(Object proxy, Method method, Object[] args) 
// 在代理实例上处理方法调用并返回结果。

举例:保存用户功能的动态代理实现

接口类: IUserDao

package com.proxy;

public interface IUserDao {
    public void save();
}

目标对象:UserDao

package com.proxy;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("保存数据");
    }
}

动态代理对象:UserProxyFactory

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    private Object target;// 维护一个目标对象

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 为目标对象生成代理对象
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开启事务");

                        // 执行目标对象方法
                        Object returnValue = method.invoke(target, args);

                        System.out.println("提交事务");
                        return null;
                    }
                });
    }
}

测试类:TestProxy

package com.proxy;

import org.junit.Test;

public class TestProxy {

    @Test
    public void testDynamicProxy (){
        IUserDao target = new UserDao();
        System.out.println(target.getClass());  //输出目标对象信息
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());  //输出代理对象信息
        proxy.save();  //执行代理方法
    }
}

输出结果

class com.proxy.UserDao
class com.sun.proxy.$Proxy4
开启事务
保存数据
提交事务
四、cglib代理

cglib is a powerful, high performance and quality Code Generation Library. It can extend JAVA classes and implement interfaces at runtime.

cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

cglib特点

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
如果想代理没有实现接口的类,就可以使用CGLIB实现。

CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。

cglib与动态代理最大的区别就是

使用动态代理的对象必须实现一个或多个接口

使用cglib代理的对象则无需实现接口,达到代理类无侵入。

使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。

cglib的Maven坐标


    cglib
    cglib
    3.2.5

举例:保存用户功能的动态代理实现

目标对象:UserDao

package com.cglib;

public class UserDao{

    public void save() {
        System.out.println("保存数据");
    }
}

代理对象:ProxyFactory

package com.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ProxyFactory implements MethodInterceptor{

    private Object target;//维护一个目标对象
    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    //为目标对象生成代理对象
    public Object getProxyInstance() {
        //工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类对象代理
        return en.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开启事务");
        // 执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        System.out.println("关闭事务");
        return null;
    }
}

测试类:TestProxy

package com.cglib;

import org.junit.Test;

public class TestProxy {

    @Test
    public void testCglibProxy(){
        //目标对象
        UserDao target = new UserDao();
        System.out.println(target.getClass());
        //代理对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());
        //执行代理对象方法
        proxy.save();
    }
}

输出结果

class com.cglib.UserDao
class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6
开启事务
保存数据
关闭事务
五、总结

静态代理实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。

JDK动态代理需要目标对象实现业务接口,代理类只需实现InvocationHandler接口。

动态代理生成的类为 lass com.sun.proxy.$Proxy4,cglib代理生成的类为class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6。

静态代理在编译时产生class字节码文件,可以直接使用,效率高。

动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

六、相关资料

代理模式相关知识

代理模式及Java实现动态代理

设计模式读书笔记 - 代理模式

JDK动态代理实现原理

Java 动态代理机制分析及扩展

Java代理(jdk静态代理、动态代理和cglib动态代理)

AOP的底层实现-CGLIB动态代理和JDK动态代理

深入浅出CGlib-打造无入侵的类代理

Spring AOP 实现原理与 CGLIB 应用

UML相关知识

博客 - UML类图与类的关系详解

goole图书 -《UML建模实例详解》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70513.html

相关文章

  • Java三种代理模式

    Java的三种代理模式 参考:http://www.cnblogs.com/cenyu/...Java核心技术原书第九版6.5节 为什么使用代理   我们在写一个功能函数时,经常需要在其中写入与功能不是直接相关但很有必要的代 码,如日志记录,信息发送,安全和事务支持等,这些枝节性代码虽然是必要的,但它会带来以下麻烦: 枝节性代码游离在功能性代码之外,它不是函数的目的,这是对OO是一种破坏 枝节性...

    Rango 评论0 收藏0
  • Java设计模式代理模式

    摘要:设计模式之代理模式今天学到的动态代理实现,对代理这个概念很模糊,看了一篇文章发现这是一种设计模式,于是学习记录一下。简介代理模式是一种对象结构型的模式,主要为其他对象提供一种代理以控制对这个对象的访问。下面依次讲解着三种代理。 设计模式之代理模式 今天学到Spring的动态代理实现AOP,对代理这个概念很模糊,看了一篇文章发现这是一种设计模式,于是学习记录一下。 简介 代理模式是一种对...

    ZHAO_ 评论0 收藏0
  • JAVA代理模式的理解应用

    摘要:代理模式代理模式通俗一点的解释就是在操作一个对象和对象中的方法时,不是直接操作这个对象,还是通过一个代理对象来操作这个实际的目标对象。 代理模式: 代理模式通俗一点的解释就是在操作一个对象和对象中的方法时,不是直接操作这个对象,还是通过一个代理对象来操作这个实际的目标对象。应用场景一般是需要在执行某个已经写好的方法前后再添加一段逻辑,比如执行方法前打印日志,或者在执行方法之前和之后打时...

    CatalpaFlat 评论0 收藏0
  • 浅入浅出Java代理三种实现

    摘要:代理模式的实现静态代理优缺点优点只对对需要的方法加代理逻辑。通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法处理的情况。 注意:本文所有的class使用的static修饰主要是为了能在一个类里面测试。实际项目中不应该这样做的,应该分包分class。文字描述不是很多,还是看代码比较好理解吧... 1. Java代理的理解 代理模式是一种设计模式,简单说即是在不改变源...

    lewif 评论0 收藏0
  • 设计模式代理模式

    摘要:下面我们通过玩英雄联盟代练的例子来说明下登录游戏赢下了一局英雄联盟,获得了金币测试结果登录游戏赢下了一局英雄联盟,获得了金币可以这样理解,自己写代理类的方式就是静态代理。 前言 刚上大学那会,英雄联盟火的一塌糊涂,当时每天都想着升到30级开启排位之旅。可是升到30级需要大把的时间不说,这时候匹配到的人,水平过于参差不齐,问候你全家的事经常发生,那个时候就想要是能有个代练帮我升到30级该...

    xuweijian 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<