设计模式——代理模式

代理模式(对原有方法进行增强)

基本介绍

image-20220529231255716

类图

image-20220529231535833

通过调用代理对象来间接使用模板对象的方法,代理对象和目的对象需要实现相同的接口或者继承相同的父类

image-20220529232442013

代理对象会对原有方法进行补充

静态代理(手动编写代理类)

我们自己手动创建一个代理对象,和目标类实现相同的接口或者父类,然后对目标类的每一个方法进行改写

编写一个接口:

public interface ITeacher {
    void teach();
}

编写目标类:

public class Teacher implements ITeacher{
    @Override
    public void teach() {
        System.out.println("授课");
    }
}

编写代理类:

public class TeacherProxy implements ITeacher{
    ITeacher teacher;
    TeacherProxy(ITeacher teacher){
        this.teacher=teacher;
    }

    @Override
    public void teach() {
        System.out.println("开始代理");
        teacher.teach();
        System.out.println("代理结束");
    }
}

客户端调用:

public class Client {
    public static void main(String[] args) {
        ITeacher teacher=new TeacherProxy(new Teacher());
        teacher.teach();
    }
}

客户端调用时,就感觉是在调用目标对象一样

静态代理的优点是灵活,缺点是如果目标类的方法很多,我们要为每个方法都编写代理方法,代码量很庞大,并且当目标类增加方法时,目标类和代理类都需要修改,代理类和目标类的耦合度很大,为了解决这个问题我们可以使用动态代理

JDK动态代理

image-20220529235213192

目标对象需要实现一个接口,而代理对象由JDK帮我们生成不需要我们来实现接口

类图:

image-20220530000119217

JVM利用反射机制生成代理对象,我们通过调用代理对象来间接调用目标对象的方法

编写一个代理工厂来为实现了接口的方法生成代理对象(包括已经被代理的对象)

实现:

和静态代理一样需要实现一个接口,对接口包含的所有方法进行代理(包括toString等Object里的方法)

代理对象工厂:

public class ProxyFactory {
    Object getProxyInstance(Object target){
        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 result = method.invoke(target, args);
                        System.out.println("代理结束");
                        return result;
                    }
                }
        );
    }
}

对所有传入的对象进行动态代理,实现原理是调用Proxy.newProxyInstance方法,这个方法需要三个参数:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

ClassLoader loader:目标对象的类加载器,直接使用target.getClass().getClassLoader()即可

Class<?>[] interfaces:目标对象实现的接口,直接使用target.getClass().getInterfaces()即可

InvocationHandler h:方法具体的增强逻辑,我们可以创建一个匿名内部类,也可以使用Lamda表达式

这样这个工厂可以为任何方法创建代理对象,代理的逻辑定义在内部类中

CGLIB动态代理

不需要实现接口,使用性能很高的ASM细节吗生成框架,常用于各种AOP框架

被代理的类不能是final的

使用的是拦截器机制

实现

创建目标类:

public class Teacher {
    void teach(){
        System.out.println("上课");
    }
}

创建代理工厂:

public class ProxyFactory implements MethodInterceptor {

    private static final ProxyFactory proxyFactory=new ProxyFactory();
    private final ThreadLocal<Object> targetMap=new ThreadLocal<>();

    private ProxyFactory(){}

    public static ProxyFactory getProxyFactory(){
        return proxyFactory;
    }
    public Object getProxyInstance(Object target){
        targetMap.set(target);
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLIB 动态代理");
        System.out.println(o.getClass());
        Object result = methodProxy.invoke(targetMap.get(),objects);
        methodProxy.invokeSuper(o,objects);
        method.invoke(targetMap.get(),objects);
        targetMap.remove();
        System.out.println("代理结束");
        return result;
    }
}

调用目标方法有三种:

methodProxy.invoke(targetMap.get(),objects);

methodProxy.invokeSuper(o,objects);

method.invoke(targetMap.get(),objects);

targetMap.get()得到的是被代理的原对象,而参数中的o是提前暴露的代理对象,一定不要视图直接使用o,否则会爆栈

method是原原方法,methodProxy是代理方法

创建客户端测试:

public class Client {
    private static final ProxyFactory proxyFactory=ProxyFactory.getProxyFactory();
    public static void main(String[] args) {
        Teacher teacher = (Teacher) proxyFactory.getProxyInstance(new Teacher());
        teacher.teach();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值