您现在的位置是:首页 >学无止境 >设计模式之代理模式网站首页学无止境

设计模式之代理模式

King Gigi. 2024-06-17 11:19:30
简介设计模式之代理模式

1、代理模式基本介绍

代理模式的定义:

  • 为其他对象提供一种代理以控制对这个对象的访问
  • 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

简单来说

  • 代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理

  • 为真实对象提供代理,然后供其他对象通过代理访问真实对象

2、Jdk中的动态代理

2.1、场景推导

实现一个简单的加减乘除运算功能

interface ICalc{
    int add(int a,int b);
    int sub(int a,int b);
    int mul(int a,int b);
    int div(int a,int b);
}
class  CalcImpl implements ICalc{
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        return a-b;
    }

    @Override
    public int mul(int a, int b) {
        return a*b;
    }

    @Override
    public int div(int a, int b) {
        return a/b;
    }
}
class AppTest{
    public static void main(String[] args) {
        CalcImpl c = new CalcImpl();
        System.out.println(c.add(4,2));
        System.out.println(c.sub(4,2));
        System.out.println(c.mul(4,2));
        System.out.println(c.div(4,2));
    }
}

现在变化来了,客户要求为每个方法添加日志,记录方法开始和结束的时机

package com.hh.demo.designpattern;

interface ICalc{
    int add(int a,int b);
    int sub(int a,int b);
    int mul(int a,int b);
    int div(int a,int b);
} 
class  CalcImpl implements ICalc{
    @Override
    public int add(int a, int b) {
        System.out.println("add方法开始!" +"a="+a+"b="+b);
        int r = a+b;
        System.out.println("add方法结束!" +"r="+r);
        return r;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("sub方法开始!" +"a="+a+"b="+b);
        int r = a-b;
        System.out.println("sub方法结束!" +"r="+r);
        return r;
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("mul方法开始!" +"a="+a+"b="+b);
        int r = a*b;
        System.out.println("mul方法结束!" +"r="+r);
        return r;
    }

    @Override
    public int div(int a, int b) {
        System.out.println("div方法开始!" +"a="+a+"b="+b);
        int r = a/b;
        System.out.println("div方法结束!" +"r="+r);
        return r;
    }
}
class AppTest{
    
    public static void main(String[] args) {
        CalcImpl c = new CalcImpl();
        System.out.println(c.add(4,2));
        System.out.println(c.sub(4,2));
        System.out.println(c.mul(4,2));
        System.out.println(c.div(4,2));
    }
}

梭哈搞定,打完收工!

我们发现,这样完成业务根本不是一个好办法

  1. 代码在重复,核心业务(加减乘除)和非核心业务(打印日志)在不断的重复。
  2. 如果Icalc和CalcImpl不是我们自己创建的,是被发现的,那我们手里是没有源代码的,不能直接修改源代码(开闭原则)
  3. 需求如果再次变化,需要加入开方,求余的过程 ;又或者客户要求 上午需要日志,下午不需要日志!!!

我们尝试使用动态代理来完成上述功能

2.2、Jdk动态代理

Jdk动态代理:在程序的执行过程中,使用jdk的反射机制,创建代理对象,并动态的指定代理的目标类

在这里插入图片描述

先来看看业务逻辑是怎么实现的

package com.hh.demo.designpattern;

interface ICalc {
    int add(int a, int b);

    int sub(int a, int b);

    int mul(int a, int b);

    int div(int a, int b);
}
class CalcImpl implements ICalc {
    @Override
    public int add(int a, int b) {
        int r = a + b;
        return r;
    }

    @Override
    public int sub(int a, int b) {
        int r = a - b;
        return r;
    }

    @Override
    public int mul(int a, int b) {
        int r = a * b;
        return r;
    }

