静态代理和动态代理有什么区别?--乐字节java

代理模式

​ 代理模式在 Java 开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用。如租房的例子:房客、中介、房东。对应于代理模式中即:客户类、代理类 、委托类(被代理类)。

​ 为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同的父类或父接口。代理类会对请求做预处理、过滤,将请求分配给指定对象。

​ 生活中常见的代理情况:

​ 租房中介、婚庆公司等

​ 代理模式的两个设计原则:

1. 代理类 与 委托类 具有相似的行为(共同)

2. 代理类增强委托类的行为

SpringAOP-01.png

​ 常用的代理模式:

​ 1. 静态代理

​ 2. 动态代理

静态代理

​ 某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

代理的三要素

​ a、有共同的行为(结婚) - 接口

​ b、目标角色(新人) - 实现行为

​ c、代理角色(婚庆公司) - 实现行为 增强目标对象行为

静态代理的特点

​ 1、目标角色固定

​ 2、在应用程序执行前就得到目标角色

​ 3、代理对象会增强目标对象的行为

​ 4、有可能存在多个代理 引起"类爆炸"(缺点)

静态代理的实现

定义行为(共同) 定义接口

/**
 * 定义行为
 */
public interface Marry {
    public void toMarry();
}

目标对象(实现行为)

/**
 * 静态代理 ——> 目标对象
 */
public class You implements  Marry {
    // 实现行为
    @Override
    public void toMarry() {
        System.out.println("我要结婚了...");
    }
}

代理对象(实现行为、增强目标对象的行为)

/**
 * 静态代理 ——> 代理对象
 */
public class MarryCompanyProxy implements Marry {

    // 目标对象
    private Marry marry;
    // 通过构造器将目标对象传入
    public MarryCompanyProxy(Marry marry) {
        this.marry = marry;
    }

    // 实现行为
    @Override
    public void toMarry() {
        // 增强行为
        before();
                
        // 执行目标对象中的方法
        marry.toMarry();
        
        // 增强行为
        after();
    }

    /**
     * 增强行为
     */
    private void after() {
        System.out.println("新婚快乐,早生贵子!");
    }

    /**
     * 增强行为
     */
    private void before() {
        System.out.println("场地正在布置中...");
    }
}

通过代理对象实现目标对象的功能

// 目标对象
You you = new You();
// 构造代理角色同时传入真实角色
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通过代理对象调用目标对象中的方法
marryCompanyProxy.toMarry();

​ 静态代理对于代理的角色是固定的,如dao层有20个dao类,如果要对方法的访问权限进行代理,此时需要创建20个静态代理角色,引起类爆炸,无法满足生产上的需要,于是就催生了动态代理的思想。

如有疑问,可加入群:10803-55292,输入暗号13,即可有大佬帮助

动态代理

​ 相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法即满足生产需要的同时又达到代码通用的目的

​ 动态代理的两种实现方式:

​ 1. JDK 动态代理

​ 2. CGLIB动态代理

动态代理的特点

  1. 目标对象不固定
  2. 在应用程序执行时动态创建目标对象
  3. 代理对象会增强目标对象的行为

JDK动态代理

注:JDK动态代理的目标对象必须有接口实现

newProxyInstance

Proxy类:

​ Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下操作方法:

/*
    返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代理对象)
        loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
        interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果                    我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这                    组接口中的方法了
        h:一个InvocationHandler接口,表示代理实例的调用处理程序实现的接口。每个代理实例都具有一个           关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序            的 invoke 方法(传入InvocationHandler接口的子类)
*/
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

获取代理对象

public class JdkHandler implements InvocationHandler {

    // 目标对象
    private Object target; // 目标对象的类型不固定,创建时动态生成
    // 通过构造器将目标对象赋值
    public JdkHandler(Object target) {
        this.target = target;
    }

    /**
     *  1、调用目标对象的方法(返回Object)
     *  2、增强目标对象的行为
     * @param proxy 调用该方法的代理实例
     * @param method  目标对象的方法
     * @param args  目标对象的方法形参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = method.invoke(target,args);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }


    /**
     * 得到代理对象
     * public static Object newProxyInstance(ClassLoader loader,
     *                                       Class<?>[] interfaces,
     *                                       InvocationHandler h)
     *      loader:类加载器
     *      interfaces:接口数组
     *      h:InvocationHandler接口 (传入InvocationHandler接口的实现类)
     *
     *
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

通过代理对象实现目标对象的功能

// 目标对象
You you = new You();
// 获取代理对象
JdkHandler jdkHandler = new JdkHandler(you);
Marry marry = (Marry) jdkHandler.getProxy();
// 通过代理对象调用目标对象中的方法
marry.toMarry();    
问:Java动态代理类中的invoke是怎么调用的?

答:在生成的动态代理类$Proxy0.class中,构造方法调用了父类Proxy.class的构造方法,给成员变量invocationHandler赋值,$Proxy0.class的static模块中创建了被代理类的方法,调用相应方法时方法体中调用了父类中的成员变量InvocationHandler的invoke()方法。

注:JDK的动态代理依靠接口实现,如果有些类并没有接口实现,则不能使用JDK代理。

CGLIB 动态代理

​ JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理,cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

添加依赖

在pom.xml文件中引入cglib的相关依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

定义类

实现MethodInterceptor接口

public class CglibInterceptor implements MethodInterceptor {

    // 目标对象
    private Object target;
    // 通过构造器传入目标对象
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy() {
        // 通过Enhancer对象的create()方法可以生成一个类,用于生成代理对象
        Enhancer enhancer = new Enhancer();
        // 设置父类 (将目标类作为其父类)
        enhancer.setSuperclass(target.getClass());
        // 设置拦截器 回调对象为本身对象
        enhancer.setCallback(this);
        // 生成一个代理类对象,并返回
        return enhancer.create();
    }

    /**
     * 拦截器
     *  1、目标对象的方法调用
     *  2、增强行为
     * @param object  由CGLib动态生成的代理类实例
     * @param method  实体类所调用的被代理的方法引用
     * @param objects 参数值列表
     * @param methodProxy  生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, 
                            MethodProxy methodProxy) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = methodProxy.invoke(target,objects);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }

}

调用方法

// 目标对象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();

User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();

JDK代理与CGLIB代理的区别

  • JDK动态代理实现接口,Cglib动态代理继承思想
  • JDK动态代理(目标对象存在接口时)执行效率高于Ciglib
  • 如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343