大话--代理模式

代理模式

定义

代理模式就是给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗讲代理模式就是我们生活中常见的中介。

使用场景

远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中。
虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
防火墙(Firewall)代理:保护目标不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。

优点

在目标对象实现的基础上,可以增强额外的功能操作,即扩展目标对象的功能。
代理模式主要起增强方法和权限拦截的作用。

缺点

1.增加了代理对象可能导致请求的处理速度变慢。
2.实现代理模式需要额外工作,有些代理模式的实现非常复杂。

组成

代理模式通常分成静态代理和动态代理。动态代理又分为JDK代理和Cglib代理

tips

1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
静态代理需要自己手动编写代理类和目标方法。
动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!
Cglib代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。

关键代码

实现代理类与被代理类的组合

类图

普通代理模式

静态代理

动态代理

Cglib代理

具体实现

静态代理

静态代理在使用时,需要定义接口或者父类,目标对象与代理对象一起实现相同的接口或者是继承相同的父类。

静态代理的优缺点:
优点:在不修改目标对象功能的前提下,能通过代理对象对目标功能扩展。
确定:需要很多代理类;一旦接口增加,目标对象和代理对象都要维护。

1.定义一个接口ITeacherDao
2.目标对象TeacherDao实现接口ITeacherDAO
3.使用静态代理方式,需要代理对象TeacherDaoProxy也实现ITeacherDao
4.调用时,通过调用代理对象的方法来调用目标对象
5.代理对象和目标对象要实现相同的接口,并通过调用相同的方法来调用目标对象的方法
package Proxy.staticProxy;

public interface ITeacherDao {
    void teach();
}

package Proxy.staticProxy;

/**
 * @title: TeacherDao
 * @Author yzhengy
 * @Date: 2020/7/27 16:23
 * @Question:  目标对象
 */
public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println("老师正在授课......");
    }
}

package Proxy.staticProxy;

/**
 * @title: TeacherDaoProxy
 * @Author yzhengy
 * @Date: 2020/7/27 16:24
 * @Question: 代理对象
 */
public class TeacherDaoProxy implements ITeacherDao {

    private ITeacherDao target;//目标对象,通过接口来聚合

    //构造器
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("开始代理......操作过程进行中");
        target.teach();
        System.out.println("提交操作");
    }
}

package Proxy.staticProxy;

/**
 * @title: Client
 * @Author yzhengy
 * @Date: 2020/7/27 16:27
 * @Question: 测试类
 */
public class Client {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao teacherDao = new TeacherDao();

        //创建代理对象,同时将目标对象传递给代理对象
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

        //通过代理对象,调用目标对象的方法(执行的是代理对象的方法,代理对象再去调用目标对象的方法)
        teacherDaoProxy.teach();

    }

}

动态代理

1.代理对象不需要实现接口,但目标对象需要实现接口。
2.代理对象的生成是利用JDK的API,动态地在内存中构建代理对象。
3.代理类所在的包:java.lang.reflect.Proxy
4.JDK实现代理只需要使用newProxyInstance方法。static Object newProxyInstance(Classloader loader,Class<?>[]interfaces,InvocationHandler h)

代理类是由Proxy这个类通过newProxyInstance方法动态生成的,生成对象后使用“实例调用方法”的方式进行方法调用,那么代理类的被代理类的关系只有在执行这行代码的时候才会生成,因此成为动态代理。

package Proxy.dynamicProxy;

public interface ITeacherDao {

    void teacher();
    void sayHello(String name);

}

package Proxy.dynamicProxy;

/**
 * @title: TeacherDao
 * @Author yzhengy
 * @Date: 2020/7/27 17:04
 * @Question: 目标对象
 */
public class TeacherDao implements ITeacherDao {

    @Override
    public void teacher() {
        System.out.println("老师授课中......");
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello" + name);
    }
}

package Proxy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @title: ProxyFactory
 * @Author yzhengy
 * @Date: 2020/7/27 17:05
 * @Question: 代理对象工厂
 */