    @Override
    public int div(int a, int b) {
        int r = a / b;
        return r;
    }
}
//调用处理器
class MyHandler implements InvocationHandler {
    //关联
    private ICalc calculator;
    public MyHandler(ICalc calculator) {
        this.calculator = calculator;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"开始,参数:"+ Arrays.toString(args));
        //利用反射机制,调用方法
        //把method所代表的方法,当作calculator对象的调用,参数是args
        //Object是为了通用
        Object res = method.invoke(calculator, args);
        System.out.println(method.getName()+"结束,结果是:"+ res);
        return res;//这个返回值会返回到代理对象的方法调用处
    }
}
public class AppTest {
    public static void main(String[] args) {
        ICalc calculator = new CalcImpl();
        //当前类的字节码获得当前类的类加载器
        ClassLoader classLoader = AppTest.class.getClassLoader();
        //创建代理对象,需要传入三个参数
        ICalc proxy = (ICalc) Proxy.newProxyInstance(classLoader, new Class[]{ICalc.class}, new MyHandler(calculator));
        //总之,对代理对象的方法的调用,都统统会进入调用处理器中
        proxy.add(3, 2);
        proxy.sub(3, 2);
        proxy.mul(3, 2);
        proxy.div(3, 2);
    }
    /**
     * add开始,参数:[3, 2]
     * add结束,结果是:5
     * sub开始,参数:[3, 2]
     * sub结束,结果是:1
     * mul开始,参数:[3, 2]
     * mul结束,结果是:6
     * div开始,参数:[3, 2]
     * div结束,结果是:1
     *
     * Process finished with exit code 0
     */
}

我们先来看看动态代理api

Proxy.newProxyInstance();

里面传三个参数,分别是:

image-20221005161215388

  • 第一个参数:

    • 实例化一个对象,必然会调用类的构造器。在调用构造器之前,Jvm会加载该类的字节码,而Jvm就是使用类加载器来加载类的字节码,这一步是Jvm自动完成的。

    • 简单来说:只要实例化对象,一定要加载类的字节码,加载字节码就一定要类的加载器。

    • 使用动态代理的api实例化对象是一种不常用的创建对象的方式,但这也是一种实例化,需要我们手动把类的加载器传入

    • 使用构造器实例化对象时Jvm会自动找到类加载器。

  • 第二个参数:

    • 第一个参数传入的类加载器,加载的是哪个类的字节码?加载的字节码就是在运行期动态生成的字节码,这个动态生成的字节码是不需要源代码的。

    • 字节码确实可以自动生成,那么动态代理api成成的字节码的内容,是根据什么生成的呢?恰恰是根据第二个参数生成的。动态生成代理,会生成一个实现了目标接口的类的字节码,在上面的栗子中就是生成了一个ICalc接口的类的字节码!

  • 第三个参数:调用处理器 InvocationHandler

    • 我们已经知道,动态代理会加载自己动态生成的字节码,且这个字节码是根据某个接口生成的,在上面的例子中就是根据ICalc接口生成的实现了ICalc接口的类的字节码
    • 实现一个接口,就要实现其中的抽象方法,那麽动态代理生成的字节码,实现了ICalc接口,必然就要实现其中的add、sub等方法
    • 这些方法被实现的方法体是什么内容呢?这恰恰是由第三个参数决定的,MyHandler类的 invoke方法,就是方法体的内容!!
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    第一个参数:动态代理的对象

    第二个参数:调用的接口方法

    第三个参数:调用的接口方法的参数

}

但是目前这个写法还是有缺点的,太复杂了,对于新手不是很友好,我们来封装一下

package com.hh.demo.designpattern;

interface ICalc {
    int add(int a, int b);

    int sub(int a, int b);

    int mul(int a, int b);

    int div(int a, int b);
}
class CalcImpl implements ICalc {
    @Override
    public int add(int a, int b) {
        int r = a + b;
        return r;
    }

    @Override
    public int sub(int a, int b) {
        int r = a - b;
        return r;
    }

    @Override
    public int mul(int a, int b) {
        int r = a * b;
        return r;
    }

