设计模式-代理模式

介绍

代理模式分为:静态代理、动态代理。
代理模式是给某一个对象提供一个代理对象,代理对象拥有原对象的引用,代理对象替原对象去执行这个行为。通俗的来讲代理模式就是我们生活中常见的中介。

1 静态代理模式

1.1 UML类图

在这里插入图片描述

1.2 示例

代码示例:代理点coffee

  1. 创建服务类接口
public interface Coffee {

    void orderCoffee();
}
  1. 瑞幸咖啡
public class LuckinCoffee implements Coffee {
    @Override
    public void orderCoffee() {
        System.out.print(" 瑞幸coffee");
    }
}
  1. 星巴克
public class StarbucksCoffee implements Coffee {

    @Override
    public void orderCoffee() {
        System.out.print(" 星巴克coffee");
    }
}
  1. 代理对象
public class CoffeeProxy implements Coffee {

    public Coffee coffee;

    public CoffeeProxy(Coffee coffee) {
        this.coffee = coffee;
    }

    public CoffeeProxy() {
        this.coffee = new StarbucksCoffee();
    }

    @Override
    public void orderCoffee() {
        System.out.print("加糖");
        this.coffee.orderCoffee();
        System.out.println("");
    }
}
  1. client
public class Client {

    public static void main(String[] args) {
        CoffeeProxy coffeeProxy = new CoffeeProxy();
        coffeeProxy.orderCoffee();

        CoffeeProxy coffeeProxy1 = new CoffeeProxy(new LuckinCoffee());
        coffeeProxy1.orderCoffee();

    }
}

输出

加糖 星巴克coffee
加糖 瑞幸coffee

2 动态代理模式

动态代理实现方式:JDK动态代理、GDLib动态代理。

2.1 JDK动态代理

JDK动态代理,代理对象要实现接口InvocationHandler

2.1.1 UML类图

在这里插入图片描述

2.1.2 代码
  1. 动态代理对象 class JdkProxy
public class JdkProxy implements InvocationHandler {

    // 目标对象引用
    private Object target;

    // 通过构造方法将目标对象注入到当前代理对象中
    public JdkProxy(Object target) {
        super();
        this.target = target;
    }

    /**
     * JDK动态生成目标对象target的一个代理类
     *
     * @return
     */
    public Object getProxy() {

        // 如何生成一个代理类呢?
        // 1、编写源文件(java文件)----目录类接口interface实现类(调用了目标对象的方法)
        // 2、编译源文件为class文件
        // 3、将class文件加载到JVM中(ClassLoader)
        // 4、将class文件对应的对象进行实例化(反射)

        /**
         * Proxy是jdk中的api
         * 参数1:目标对象的类加载器
         * 参数2:目标对象的接口
         * 参数3:代理对象执行器处理器,当前对象invoke方法代理执行,一般传入当前对象this
         */
        Object proxyObject = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return proxyObject;
    }

    /**
     * 代理对象会执行的方法
     * @param proxy  代理对象
     * @param method 代理执行的方法
     * @param args   参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("这是jdk的代理方法");
        // 下面的代码,是反射中的API用法
        // 该行代码,实际调用的是[目标对象]
        Object invoke = method.invoke(this.target, args);
        return invoke;
    }


}
  1. Client
public class Client {

    public static void main(String[] args) {
        // 1、创建目标对象
        Coffee service = new LuckinCoffee();
        // 2、生成代理对象
        JdkProxy proxyFactory = new JdkProxy(service);
        // 得到代理对象
        Coffee proxy = (Coffee) proxyFactory.getProxy()
        // 3、调用目标对象的方法
        service.orderCoffee();
        System.out.println("===============");
        // 4、调用代理对象的方法
        proxy.orderCoffee();
    }
 }

输出

 瑞幸coffee
===============
这是jdk的代理方法
 瑞幸coffee

2.2 GCLib动态代理

GCLib动态代理要使用到两个jar包

  • cglib-2.2.2.jar
  • asm-3.3.1.jar

下载链接地址 提取码:b5ad

2.2.1 UML类图

在这里插入图片描述

2.2.2 代码
  1. CGLib动态代理技术实现 它是基于继承的方式实现 CGLibProxy
public class CgLibProxy implements MethodInterceptor {

	/**
	 * @param clazz
	 * @return
	 */
	public Object getProxyByCgLib(Class clazz) {
		// 创建增强器
		Enhancer enhancer = new Enhancer();
		// 设置需要增强的类的类对象
		enhancer.setSuperclass(clazz);
		// 设置回调函数
		enhancer.setCallback(this);
		// 获取增强之后的代理对象
		return enhancer.create();
	}

	/***
	 * Object proxy:这是代理对象,也就是[目标对象]的子类 
	 * Method method:[目标对象]的方法 
	 * Object[] arg:参数
	 * MethodProxy methodProxy:代理对象的方法
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
		// 因为代理对象是目标对象的子类
		// 该行代码,实际调用的是父类目标对象的方法
		System.out.println("这是cglib的代理方法");

		// 通过调用子类[代理类]的invokeSuper方法,去实际调用[目标对象]的方法
		Object returnValue = methodProxy.invokeSuper(proxy, arg);
		// 代理对象调用代理对象的invokeSuper方法,而invokeSuper方法会去调用目标类的invoke方法完成目标对象的调用
		
		return returnValue;
	}
}
  1. Client
public class Client {

    public static void main(String[] args) {
        Coffee coffee = new LuckinCoffee();

        // 生成代理对象
        CgLibProxy cgLibProxy = new CgLibProxy();
        Coffee coffeProxy = (Coffee) cgLibProxy.getProxyByCgLib(coffee.getClass());

        // 调用目标对象的方法
        coffee.orderCoffee();
        System.out.println("===============");
        // 调用代理对象的方法
        coffeProxy.orderCoffee();
    }
}

输出

 瑞幸coffee
===============
这是cglib的代理方法
 瑞幸coffee

3 总结

  1. 静态代理模式与装饰模式在代码结构上很类似。在使用上如何去区别呢?装饰模式主要是对对象装饰,代理模式而是行为上的代理执行。比如对一个销售产品的包装行为,使用装饰模式;对产品销售的行为,使用代理模式。
  2. 动态代理,JDK动态代理与GCLib动态代理主要的区别是实现方式,对于业务流程的使用上没有差异。动态代理proxy在执行方法,会回调方法invoke(Object proxy, Method method, Object[] args)或intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy),在该方法中新增增强行为的代码,通过反射的方式执行原对象的行为。

更多推荐