静态代理&动态代理,由浅入深

引入:
spring的aop(面向切面编程)离不开动态代理技术,这里就详细的讲一下动态代理技术,为了更加理解aop思想

任意门:博客 spring aop (待更新。。。。)

一、静态代理

给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用

1.1 引入代理模式

所有interface类型的变量总是通过向上转型并指向某个实例的

常规
业务:租房
场景:第一步:找房子;第二步:租房子,入住;第三步:付钱给房东

  1. 定义一个接口
public interface Rent{
	//1.第1步:找房子
	public void lookforHouse();
	//1.第2步:找房子
	public void  rentHouse();
	//1.第3步:付钱给房东
	public void payforMaster();
}
  1. 编写接口的实现类
public class Customer implements Rent {
    @Override
    public void lookforHouse() {
        System.out.println("找房子");
    }

    @Override
    public void rentHouse() {
        System.out.println("租房子并且入住房子");
    }

    @Override
    public void payforMaster() {
        System.out.println("付钱给房东");
    }
}

  1. 接口引用指向实例对象
 Rent rent = new RentImpl();
        rent.lookforHouse();
        rent.rentHouse();
        rent.payforMaster();

这种方式就是我们通常编写代码的方式。

静态代理模式
业务:租房
场景:换不同的人来租房,如果是上面那种普通模式,那么每来一个人租房,都要重复写找房lookforHouse(),租房入住rentHouse(),付钱payforMaster()的步骤,,造成代码的冗余,我们会发现,其实找房和付钱这两个过程不管是谁来,步骤都是一样,只有租房入住和当前对象息息相关

为了解决上述问题,我们可以用到静态代理模式

1.2 静态代理模式

多了一个代理对象,一些相同的操作都交给代理对象完成,真实对象只要专注自己的业务就好

1.3 uml图

在这里插入图片描述

1.4 案例&代码

接口:
真实对象和代理对象都要实现这个接口

public interface Rent {
    /**
     * 租房
     */
    public void  rentHouse();

}

真实对象:
专注自己的业务需求,其它的事情交给代理对象做就好

public class Customer implements Rent {
    @Override
    public void rentHouse() {
        System.out.println("客户租房入住");
    }
}

代理对象:
帮助真实对象完成一些附属操作

public class Zhongjie implements Rent {
    //包含真实角色的引用
    private Customer customer;
    Zhongjie(Customer customer){
        this.customer = customer;
    }
    @Override
    public void rentHouse() {
        lookforHouse();
        customer.rentHouse();
        payforMaster();
    }

    public void lookforHouse() {
        System.out.println("找1500元/月房子");
    }

    public void payforMaster() {
        System.out.println("收取客户2000,付1500给房东");
    }
}

测试:

 public static void main(String[] args) {
        Zhongjie zhongjie = new Zhongjie(new Customer());
        zhongjie.rentHouse();
    }

结果:
在这里插入图片描述

1.5 静态代理总结

  • 可以使真实的角色的操作更加纯粹!不用去关注一些公共的业务!
  • 公共业务就交给代理角色!实现业务的分工
  • 公共业务发生扩展的时候,方便集中管理。

二、动态代理

建议先看完静态代理,通过案例循序渐进。

看完了静态代理,现在来说动态代理
动态代理模式
业务:租房
场景:现在是rentHouse() 即真实客户的需求写死了,如果rentHouse()的业务发生改变,那么我就要重新生成一个代理对象,如果业务有很多种类,那么我要写很多不同的代理对象来满足不同的业务。

为了解决这个问题,这个时候就要引入动态代理模式了。

2.1 使用 JDK 官方的 Proxy 类创建代理对象

  • 要想创建一个代理对象, 需要使用 Proxy 类的 newProxylnstance 方法。 这个方法有三个参数:
  1. 一个类加载器(class loader) 。作为 Java 安全模型的一部分, 对于系统类和从因特网上下载下来的类,可以使用不同的类加载器。用 null 表示使用默认的类加载器。
  2. 一个Class 对象数组, 每个元素都是需要实现的接口。
  3. 一个调用处理器 handler(如何实现代理)。
  • 要求:被代理的对象至少实现一个接口