    @Override
    public int div(int a, int b) {
        int r = a / b;
        return r;
    }
}
class MyHandler implements InvocationHandler {
    //关联
    private Object target;
    public MyHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"开始,参数:"+ Arrays.toString(args));
        //利用反射机制,调用方法
        //把method所代表的方法,当作calculator对象的调用,参数是args
        //Object是为了通用
        Object res = method.invoke(target, args);
        System.out.println(method.getName()+"结束,结果是:"+ res);

        return res;//这个返回值会返回到代理对象的方法调用处
    }
}
//----------------------------------------------------------------------------------------------------
class MyProxy{
    //封装:对外隐藏复杂的实现细节,暴露出简单的使用方法
    public Object getProxy(Object target){
        //当前类的字节码获得当前类的类加载器
        ClassLoader classLoader = MyProxy.class.getClassLoader();
        //获取target所属的类,所实现的接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //创建代理对象,需要传入三个参数
        Object proxy =  Proxy.newProxyInstance(classLoader,interfaces, new MyHandler(target));

        return proxy;
    }
}
public class AppTest {
    public static void main(String[] args) {
        ICalc calculator = new CalcImpl();
        ICalc proxy = (ICalc) new MyProxy().getProxy(calculator);
        proxy.add(3, 2);
        proxy.sub(3, 2);
        proxy.mul(3, 2);
        proxy.div(3, 2);
    }
    /**
     * add开始,参数:[3, 2]
     * add结束,结果是:5
     * sub开始,参数:[3, 2]
     * sub结束,结果是:1
     * mul开始,参数:[3, 2]
     * mul结束,结果是:6
     * div开始,参数:[3, 2]
     * div结束,结果是:1
     *
     * Process finished with exit code 0
     */
}

MyProxy类对外隐藏复杂的实现细节,暴露出简单的使用方法。似乎有点代理的意思了

目前看起来似乎挺好的,但是仍然有问题:

目前我们创建的代理对象,只能在真实对象的真实方法调用前后加上日志,无法扩展其他功能,比如,用户不想加日志功能,而是想加缓存功能,或者权限控制…

再次封装代码,我们定义一个接口,用来描述代理类对应方法执行前后需要拓展执行的方法。

称这个接口为Interceptor拦截器,也可以理解为切面

package com.hh.demo.designpattern;

interface ICalc {
    int add(int a, int b);

    int sub(int a, int b);

    int mul(int a, int b);

    int div(int a, int b);
}
class CalcImpl implements ICalc {
    @Override
    public int add(int a, int b) {
        int r = a + b;
        return r;
    }

    @Override
    public int sub(int a, int b) {
        int r = a - b;
        return r;
    }

    @Override
    public int mul(int a, int b) {
        int r = a * b;
        return r;
    }

    @Override
    public int div(int a, int b) {
        int r = a / b;
        return r;
    }
}
class MyHandler implements InvocationHandler {
    //关联
    private Object target;
    private Interceptor interceptor;

    public MyHandler(Object target,Interceptor interceptor) {
        this.target = target;
        this.interceptor = interceptor;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法执行前的钩子函数
        interceptor.before(target, method, args);

        Object res = method.invoke(target, args);

        //方法执行后的钩子函数
        interceptor.after(target, method, args, res);
        // 返回到代理对象的方法调用处
        return res;
    }
}
class MyProxy{
    public Object getProxy(Object target, Interceptor interceptor){
        //当前类的字节码获得当前类的类加载器
        ClassLoader classLoader = MyProxy.class.getClassLoader();
        //获取target所属的类,所实现的接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //创建代理对象,需要传入三个参数
        Object proxy =  Proxy.newProxyInstance(classLoader,interfaces, new MyHandler(target,interceptor));

        return proxy;
    }
}
interface Interceptor {
    //前置通知
    void before(Object target, Method method, Object[] args);
	//后置通知
    void after(Object target, Method method, Object[] args, Object returnVal);
}
//----------------------------------------------------------------------------------------------------
//用户制作拦截器的实现类
class LogInterceptor implements Interceptor{

    @Override
    public void before(Object target, Method method, Object[] args) {
        System.out.println(String.format("方法名为:%s,参数为:%s", method.getName(), Arrays.toString(args)));
    }

    @Override
    public void after(Object target, Method method, Object[] args, Object returnVal) {
        System.out.println(String.format("返回结果为:%s", returnVal.toString()));
    }
}
public class AppTest {
    public static void main(String[] args) {
        ICalc calculator = new CalcImpl();
        ICalc proxy = (ICalc) new MyProxy().getProxy(calculator,new LogInterceptor());
        proxy.add(3, 2);
        proxy.sub(3, 2);
        proxy.mul(3, 2);
        proxy.div(3, 2);
    }
    /**
     * 方法名为:add,参数为:[3, 2]
     * 返回结果为:5
     * 方法名为:sub,参数为:[3, 2]
     * 返回结果为:1
     * 方法名为:mul,参数为:[3, 2]
     * 返回结果为:6
     * 方法名为:div,参数为:[3, 2]
     * 返回结果为:1
     *
     * Process finished with exit code 0
     */
}

