代理模式之通俗易懂

1、是什么

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
办事要求人,所以找代理。
结构型设计模式
在这里插入图片描述

2、干嘛用

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

远程代理
虚拟代理
安全代理
智能代理
(1) 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
(2) 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
(3) 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(4) 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

spring框架中的aop技术

3、怎么用

案例:
疫情期间,公司非法裁员,员工找律师代理跟公司打官司。

1、静态代理

程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
组成
一个委托类和代理类之间的约束接口,一个代理类,一个委托类,一个工厂类

代码实现:

首先得有一个接口,通用的接口是代理模式实现的基础,这个接口有打官司的功能

public interface Lawsuit {
    void dolawsuit();
}

//被代理类

public class Mylawsuit implements Lawsuit {
    @Override
    public void dolawsuit() {
        System.out.println("跟公司打官司");
    }
}

//代理类

public class LawsuitBylawyer implements Lawsuit {
    private Lawsuit lawsuit;

    public LawsuitBylawyer(Lawsuit lawsuit) {
        this.lawsuit = lawsuit;
    }

    @Override
    public void dolawsuit() {
        System.out.println("代理全权代理");
        lawsuit.dolawsuit();
        System.out.println("打得李晶丁卓满地找牙");
    }
}

//代理工厂

public class SubjectStaticFactory {
    public static Lawsuit getInstance() {
        return new LawsuitBylawyer(new Mylawsuit());
    }
}

//测试

public class Test {
    public static void main(String[] args) {
        Lawsuit instance = SubjectStaticFactory.getInstance();
        instance.dolawsuit();
    }
}

在这里插入图片描述

2、动态代理

基于JDK技术 动态代理类技术核心 Proxy类和一个 InvocationHandler 接口
基于CGlib 动态代理模式 基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术
基于 Aspectj 实现动态代理(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )
基于 instrumentation 实现动态代理(修改目标类的字节码、类装载的时候动态拦截去修改,基于javaagent) -javaagent:spring-instrument-4.3.8.RELEASE.jar (类装载的时候 插入动态代理的字节码,不会生成全新的Class )

(1)、基于JDK技术 动态代理

XXXInvocationHandler 类 实现InvocationHandler接口,这个类中持有一个被代理对象(委托类)的实例target。
该类别JDK Proxy类回调InvocationHandler 接口中有一个invoke方法,当一个代理实例的方法被调用时,代理方法将被编码并分发到 InvocationHandler接口的invoke方法执行。

invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

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

return result;
}

invoke参数解释
proxy 代表动态生成的 动态代理 对象实例
method 代表被调用委托类的接口方法,和生成的代理类实例调用的接口方法是一致的,它对应的Method 实例
args 代表调用接口方法对应的Object参数数组,如果接口是无参,则为null; 对于原始数据类型返回的他的包装类型。

代码实现

//通用的接口

public interface Lawsuit {
    void dolawsuit();
}

//被代理类

public class Mylawsuit implements Lawsuit {
    @Override
    public void dolawsuit() {
        System.out.println("跟公司打官司");
    }
}

//动态代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 全能代理类
 */
public class ProxyHandler implements InvocationHandler {

    private Object target;

    public ProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 在转调具体目标对象之前,可以执行一些功能处理
         */
        System.out.println("被动态代理类回调执行, 代理类 proxyClass =" + proxy.getClass()
                + " 方法名: " + method.getName() + "方法. 方法返回类型:" + method.getReturnType()
                + " 接口方法入参数组: " + (args == null ? "null" : Arrays.toString(args)));
        System.out.println("代理全权代理");
        Object invoke = method.invoke(target, args);
        System.out.println("打得李晶满地找牙");
        return invoke;
    }
}

