代理模式

静态代理

代理模式的作用:

为其他对象提供一种代理以控制对这个对象的访问。

涉及的角色:

  • 抽象角色:声明真实对象和代理对象的共同接口。

  • 代理角色:

    • 代理对象角色内部含有对真实对象的引用,从而可以操作真实对象;
    • 代理对象提供与真实对象相同的接口以便在任何时候都能代替真实对象;
    • 代理对象可以在执行真实对象操作是,附加其他的操作,相对于对真实对象进行封装。
  • 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

优点

可以做到在不修改目标对象的功能前提下,对目标功能扩展。

缺点

  1. 每一个代理类都必须实现一遍委托类(realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改.
  2. 代理类每一个接口对象对应一个委托对象。如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。

JDK 动态代理

动态代理解决静态代理中代理类接口过多的问题。通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。

步骤:

  1. 编写一个委托类的接口,即静态代理的(Subject接口)。
  2. 实现一个真正的委托类,即静态代理的(RealSubject类)。
  3. 创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法。
  4. 在测试类中生成动态代理对象。

第一二步骤和静态代理一样,第三步:

public class DynamicProxy implements InvocationHandler {
    private Object object;
    public DynamicProxy(Object object) {
        this.object = object;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] ar gs) throws Throwable {
        Object result = method.invoke(object, args);
        return result;
    }
}

创建动态代理的对象:

Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new Class[]{Subject.class}, proxy);
subject.visit(); 

上述代码的关键是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)⽅法,该⽅法会根据指定的参数动态创建代理对象。

三个参数的意义如下:

  1. loader,指定代理对象的类加载器。
  2. interfaces,代理对象需要实现的接⼝,可以同时指定多个接⼝。
  3. handler,⽅法调⽤的实际处理者,代理对象的⽅法调⽤都会转发到这⾥。

CGLIB 动态代理

  1. ⾸先实现⼀个MethodInterceptor,⽅法调⽤会被转发到该类的intercept() ⽅法。
  2. 然后在需要使⽤HelloConcrete的时候,通过CGLIB动态代理获取代理对象。

通过CGLIB的Enhancer来指定要代理的⽬标对象、实际处理代理逻辑的对象,最终通过调⽤create()⽅法得到代理对象。

这个对象所有⾮final⽅法的调⽤都会转发给MethodInterceptor.intercept()⽅法,在 intercept()⽅法⾥我们可以加⼊任何逻辑,⽐如修改⽅法参数,加⼊⽇志功能、安全检查功能等;

通过调⽤MethodProxy.invokeSuper()⽅法,我们将调⽤转发给原始对象。

对于从Object中继承的⽅法,CGLIB代理也会进⾏代理,如hashCode()equals()toString()等,但是 getClass()wait()等⽅法不会,因为它是final⽅法,CGLIB⽆法代理。

原理:

CGLIB是⼀个强⼤的⾼性能的代码⽣成包,底层是通过使⽤⼀个⼩⽽快的字节码处理框架ASM,它可以在运⾏期扩展Java类与实现Java接⼝

Enhancer是CGLIB的字节码增强器,可以很⽅便的对类进⾏拓展 创建

代理对象的⼏个步骤:

1、⽣成代理类的⼆进制字节码⽂件

2、加载⼆进制字节码,⽣成Class对象( 例如使⽤Class.forName()⽅法 )

3、通过反射机制获得实例构造,并创建代理类对象

总结:

  1. jdk动态代理:利⽤拦截器(拦截器必须实现InvocationHanlder)加上反射机制⽣成⼀个实现代理接⼝的匿名类,在调⽤具体⽅法前调⽤InvokeHandler来处理。只能对实现了接⼝的类⽣成代理只能对实现了接⼝的类⽣成代理。

  2. cglib动态代理:利⽤ASM开源包,让代理对象类的class⽂件加载进来,通过修改其字节码⽣成⼦类来处理。

    主要是对指定的类⽣成⼀个⼦类,覆盖其中的⽅法,并覆盖其中⽅法实现增强,但是因为采⽤的是继承, 对于final类或⽅法,是⽆法继承的。

  3. 选择

    a. 如果⽬标对象实现了接⼝,默认情况下会采⽤JDK的动态代理实现AOP

    b. 如果⽬标对象实现了接⼝,也可以强制使⽤CGLIB实现AOP

    c. 如果⽬标对象没有实现了接⼝,必须采⽤CGLIB库。Spring会⾃动在JDK动态代理和CGLIB之间转
    换。

编写不易,转载注明出处:https://www.cnblogs.com/lmh15054109/p/14411727.html

posted @ 2021-02-18 14:39  Spear_J  阅读(64)  评论(0编辑  收藏  举报