这样就简单了很多,应对不同的需求我们就去定制不同的代理类和拦截器,实现不同的需求

可是现在变化又来了,客户有了新需求:

针对ICalc接口的日志功能,add方法使用中文日志,sub方法使用英文日志,mul方法和div方法不要日志。

这时只能用判断来解决了,针对不同方法有不同的日志。我们来实现一下

//用户制作拦截器的实现类
class LogInterceptor implements Interceptor{

    @Override
    public void before(Object target, Method method, Object[] args) {
        if("add".equals(method.getName())){
            System.out.println(String.format("方法名为:%s,参数为:%s", method.getName(), Arrays.toString(args)));
        }else if("sub".equals(method.getName())){
            System.out.println(String.format("methodName is:%s,parameter is:%s", method.getName(), Arrays.toString(args)));
        }else{
            System.out.println(method.getName()+Arrays.toString(args));
        }

    }

    @Override
    public void after(Object target, Method method, Object[] args, Object returnVal) {
        if("add".equals(method.getName())){
            System.out.println(String.format("返回结果为:%s", returnVal.toString()));
        }else if("sub".equals(method.getName())){
            System.out.println(String.format("result is:%s", returnVal.toString()));
        }else{
            System.out.println(returnVal.toString());
        }
    }
}

运行结果:

方法名为:add,参数为:[3, 2]
返回结果为:5
methodName is:sub,parameter is:[3, 2]
result is:1
mul[3, 2]
6
div[3, 2]
1

Process finished with exit code 0

可以看到,虽然做虽然满足了客户的需求,但是有很多的 if else ,感觉好像怪怪的!!!

仔细想想,这设计违反了什么设计原则呢?单一职责设计原则

那就拆分呗,设计原则不就是讲究一个字吗? 我们针对于四个方法,做四个拦截器,这里写两个作为演示

//用户制作拦截器的实现类
class  addInterceptor implements Interceptor{

    @Override
    public void before(Object target, Method method, Object[] args) {
        if("add".equals(method.getName())){
            System.out.println(String.format("方法名为:%s,参数为:%s", method.getName(), Arrays.toString(args)));
        }
    }

    @Override
    public void after(Object target, Method method, Object[] args, Object returnVal) {
        if("add".equals(method.getName())) {
            System.out.println(String.format("返回结果为:%s", returnVal.toString()));
        }
    }
}
class  subInterceptor implements Interceptor{

    @Override
    public void before(Object target, Method method, Object[] args) {
        if("sub".equals(method.getName())){
            System.out.println(String.format("methodName is:%s,parameter is:%s", method.getName(), Arrays.toString(args)));
        }
    }

    @Override
    public void after(Object target, Method method, Object[] args, Object returnVal) {
        if("sub".equals(method.getName())){
            System.out.println(String.format("result is:%s", returnVal.toString()));
        }
    }
}

客户端:

public class AppTest {
    public static void main(String[] args) {
        //calculator是目标对象
        ICalc calculator = new CalcImpl();
        //根据目标对象calculator,动态生成一个代理对象
        ICalc proxy = (ICalc) new MyProxy().getProxy(calculator,new addInterceptor());
        proxy.add(3, 2);
        proxy.sub(3, 6);
    }
    /**
     * 方法名为:add,参数为:[3, 2]
     * 返回结果为:5
     *
     * Process finished with exit code 0
     */
}

但是问题又来了

现在,getProxy方法只能传addInterceptor 或者subInterceptor,不能同时用啊

有同学可能想到用可变参数解决这个问题,没错,是可以的;

但是换一种思路,既然能根据目标对象动态代理生成一个代理对象,那我是不是可以将这个代理对象再当成一个新的目标对象动态代理一下?等等,感觉cpu要烧了!!!!

public class AppTest {
    public static void main(String[] args) {
        //calculator是目标对象
        ICalc calculator = new CalcImpl();
        //根据目标对象calculator,动态生成一个代理对象
        ICalc proxy = (ICalc) new MyProxy().getProxy(calculator,new addInterceptor());
        //我们把proxy这个代理对象,再当成一个新的目标对象
        ICalc proxy2 = (ICalc) new MyProxy().getProxy(proxy,new subInterceptor());
        //我们发现add方法和sub方法都能拦截
        proxy2.add(3, 2);
        proxy2.sub(3, 6);
    }
    /**
     * 方法名为:add,参数为:[3, 2]
     * 返回结果为:5
     * methodName is:sub,parameter is:[3, 6]
     * result is:-3
     *
     * Process finished with exit code 0
     */
}