//动态代理调用

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        System.out.println("-------------------第一种创建代理类方法--------------");
        //创建一个实例对象,这个对象是被代理的对象,委托类
        Mylawsuit mylawsuit = new Mylawsuit();
        //创建一个与代理类相关联的InvocationHandler,每一个代理类都有一个关联的 InvocationHandler,并将代理类引用传递进去
        InvocationHandler handler = new ProxyHandler(mylawsuit);
        Lawsuit lawsuitProxy = (Lawsuit) Proxy.newProxyInstance(Lawsuit.class.getClassLoader(), new Class<?>[]{Lawsuit.class}, handler);
        lawsuitProxy.dolawsuit();

        System.out.println("-------------------第二种创建代理类方法--------------");        /**
         *  动态代理对象步骤
         *      1、 创建一个与代理对象相关联的 InvocationHandler,以及真实的委托类实例
         *      2、Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass,该类继承Proxy类,实现 Person.java接口;JDK动态代理的特点是代理类必须继承Proxy类
         *      3、通过代理类 proxyClass 获得他的带InvocationHandler 接口的构造函数 ProxyConstructor
         *      4、通过 构造函数实例 ProxyConstructor 实例化一个代理对象,并将  InvocationHandler 接口实例传递给代理类。
         */

        // 1、创建 InvocationHandler 实例并设置代理的目标类对象
        Mylawsuit mylawsuit2 = new Mylawsuit();
        InvocationHandler Handlertwo = new ProxyHandler(mylawsuit2);
        // 2 创建代理类,是一个字节码文件, 把 proxyClass 保存起来就能看到 他继承Proxy 类,实现Person接口
        Class<?> proxyClass = Proxy.getProxyClass(Lawsuit.class.getClassLoader(), new Class<?>[]{Lawsuit.class});
        /** 代理类信息 */
        System.out.println("package2 = " + proxyClass.getPackage() + " SimpleName = " + proxyClass.getSimpleName()
                + " name =" + proxyClass.getName() + " CanonicalName = " + "" + proxyClass.getCanonicalName()
                + " 实现的接口 Interfaces = " + Arrays.toString(proxyClass.getInterfaces())
                + " superClass = " + proxyClass.getSuperclass() + " methods =" + Arrays.toString(proxyClass.getMethods()));

        // 3、  通过 proxyClass 获得 一个带有InvocationHandler参数的构造器constructor
        Constructor<?> ProxyConstructor = proxyClass.getConstructor(InvocationHandler.class);

        // 4、通过构造器创建一个  动态代理类 实例
        Lawsuit stuProxy = (Lawsuit) ProxyConstructor.newInstance(Handlertwo);
        /** 检测生成的类是否是代理类 */
        System.out.println("stuProxy isProxy " + Proxy.isProxyClass(stuProxy.getClass()));
        /** 获取 代理类关联的 InvocationHandler 是哪个*/
        InvocationHandler handlerObject = Proxy.getInvocationHandler(stuProxy);
        System.out.println(handlerObject.getClass().getName());
        stuProxy.dolawsuit();
    }
}

(2)、基于CGlib 技术动态代理代理类实现 (基于继承)

Cglib是针对类来实现代理的,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。

但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。

导入maven 依赖

<!-- cglib 动态代理依赖 begin -->
 <dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.2.5</version>
   </dependency>
   <!-- cglib 动态代理依赖 stop -->

//被代理类,普通类

public class Mylawsuit {
    public void dolawsuit() {
        System.out.println("跟公司打官司");
    }
}

方法拦截器 实现 MethodInterceptor 接口

一个切面,用于在方法拦截器中intercept 方法中调用真正业务方法之前 之后处理逻辑

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibMethodInterceptor implements MethodInterceptor {
    /**
     * 用于生成 Cglib 动态代理类工具方法
     *
     * @param target 代表需要 被代理的 委托类的 Class 对象
     * @return
     */
    public Object proxyInterceptor(Class target) {
        /** 创建cglib 代理类 start */
        // 创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        // 为代理类指定需要代理的类,也即是父类
        enhancer.setSuperclass(target);
        // 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截
        enhancer.setCallback(this);
        // 获取动态代理类对象并返回
        return enhancer.create();
        /** 创建cglib 代理类 end */
    }

    /**
     * 功能主要是在调用业务类方法之前 之后添加统计时间的方法逻辑.
     * intercept 因为  具有 MethodProxy proxy 参数的原因 不再需要代理类的引用对象了,直接通过proxy 对象访问被代理对象的方法(这种方式更快)。
     * 当然 也可以通过反射机制,通过 method 引用实例    Object result = method.invoke(target, args); 形式反射调用被代理类方法,
     * target 实例代表被代理类对象引用, 初始化 CglibMethodInterceptor 时候被赋值 。但是Cglib不推荐使用这种方式
     *
     * @param obj    代表Cglib 生成的动态代理类 对象本身
     * @param method 代理类中被拦截的接口方法 Method 实例
     * @param args   接口方法参数
     * @param proxy  用于调用父类真正的业务类方法。可以直接调用被代理类接口方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("代理全权代理");
        Object invokeSuper = proxy.invokeSuper(obj, args);
        System.out.println("打得李晶满地找牙");
        return invokeSuper;
    }
}

Cglib测试类

public class CglibTest {
    public static void main(String[] args) {
        CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
        Mylawsuit o = (Mylawsuit) cglibMethodInterceptor.proxyInterceptor(Mylawsuit.class);
        o.dolawsuit();
    }
}

Cglib 总结

CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类.
由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的
做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用.
提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活。
CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值