参数三:handler
动态的传入 method 解决变化问题,要继承IncovationHandler类

class TraceHandler implements IncovationHandler{
	//这个引用可要可不要,具体看你怎么写,我这里是为了实现后面的invoke方法
    private Object target;
    public TraceHandler(Object t){
        target = t;
    }
    /**
* 执行被代理对象的任何方法,都会经过该方法。
* 此方法有拦截的功能。
* 
* 参数:
* proxy:代理对象的引用。不一定每次都用得到
* method:当前执行的方法对象
* args:执行方法所需的参数
* 返回值:
* 当前执行方法的返回值
*/
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
       // do something before
        return m.invoke(target, args);
        // do something after
    }
}

Proxy创建代理对象

//参数1
Object value = ...;
//参数2
InvocationHandler handler = new TraceHandler(value);
//参数3
Class[] interfaces = new Class[]{Comparable.class};
//jdk的Proxy类的newProxyInstance方法
Object proxy = Proxy.newProxyInstance(null,interfaces,handler);

关于invoke方法,这就涉及到了反射知识点
任意门👉 Java反射的使用,三种获取Class对象的案例比较

案例&代码

public class MyTest {
    public static void main(String[] args) {
        Customer customer = new Customer();
        //三个参数,最后一个参数采用匿名内部类的形式
        Rent proxy = (Rent) Proxy.newProxyInstance(customer.getClass().getClassLoader(), customer.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("找房子");
                Object o = method.invoke(customer, args);
                System.out.println("付钱给房东");
                return o;
            }
        });
        proxy.rentHouse();
    }
}

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

由于JDK只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以cgLib横空出世!CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。

前提:

CGLIB是一个强大的高性能的代码生成包,在运行时动态生成字节码并生成新类,想要用它,就要实现MethodInterceptor接口,这个接口在cglib.proxy下

在这里插入图片描述


基于子类的动态代理

需要用到CGLIB的Enhancercreate()方法

要求:被代理对象不能是最终类(被final修饰)

set参数之一:MethodInterceptor的实现类(如何代理)

class interceptor implements MethodInterceptor{
    private Object target;
    public TraceHandler(Object t){
        target = t;
    }
   /**
* 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
方法进行增强。
* 
* 参数:
* 前三个和基于接口的动态代理是一样的。
* MethodProxy:当前执行方法的代理对象。
* 返回值:
* 当前执行方法的返回值
*/
   @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       // do something before
        Object result = method.invoke(target, args);
        // do something after
        return result;
    }
}

如何生成代理类

Enhancer enhancer = new Enhancer();
//传入真实对象
enhancer.setSuperclass(XXXXX.class);
//传入实现了MethodInterceptor的类
enhancer.setCallback(new intercept());
//生成代理对象
Object proxy= enhancer.create();

通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法。

案例&代码

真实对象没有实现任何接口

public class Cusomer2 {
    public void rentHouse() {
        System.out.println("客户租房入住");
    }
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyTest2 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Cusomer2.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("找房子");
                Object o = methodProxy.invokeSuper(object, args);
                System.out.println("付钱给房东");
                return o;
            }
        });
        Cusomer2 cusomer2 = (Cusomer2) enhancer.create();
        cusomer2.rentHouse();

    }
}

Cglib源码分析 invoke和invokeSuper的差别

建议使用 invokeSuper()方法
任意门👉 Cglib源码分析 invoke和invokeSuper的差别

2.3 JDK动态代理 VS CGLIB 对比

  • 字节码创建方式:JDK动态代理通过JVM实现代理类字节码的创建,cglib通过ASM创建字节码。
  • JDK动态代理强制要求目标类必须实现了某一接口,否则无法进行代理。而CGLIB则要求目标类和目标方法不能是final的,因为CGLIB通过继承的方式实现代理。
  • CGLib不能对声明为final的方法进行代理,因为是通过继承父类的方式实现,如果父类是final的,那么无法继承父类。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值