public class ProxyFactory {

    private Object target;//维护一个目标对象

    public ProxyFactory(Object target){//构造器,对target进行初始化
        this.target = target;
    }

    //给目标对象生成一个相应的代理对象
    public Object getProxyInstance(){
		/*说明
		 *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
		 */
		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("JDK动态代理开始......");
                Object returnVal = method.invoke(target,args);//利用反射机制调用目标对象的方法
                System.out.println("JDK动态代理提交......");
                return returnVal;
            }
        });

    }
}

package Proxy.dynamicProxy;

/**
 * @title: Client
 * @Author yzhengy
 * @Date: 2020/7/27 17:13
 * @Question: 测试类
 */
public class Client {

    public static void main(String[] args) {
        //创建目标对象
        ITeacherDao target = new TeacherDao();

        //给了目标对象,创建代理对象,需要转成ITeacherDao
        ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();

        //proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
        System.out.println("proxyInstance=" + proxyInstance.getClass());

        //通过代理对象,调用目标对象的方法
        proxyInstance.teacher();
        proxyInstance.sayHello("tina");
    }
}

Cglib代理

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
1.引用Cglib的jar包
2.在内存中动态地创建子类,注意代理的类不能为final,否则会出现java.lang.IllegalArgumentException错误。
3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

package Proxy.cglibProxy;

/**
 * @title: TeacherDao
 * @Author yzhengy
 * @Date: 2020/7/27 18:10
 * @Question: 目标对象
 */
public class TeacherDao {

    public String teach(){
        System.out.println("老师授课中  , 我是cglib代理,不需要实现接口");
        return "hello";
    }
}

package Proxy.cglibProxy;

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

import java.lang.reflect.Method;

/**
 * @title: ProxyFactory
 * @Author yzhengy
 * @Date: 2020/7/27 17:38
 * @Question: 代理对象
 */
public class ProxyFactory implements MethodInterceptor {

    //维护一个目标对象
    private Object target;

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

    //返回一个代理对象,是target目标对象的代理对象
    public Object getProxyInstance(){
        //1.创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(target.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类对象,即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理模式......开始");
        Object returnVal = method.invoke(target,args);
        System.out.println("cglib代理模式......结束");
        return returnVal;
    }

}

package Proxy.cglibProxy;

/**
 * @title: Client
 * @Author yzhengy
 * @Date: 2020/7/27 18:21
 * @Question: 测试类
 */
public class Client {

    public static void main(String[] args) {
        //.创建目标对象
        TeacherDao target = new TeacherDao();

        //.获取代理对象,并将目标对象传递给代理对象
        TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();

        //.执行代理对象的方法,触发intercept方法,实现对目标对象方法的调用
        String res = proxyInstance.teach();
        System.out.println("res=" + res);
    }

}

使用代理模式的源码

Spring AOP 编程的实现原理就是动态代理。使用的是JDK代理和cglib代理,比如Spring的事务使用的是AOP技术,当目标类没有实现接口时候,会使用cglib代理,实现了接口默认使用JDK代理。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

 @Override
 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
     Class<?> targetClass = config.getTargetClass();
     if (targetClass == null) {
       throw new AopConfigException("TargetSource cannot determine target class: " +
           "Either an interface or a target is required for proxy creation.");
     }
       // 判断目标类是否是接口或者目标类是否Proxy类型,若是则使用JDK动态代理
     if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
       return new JdkDynamicAopProxy(config);
     }
       // 配置了使用CGLIB进行动态代理或者目标类没有接口,那么使用CGLIB的方式创建代理对象
     return new ObjenesisCglibAopProxy(config);
   }
   else {
       // 上面的三个方法没有一个为true,那使用JDK的提供的代理方式生成代理对象
     return new JdkDynamicAopProxy(config);
   }
 }
   //其他方法略……
}

posted @ 2020-07-27 19:43  10000_Hours  阅读(315)  评论(0编辑  收藏  举报