Spring03

一、代理模式

        代理模式(Proxy Pattern)是一种结构型设计模式,它的概念很简单,它通过创建一个代理对象来控制对原始对象的访问。代理模式主要涉及两个角色:代理角色真实角色。代理类负责代理真实类,为真实类提供控制访问的功能,真实类则完成具体的业务逻辑。这样,当我们不方便或者不能直接访问真实对象时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象

Spring03_第1张图片

 1.1、代理模式的实现方式 

        实现代理模式有两种方案:静态代理和动态代理静态代理是指代理类在编译期就已经确定,即需要事先手动编写一个代理类动态代理则是在运行时动态生成代理类

        动态代理方案两种实现:其一,通过Java本身自带 java.lang.reflect.Proxy 类来实现动态代理功能。其二,通过额外引入一个开源的高性能代码生成包CGlib来动态生成代理类。二者底层实现上都应用了反射和操作字节码技术 

        JDK动态代理是基于接口实现的代理,只能代理实现了接口的类

        CGlib方式是基于继承实现的代理,它不是指真实类需要继承某个父类,而是生成的代理类作为真实类的子类去代理父类,即代理类继承自真实类。这种方式不需实现接口,可以作为JDK代理方式的补充方案。

 Spring03_第2张图片

二、静态代理的实现

 抽象接口:定义视频播放器接口Player

public interface Player {
    void loadVideo(String filename);
    void playVideo(String filename);
}

 真实类:定义接口实现类VPlayer

public class VPlayer implements Player {
    @Override
    public void loadVideo(String filename) {
        System.out.println("加载MP4视频文件:"+filename);
    }

    @Override
    public void playVideo(String filename) {
        System.out.println("播放MP4视频:"+filename);
    }
}

代理类:定义代理类VPlayerProxy,实现同样的接口 

public class VPlayerProxy implements Player {

    private Player player;

    public VPlayerProxy(Player player) {
        this.player = player;
    }

    @Override
    public void loadVideo(String filename) {
        player.loadVideo(filename);
    }

    @Override
    public void playVideo(String filename) {
        player.playVideo(filename);
    }
}

客户端调用

public class Client1 {
    public static void main(String[] args) {
        //直连方式
        Player vplay=new VPlayer();
        vplay.playVideo("aaa.mp4");
        System.out.println();

        //代理方式
        Player proxy=new VPlayerProxy(vplay);
        proxy.loadVideo("aaa.mp4");
        proxy.playVideo("aaa.mp4");

    }
}

最终得到的结果:

Spring03_第3张图片

2.1、分析

        Spring03_第4张图片

         客户端调用中,采用代理方式时,Player proxy=new VPlayerProxy(vplay);采用多态的形式创建了代理类的对象proxy,此时通过有参构造,传入的参数是真实类的对象vplay。

        

 Spring03_第5张图片

        通过上图可以知道,传入的真实类的对象vplay赋值给了成员变量player。   

 Spring03_第6张图片

         再去调用代理对象的方法时,实际是使用真实类的对象去调用真实类的方法

三、JDK 实现动态代理

        JDK动态代理是Java标准库中提供的一种代理方式,它可以在运行时动态生成一个代理对象,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理。JDK动态代理通过反射机制实现代理功能。

抽象接口:定义明星接口Star

package com.itheima.proxy;

public interface Star {
    String sing(String name);

    void dance();
}

真实类:定义接口实现类BigStar

package com.itheima.proxy;

public class BigStar implements Star {
    private String name;

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

    public String sing(String name) {
        System.out.println(this.name + "正在唱:" + name);
        return "谢谢!";
    }

    public void dance() {
        System.out.println(this.name + "正在优美的跳舞~~");
    }
}

 工具类:定义生成代理类的工具类ProxyUtil,此时的代理类是在运行时动态生成的。

package com.itheima.proxy;

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

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar) {
        //ClassLoader loader,
        //Class[] interfaces,
        //InvocationHandler h)

        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20万");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱100万");
                        }
                        return method.invoke(bigStar, args);
                    }
                });
        return starProxy;
    }
}

 客户端调用

package com.itheima.proxy;

import org.springframework.cglib.proxy.Enhancer;


public class Test1 {
    public static void main(String[] args) {
        BigStar star = new BigStar("大明星");
        Star starProxy = ProxyUtil.createProxy(star);

        String rs = starProxy.sing("七里香");
        System.out.println(rs);





    }
}

最终得到的结果:

 

3.1、分析 

        JDK动态代理是基于反射实现的,所以导入的包是import java.lang.reflect.Proxy;

采用的是Proxy的静态方法newProxyInstance去创建代理对象。

        Proxy.newProxyInstance中的参数含义:

1、ClassLoader loader:指定一个类加载器,把生成的代码对象加载到内存中。

一般采用        当前类.class.getClassLoader()

2、Class[] interfaces:指定真实类实现的接口,用于指定生成的代理类有哪些方法

3、InvocationHandler h:这是一个接口,所以直接new出来的是它的匿名内部类。

4、整个流程如下:

        4.1、首先创建代理对象starProxy:Star starProxy = ProxyUtil.createProxy(star);

        4.2、调用代理对象的sing方法:String rs = starProxy.sing("七里香");

        4.3、调用了代理对象的任何方法都会调用代理对象的invoke方法,因此invoke方法被称为回调方法

        4.4、进入了invoke方法后就会进行判断:

                if (method.getName().equals("sing")) {

                System.out.println("准备话筒,收钱20万");

                        }

        就会打印:准备话筒,收钱20万

        4.5、调用真实对象的方法:

        method.invoke(bigStar, args);会去调用真实对象的方法,其中bigStar是传入的真实对象,

 method是真实对象中的方法,args是传入的参数,会传到真实对象的方法中。

四、cglib实现动态代理

        CGLIB(Code Generation Library)是一个基于ASM(Java字节码操作框架)实现的代码生成库,它可以在运行时动态生成目标类的子类作为代理类并覆盖其中的方法来实现代理功能。与Java自带的JDK动态代理不同,CGlib动态代理可以代理没有实现接口的类

真实类: 没有实现任何接口

//音频播放器
public class APlayer {
    public void loadAudio(String filename) {
        System.out.println("加载MP3音频文件:"+filename);
    }

    public void playAudio(String filename) {
        System.out.println("播放MP3:"+filename);
    }
}

 工具类:定义生成代理类的工具类CglibProxyFactory,此时的代理类是在运行时动态生成的。

public class CglibProxyFactory implements MethodInterceptor {

    @SuppressWarnings("unchecked")
    public  T getProxy(Class clazz) {
        Enhancer en = new Enhancer();
        //设置代理的父类
        en.setSuperclass(clazz);
        //设置方法回调
        en.setCallback(this);
        //创建代理实例
        return (T)en.create();
    }

    @Override
    //参数中的object是目标对象,method和args是目标对象的方法和参数,methodProxy是方法代理
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        if ("loadAudio".equals(method.getName())) {
            //通过继承的方法实现代理,因此这里调用invokeSuper
            result = methodProxy.invokeSuper(object, args);
        }
        if ("playAudio".equals(method.getName())) {
            result = methodProxy.invokeSuper(object, args);
        }
        return result;
    }
}

客户端调用

public class Client3 {
    public static void main(String[] args) {
        APlayer aplayer=new APlayer();
        APlayer proxy = new CglibProxyFactory().getProxy(aplayer.getClass());
        //验证代理类的父类
        System.out.println("代理类的父类:"+proxy.getClass().getSuperclass().getSimpleName());
        System.out.println();

        proxy.loadAudio("荷塘月色.mp3");
        proxy.playAudio("荷塘月色.mp3");
    }
}

最终的结果:

Spring03_第7张图片

4.1、分析 

整个流程如下:

1、

APlayer proxy = new CglibProxyFactory().getProxy(aplayer.getClass());

通过CglibProxyFactory工具类生成代理对象proxy。

2、

proxy.loadAudio("荷塘月色.mp3");

此时调用了代理对象中的loadAudio方法,就会被代理对象中intercept方法拦截。

所有调用代理对象中的方法都会被intercept方法拦截。

3、if ("loadAudio".equals(method.getName())) {
            //通过继承的方法实现代理,因此这里调用invokeSuper
            result = methodProxy.invokeSuper(object, args);
        }

        判断为true,就会进行result = methodProxy.invokeSuper(object, args);方法

result = methodProxy.invokeSuper(object, args);调用的是真实对象中的方法。

object表示真实对象,

methodProxy表示真实对象中的方法,

args表示传过去的参数

五、总结

1、 实现代理模式有两种方案:静态代理和动态代理。

2、静态代理是指代理类在编译期就已经确定,即需要事先手动编写一个代理类

3、动态代理则是在运行时动态生成代理类

4、JDK动态代理是基于接口实现的代理,只能代理实现了接口的类。

5、CGlib方式是基于继承实现的代理,它不是指真实类需要继承某个父类,而是生成的代理类作为真实类的子类去代理父类,即代理类继承自真实类。这种方式不需实现接口。

参考文章:
代理模式:一文彻底搞懂静态代理和动态代理-CSDN博客

你可能感兴趣的:(Spring框架,Java基础进阶,java,代理模式)