简单理解就是套娃,贴一张图帮助理解

在这里插入图片描述

现在代码总没有问题了吧?是吗?那我说目前代码还有问题呢?

问题是:添加拦截器的顺序是逆向的,对用户不友好

解决方法:

public class AppTest {
    public static void main(String[] args) {
        //calculator是目标对象
        ICalc calculator = new CalcImpl();

        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(new addInterceptor());
        interceptors.add(new subInterceptor());

        for (int i =interceptors.size() - 1; i >=0; i--) {
           Interceptor interceptor = interceptors.get(i);
           calculator = (ICalc) MyProxy.getProxy(calculator, interceptor);
        }
        calculator.add(1,2);
        calculator.sub(3,2);

    }
    /**
     *     方法名为:add,参数为:[1, 2]
     *     返回结果为:3
     *     methodName is:sub,parameter is:[3, 2]
     *     result is:1
     *
     *     Process finished with exit code 0
     */
}

但是现在客户端代码很复杂了,对用户不友好呀

我们封装一下倒叙添加拦截器的逻辑

class MyProxy{
    public static Object getProxy(Object target, Interceptor interceptor){
        //当前类的字节码获得当前类的类加载器
        ClassLoader classLoader = MyProxy.class.getClassLoader();
        //获取target所属的类,所实现的接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //创建代理对象,需要传入三个参数
        Object proxy =  Proxy.newProxyInstance(classLoader,interfaces, new MyHandler(target,interceptor));

        return proxy;
    }
    //封装倒叙添加拦截器
    public static Object getProxy2(Object target, List<Interceptor> interceptors){

        for (int i =interceptors.size() - 1; i >=0; i--) {
            Interceptor interceptor = interceptors.get(i);
            target = (ICalc) MyProxy.getProxy(target, interceptor);
        }
        return  target;
    }
}

客户端代码:

public class AppTest {
    public static void main(String[] args) {
        //calculator是目标对象
        ICalc calculator = new CalcImpl();

        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(new addInterceptor());
        interceptors.add(new subInterceptor());

        ICalc proxy2 = (ICalc) MyProxy.getProxy2(new CalcImpl(), interceptors);

        proxy2.add(1,2);
        proxy2.sub(3,2);

    }
    /**
     *     方法名为:add,参数为:[1, 2]
     *     返回结果为:3
     *     methodName is:sub,parameter is:[3, 2]
     *     result is:1
     *
     *     Process finished with exit code 0
     */
}

现在还有问题,以后,用户要添加拦截器,删除拦截器,必然要修改应用程序代码,要修改 List,这不合理鸭

用户应该是改配置而不是改代码,所以这个interceptors根本不用传

//封装倒叙添加拦截器
public static Object getProxy2(Object target) throws Exception{
    //拦截器集合不是用户传进来的,是读取配置文件得到的,配置文件放在同一个包下
    Properties prop = new Properties();
    InputStream in = MyProxy.class.getResourceAsStream("myconfig.properties");
    prop.load(in);
    String str = prop.getProperty("interceptors");
    String[] split = str.split(",");

    List<Interceptor> interceptors = new ArrayList<>();
    for (String hh : split) {
        interceptors.add((Interceptor)Class.forName(hh).newInstance());
    }
    for (int i =interceptors.size() - 1; i >=0; i--) {
        Interceptor interceptor = interceptors.get(i);
        target = (ICalc) MyProxy.getProxy(target, interceptor);
    }
    return  target;
}

客户端:

public static void main(String[] args) throws Exception {

    ICalc proxy2 = (ICalc) MyProxy.getProxy2(new CalcImpl());

    proxy2.add(1,2);
    proxy2.sub(3,2);

}

客户端代码变得非常简洁

3、静态代理

业务场景:现在需要做一个图书解析器,解析一本书里面有多少个句子,多少个副词

package com.hh.demo.designpattern;
//图书解析器
class BookParser{
    //接收一本书的内容,字符串的值,是很大的
    private String content = "天下大事,分久必合,合久必分...!!";

