动态代理两种方式

1.动态代理两种方式简述

JDK动态代理:利用反射机制生成一个实现代理接口匿名类,在调用具体方法前调用InvokeHandler来处理。

CGLib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

2.动态代理的特点

1.字节码随用随创建,随用随加载。
2.它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
3.装饰者模式就是静态代理的一种体现。

3.动态代理常用的有两种方式

3.1 基于接口的动态代理

提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。

3.2 基于子类的动态代理

提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。

4.使用JDK官方的Porxy类创建对象

实体类:

package com.jh.spring13jdk动态代理;

import lombok.Data;

@Data
public class Game implements Open{
    //游戏的网速
    private int ms = 460;

    @Override
    public int openApp() {
        System.out.println("打开游戏后的网速是:" + this.getMs());
        return this.getMs();
    }
}

接口:

package com.jh.spring13jdk动态代理;

public interface Open {
    int openApp();
}

测试类:

package com.jh.spring13jdk动态代理;

import org.junit.Test;

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


public class Spring13Test {
    //JDK动态代理 省略了代理对象!!! 直接有jdk中的代理类来实现,但是目标类必须有接口
    @Test
    public void test01() {
        final Game game = new Game();
        /**
         * 代理:
         * 间接。
         * 获取代理对象:
         * 要求:
         * 被代理类最少实现一个接口
         * 创建的方式
         * Proxy.newProxyInstance(三个参数)
         * 参数含义:
         * ClassLoader:和被代理对象使用相同的类加载器。
         * Interfaces:和被代理对象具有相同的行为。实现相同的接口。
         * InvocationHandler:如何代理。
         * 策略模式:使用场景是:
         * 数据有了,目的明确。
         * 如何达成目标,就是策略。
         *
         */
        //使用JDK动态类对象,当作迅游加速器的类,代替了静态的代理类
        //Proxy:代理的意思。 newProxyInstancece创建代理对象
        Open jdkProxy = (Open) Proxy.newProxyInstance(
                game.getClass().getClassLoader(), //类的加载器
                game.getClass().getInterfaces(), //类的所有接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Integer ms = (Integer) method.invoke(game, args);
                        if (ms != null) {
                            ms = ms - 400;
                        }
                        return ms;
                    }
                }
        );
        int i = jdkProxy.openApp();
        System.out.println("i=" + i);
    }
}

5.使用CGLib的Enhancer类创建代理对象

对于没有接口的类,如何实现动态代理呢,这就需要CGLib了,CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承。所以不能对final修饰的类进行代理。

实体类:

package com.jh.spring14cjlib动态代理;

import lombok.Data;

/**
 * 目标类
 * 父母
 */
@Data
public class Parents {
    //成绩
    private int score = 599;

    //高考
    public int gaoKao(){
        System.out.println("父母参加高考,分数是:"+this.getScore());
        return this.getScore();
    }
}

工厂类:

package com.jh.spring14cjlib动态代理;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//MethodInterceptor:方法上的拦截
public class ParentsFactory implements MethodInterceptor {
    private Parents parents;

    public ParentsFactory() {
        parents = new Parents();
    }


    /**
     * 基于子类的动态代理
     * 要求:
     * 被代理对象不能是最终类
     * 用到的类:
     * Enhancer
     * 用到的方法:
     * create(Class, Callback)
     * 方法的参数:
     * Class:被代理对象的字节码
     * Callback:如何代理
     */
    //增强器 ,把parents创造一个子类
    public Parents createParentsSon() {
        //使用字节码增强器,去增强我们的父类
        Enhancer enhancer = new Enhancer(); //字节码增强器,可以读懂class文件
        //enhancer 指定一个对象
        enhancer.setSuperclass(Parents.class);//反射
        //使用工厂,换行(创建子类)
        enhancer.setCallback(this);
        //创建子类
        Parents son = (Parents) enhancer.create(); //多态
        return son;
    }

    /**
     * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
     方法进行增强。
     *
     * 参数:
     * 前三个和基于接口的动态代理是一样的。
     * MethodProxy:当前执行方法的代理对象。
     * 返回值:
     * 当前执行方法的返回值
     */
    //方法的拦截
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Integer score = (Integer) method.invoke(parents, objects);
        if (score != null) {
            score = score + 30;
        }
        return score;
    }
}

测试类:

package com.jh.spring14cjlib动态代理;

import org.junit.Test;

public class Spring14Test {
    @Test
    public void test01(){
        ParentsFactory parentsFactory = new ParentsFactory();
        Parents parentsSon = parentsFactory.createParentsSon();
        int score = parentsSon.getScore();
        System.out.println(score);
    }
}

6.总结:

CGLib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLib创建对象时所花费的时间却比JDK多的的。

7.问题:

为什么,要在不改变源代码的基础上,去写一个代理类增强一些功能呢?

因为项目大了,就有主要的功能和次要的功能,要想主要功能和次要功能一起运行,必须用AOP

我再解释下面向对象和面向切面,面向对象OOP,面向的是主要功能的对象,而AOP是面向对象OOP的一个补充,面向次要功能的对象

目的是为了降低耦合度,提高代码的复用性。(自己总结的,仅供参考,你懂的,嘻嘻)。

posted @ 2021-12-02 19:36  蔡地像徐坤  阅读(322)  评论(0编辑  收藏  举报