设计模式之代理模式

  1. 为什么要代理?

    • 解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  2. 什么是代理模式?

    • 代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
  3. 代理模式分为静态代理和动态代理

    • 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
    • 动态:在程序运行时,运用反射机制动态创建而成。
  4. 静态代理

    • 静态代理是定义父类或者接口,然后被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。代理对象与目标对象实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

      • 优点:可不修改目标对象的功能,通过代理对象对目标功能扩展。
      • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。
    • 举例

    • ITeacherDao:接口

    • TeacherDao:目标对象,实现接口ITeacherDao

    • TeacherDAOProxy:代理对象,也实现ITeacherDao接口,并且聚合ITeacherDao属性,通过构造器传参设置值,调用的时候通过调用代理对象的方法来调用目标对象。

  5. 动态代理

    两种创建动态代理的方法:JDK动态代理:Proxy.newProxyInstance(三个参数);CGLib动态代理:Enhancer.create(两个参数);

    1)jdk动态代理: 使用java反射包中的类和接口实现动态代理的功能。

    • 动态代理也叫JDK代理、接口代理。它使代理对象不需要实现接口(但目标对象要实现接口),代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

    • 即使用JDK包java.lang.reflect.Proxy中的newProxyInstance方法来动态的创建目标对象(被代理对象),该方法需要如下接收三个参数:

      • ClassLoader loader:指定当前目标对象使用的类加载器
      • Class<?>[] interfaces :目标对象实现的接口类型,使用泛型方法确认类型
      • InvocationHandler h :事情处理,执行目标对象的方法时,会触发事情处理器方法,把当前执行的目标对象方法作为参数传入

    • 核心是getProxyInstacne()

      • 根据传入的对象TeacherDao目标对象
      • 利用反射机制,返回一个代理对象
      • 然后通过代理对象,调用目标对象方法
    //接口
    public interface ITeacherDao {
    	void teach();
    	void test(String name);
    }
    //目标对象
    public class TeacherDao implements ITeacherDao {
    	@Override
    	public void teach() {
    		System.out.println("一键三连");
    	}
    	@Override
    	public void test(String name) {
    		System.out.println("传参测试:" + name);
    	}
    }
    //代理对象
    public class ProxyFactory {
    	//维护一个目标对象 , Object
    	private Object target;
    	//构造器 , 对target 进行初始化
    	public ProxyFactory(Object target) {
    		this.target = target;
    	}
    	//动态生成一个代理对象
    	public Object getProxyInstance() {
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    				target.getClass().getInterfaces(),
    				new InvocationHandler() { //匿名类重写invoke方法
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						System.out.println("动态代理开始");
    						Object returnVal = method.invoke(target, args);//反射机制调用目标对象的方法
    						System.out.println("动态代理结束");
    						return returnVal;
    					}
    				});
    	}
    }
    //测试
    public class Client {
    	public static void main(String[] args) {
    		//创建目标对象
    		ITeacherDao target = new TeacherDao();
    
    		//创建代理对象
    		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
    
    		//内存中动态生成了代理对象
    		System.out.println(proxyInstance.getClass());
    
    		//通过代理对象,调用目标对象的方法
    		proxyInstance.teach();
    		proxyInstance.test("一键三连");
    	}
    }
    

    2)cglib动态代理: cglib是第三方的工具库, 创建代理对象。

    cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。

    • CGLib 实现步骤

      • 创建一个实现接口 MethodInterceptor 的代理类,重写 intercept 方法;
      • 创建获取被代理类的方法 getInstance(Object target);
      • 获取代理类,通过代理调用方法。
    • CGLib 底层原理

    • 通过查看 Enhancer 类源码,最终也是生成动态代理类的字节码,动态代理类继承要被代理的类,然后实现其方法。

    • 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。

    • 两者区别

      • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
      • Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
      • JDK Proxy 是通过拦截器加反射的方式实现的;
      • JDK Proxy 只能代理实现接口的类;
      • JDK Proxy 实现和调用起来比较简单;
      • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
      • CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的。
posted @ 2024-05-13 14:24  Hanyta  阅读(9)  评论(0编辑  收藏  举报