    public Integer numberOfSentence(){
        //每次解析,都有很高的执行代价
        return content.split("[.!?]").length;
    }
    public Integer numberOfVerb() throws InterruptedException {
        //假设执行了很多逻辑;
        Thread.sleep(1000);
        return 80;
    }
    public Integer numberOfAdverb() throws InterruptedException {
        //假设执行了很多逻辑;
        Thread.sleep(1000);
        return 220;
    }
}

public class AppTest {
    public static void main(String[] args) throws InterruptedException {
        BookParser bp = new BookParser();
        Integer a = bp.numberOfAdverb();
        System.out.println("有"+ a + "个副词");
        Integer a2 = bp.numberOfAdverb();
        System.out.println("有"+ a2 + "个副词");
        Integer a3 = bp.numberOfAdverb();
        System.out.println("有"+ a3 + "个副词");
    }
    /**
     * //每隔一秒出现一个结果
     * 有220个副词
     * 有220个副词
     * 有220个副词
     *
     * Process finished with exit code 0
     */

}

现在有个问题,每解析一次就要花费1s, 这是极其不合理的;

我们可以做一个代理,每次调方法时进入代理,代理判断一下这个数字有没有统计过,如果统计过了,直接返回这个值,就不用去调用真实对象,如果没有统计过,就去调真实对象,然后返回值,并将这个值存到缓存中;

//图书解析器
class BookParser{
    //接收一本书的内容,字符串的值,是很大的
    private String content = "天下大事,分久必合,合久必分...!!";

    public Integer numberOfSentence(){
        //每次解析,都有很高的执行代价
        return content.split("[.!?]").length;
    }
    public Integer numberOfVerb() throws InterruptedException {
        //假设执行了很多逻辑;
        Thread.sleep(1000);
        return 80;
    }
    public Integer numberOfAdverb() throws InterruptedException {
        //假设执行了很多逻辑;
        Thread.sleep(1000);
        return 220;
    }
}
class BookParserProxy extends BookParser{
    //因为没有定义接口,所以为了与真实对象有相同的方法,继承一下BookParser
    private Integer numberOfSentence;
    private Integer numberOfVerb;
    private Integer numberOfAdverb;

    @Override
    public Integer numberOfSentence() {
        if(numberOfSentence == null){
            numberOfSentence = super.numberOfSentence();
        }
        return numberOfSentence;
    }

    @Override
    public Integer numberOfVerb() throws InterruptedException {
        if(numberOfVerb == null){
            numberOfVerb = super.numberOfVerb();
        }
        return numberOfVerb;
    }

    @Override
    public Integer numberOfAdverb() throws InterruptedException {
        if(numberOfAdverb == null){
            numberOfAdverb = super.numberOfAdverb();
        }
        return numberOfAdverb;
    }
}
public class AppTest {
    public static void main(String[] args) throws InterruptedException {
        BookParser bp = new BookParserProxy();
        Integer a = bp.numberOfAdverb();
        System.out.println("有"+ a + "个副词");
        Integer a2 = bp.numberOfAdverb();
        System.out.println("有"+ a2 + "个副词");
    }
    /**
     * //等待一秒后两个结果同时出现,说明什么,说明第二次没有调用真实方法
     * 有220个副词
     * 有220个副词
     *
     * Process finished with exit code 0
     */

}

这个就叫做静态代理,自己手写的代码,写死的代理类

而动态代理是运行时动态的生成字节码

4、代理模式的关键点

  1. 代理对象,一定与目标对象有相同的接口。这样才能做代理
  2. 代理对象中,一定有目标对象
  3. 代理对象,具有对目标对象的访问权限

其中上面的栗子中充分体现了前两点;第三点也可以体现,只需要改一下前置通知的返回值为boolean,然后做个判断就好啦

类似于现在要做一个权限验证的业务逻辑,再前置通知里面判断,返回true,才能调目标对象。

5、代理模式和适配器模式的比较

  1. 代理模式中,代理对象和它所包裹的目标对象,必须实现相同的接口;适配器模式种 ,适配器和它所包裹的对象不用实现相同的接口
  2. 代理模式中,代理对象可以控制它所包裹的目标对象的方法是否执行;适配器模式中,适配器总是调用目标对象的方法,无法控制

6、代理模式UML图

在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。