代理模式

156 阅读3分钟

代理模式的定义

静态代理

实现方式

1.继承

子类通过继承父类,在子类重写父类方法时,手动调用父类的方法,并在子类中对其增强。

-- 例子

子类Car2对父类Car进行代理:

2.聚合

实现同一个接口,并持有被代理的对象。

-- 例子

Class CarProxy  implements  Moveable{

        private Car car;
        public CarProxy(Car car){
            this.car = car
        }
		
        @Override
        public void move(){
        	前增强;
            car.move();
            后增强;
        }
}

比较

聚合代理优于继承代理。因为实现功能叠加的情况下,聚合代理通过相互代理可以实现功能重用,而继承代理必须写多个类来实现多功能叠加。

-- 例子

  • 共同接口 Moveable
public interface Moveable {
	void move();
}
  • 被代理类 Car
public class Car implements Moveable {

	@Override
	public void move() {
		//实现开车
		try {
			Thread.sleep(new Random().nextInt(1000));
			System.out.println("汽车行驶中....");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
  • 代理类1 --日志代理类 CarLogProxy
public class CarLogProxy implements Moveable {

	public CarLogProxy(Moveable m) {   // 传入共同实现的接口对象
		super();
		this.m = m;
	}

	private Moveable m;
	
	@Override
	public void move() {
		System.out.println("日志开始....");
		m.move();
		System.out.println("日志结束....");
	}
}
  • 代理类2 --时间代理类 CarTimeProxy
public class CarTimeProxy implements Moveable {

	public CarTimeProxy(Moveable m) {  // 传入共同实现的接口对象
		super();
		this.m = m;
	}

	private Moveable m;
	
	@Override
	public void move() {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		m.move();
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
	}
}
  • 测试类 Client
public class Client {
	public static void main(String[] args) {
		Car car = new Car();
		CarLogProxy clp = new CarLogProxy(car);
        // 因为实现共同的接口,这里传入的是日志代理对象,实现代理叠加
		CarTimeProxy ctp = new CarTimeProxy(clp);  
		ctp.move();
	}
}

-- 运行结果

动态代理

动态产生代理,实现对不同类,不同方法的代理。

-- 例子

  • 接口 Moveable
public interface Moveable {
	void move();
}
  • 被代理类 Car
public class Car implements Moveable {

	@Override
	public void move() {
		//实现开车
		try {
			Thread.sleep(new Random().nextInt(1000));
			System.out.println("汽车行驶中....");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
  • 动态代理类 TimeHandler
public class TimeHandler implements InvocationHandler {

	private Object target;
	public TimeHandler(Object target) {
		super();
		this.target = target;
	}

	/*
	 * 参数:
	 * proxy  被代理对象
	 * method  被代理对象的方法
	 * args 方法的参数
	 * 
	 * 返回值:
	 * Object  方法的返回值
	 * */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		method.invoke(target);
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
		return null;
	}
}
  • 测试类
public class Test {
	public static void main(String[] args) {
		Car car = new Car();
		InvocationHandler h = new TimeHandler(car);
		Class<?> cls = car.getClass();
		/**
		 * loader  被代理类的类加载器
		 * interfaces  实现接口
		 * h InvocationHandler
		 */
		Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
												cls.getInterfaces(), h);
		m.move();  // 由于传入的InvocationHandler,代理对象调用被代理对象方法时,会自动触发代理类的`invoke`方法
	}
}

-- 运行结果

动态代理类总结

动态代理实现步骤

cglib vs jdk

  • jdk动态代理机制:只能代理实现某些接口的类,如果没有实现接口的类则不能使用JDK动态代理。
  • cglib动态代理机制:针对类产生代理,原理就是为指定的目标类产生一个子类,子类通过方法拦截技术(覆盖父类的方法来实现功能的增强)拦截所有父类方法的调用(因为该种方式是使用继承方式,所以不能对final修饰的类进行代理)。

动态代理实现思路

实现功能:通过Proxy的newProxyInstance返回代理对象

  1. 声明一段源码(动态产生代理):利用InvocationHandler动态地实现指定的业务逻辑。 通过interface.getName().getClass().getMethod()获取Method,再传入Method对象至invoke(Object obj, Method method),可实现对不同方法的代理。
  2. 编译源码(JDK Compiler API),产生新的类(代理类)
  3. 将这个类load到内存当中,产生一个新的对象(代理对象)
  4. return 代理对象

具体流程见 大鹏111 的课程笔记

参考:

模式的秘密---代理模式